首页IT科技用了react怎么和后端对接(react 高效高质量搭建后台系统 系列 —— 结尾)

用了react怎么和后端对接(react 高效高质量搭建后台系统 系列 —— 结尾)

时间2025-05-03 17:38:35分类IT科技浏览3697
导读:其他章节请看:...

其他章节请看:

react 高效高质量搭建后台系统 系列

尾篇

本篇主要介绍表单查询            、表单验证                 、通知(WebSocket)      、自动构建            。最后附上 myspug 项目源码                  。

项目最终效果:

表单查询

需求:给角色管理页面增加表格查询功能            ,通过输入角色名称                  ,点击查询     ,从后端检索出相应的数据     。

效果如下:

spug 中的实现

spug 中的这类查询都是在前端过滤出相应的数据(没有查询按钮)         ,因为 spug 中大多数的 table 都是一次性将数据从后端拿回来         。

spug 中角色管理搜索相关代码如下:

随着 input 中输入要搜索的角色名称更改 store 中的 f_name 字段: <SearchForm> <SearchForm.Item span={8} title="角色名称"> <Input allowClear value={store.f_name} onChange={e => store.f_name = e.target.value} placeholder="请输入"/> </SearchForm.Item> </SearchForm>

注:select 中的值不同于 input(e.target.value)                  ,直接就是第一个参数        ,所以得这么写:onChange={v => store.f_xx = v}

表格的数据源会动态过滤: @computed get dataSource() { // 从 this.records 中过滤出数据 let records = this.records; if (this.f_name) records = records.filter(x => x.name.toLowerCase().includes(this.f_name.toLowerCase())); return records } 实现

相对 spug 的查询      ,现在思路得变一下:通过点击搜索按钮                  ,重新请求数据           ,附带查询关键字给后端                  。

核心逻辑如下:

// myspug\src\pages\system\role\index.js import ComTable from ./Table; import { AuthDiv, SearchForm, } from @/components; import store from ./store; export default function () { return ( <AuthDiv auth="system.role.view"> <SearchForm> <SearchForm.Item span={6} title="角色名称"> <Input allowClear value={store.f_name} onChange={e => store.f_name = e.target.value} placeholder="请输入" /> </SearchForm.Item> <SearchForm.Item span={6}> <Button type="primary" onClick={() => { // 重置为第一页 store.setCurrent(1) store.fetchRecords(); }}>查询</Button> </SearchForm.Item> </SearchForm> <ComTable /> </AuthDiv> ) }

Store 中就是在请求表格时将过滤参数带上:

class Store { + @observable f_name; @observable records = []; _getTableParams = () => ({current: this.current, ...this.tableOptions}) + @action setCurrent(val){ + this.current = val + } fetchRecords = () => { const realParams = this._getTableParams() + // 过滤参数 + if(this.f_name){ + realParams.role_name = this.f_name + } + console.log(realParams, realParams) this.isFetching = true; http.get(/api/account/role/, {params: realParams}) .then(res => {

Tip:剩余部分就没什么了   ,比如样式直接复制 spug 中(笔者直接拷过来页面有点问题                  ,稍微注释了一段 css 即可);SearchForm 就是对表单简单封装              ,统一 spug 中表单的写法:

// myspug\src\components\SearchForm.js import React from react; import { Row, Col, Form } from antd; import styles from ./index.module.less; export default class extends React.Component { static Item(props) { return ( <Col span={props.span} offset={props.offset} style={props.style}> <Form.Item label={props.title}> {props.children} </Form.Item> </Col> ) } render() { return ( <div className={styles.searchForm} style={this.props.style}> <Form style={this.props.style}> <Row gutter={{md: 8, lg: 24, xl: 48}}> {this.props.children} </Row> </Form> </div> ) } } 效果

实现效果如下:

输入关键字name,点击查询按钮               ,重新请求表格数据(从第一页开始)

表单验证

spug 中的表单验证

关于表单验证                 ,spug 中前端写的很少        。请看以下一个典型示例:

新建角色时  ,为空等校验都是后端做的      。

虽然后端一定要做校验            ,但前端最好也做一套                  。

实现

笔者表单的验证思路是:

必填项都有值(还可以包括其他逻辑)                  ,提交按钮才可点     ,否则置灰 点击提交后         ,前端根据需求做进一步验证                  ,例如名字不能有空格

以下是新增和编辑时的效果(重点关注确定按钮):

当必填项都有值时确定按钮可点        ,否则置灰 必填项都有值时      ,点击确定按钮做进一步校验(例如名字不能有空格) 编辑时如果都有值                  ,则确定按钮可点击 表单

先实现表单           ,效果如下:

核心代码如下:

首先定义表单模块: // myspug\src\pages\system\role\Form.js import http from @/libs/http; import store from ./store; export default observer(function () { // 文档中未找到这种解构使用方法 const [form] = Form.useForm(); // useState 函数组件中使用 state // loading 默认是 flase const [loading, setLoading] = useState(false); function handleSubmit() { setLoading(true); // 取得表单字段的值 const formData = form.getFieldsValue(); // 新建时 id 为 undefined formData[id] = store.record.id; http.post(/api/account/role/, formData) .then(res => { message.success(操作成功); store.formVisible = false; store.fetchRecords() }, () => setLoading(false)) } return ( // Modal 对话框 <Modal visible maskClosable={false} title={store.record.id ? 编辑角色 : 新建角色} onCancel={() => store.formVisible = false} confirmLoading={loading} onOk={handleSubmit}> <Form form={form} initialValues={store.record} labelCol={{ span: 6 }} wrapperCol={{ span: 14 }}> <Form.Item required name="name" label="角色名称"> <Input placeholder="请输入角色名称" /> </Form.Item> <Form.Item name="desc" label="备注信息"> <Input.TextArea placeholder="请输入角色备注信息" /> </Form.Item> </Form> </Modal> ) }) 然后在入口页中根据 store 中的 formVisible 控制显隐藏表单组件 // myspug\src\pages\system\role\index.js export default observer(function () { return ( <AuthDiv auth="system.role.view"> <SearchForm> </SearchForm.Item> </SearchForm> <ComTable /> + {/* formVisible 控制表单显示 */} + {store.formVisible && <ComForm />} </AuthDiv> ) }) 点击新建是调用 store.showForm() 让表单显示出来 // myspug\src\pages\system\role\store.js class Store { + @observable formVisible = false; + @observable record = {}; + // 显示新增弹框 + // info 或许是为了编辑 + showForm = (info = {}) => { + this.formVisible = true; + this.record = info + }; 表单校验

在表单基础上实现校验           。

主要在 Form.js 中修改   ,思路如下:

首先利用 okButtonProps 控制确定按钮是否可点 然后通过 shouldUpdate={emptyValid} 自定义字段更新逻辑 可提交后                  ,在做进一步判断              ,例如名字不能为空 // myspug\src\pages\system\role\Form.js -import React, { useState } from react; +import React, { useEffect, useState } from react; import { observer } from mobx-react; import { Modal, Form, Input, message } from antd; import http from @/libs/http; // useState 函数组件中使用 state // loading 默认是 flase const [loading, setLoading] = useState(false); + const [canSubmit, setCanSubmit] = useState(false); function handleSubmit() { // 取得表单字段的值 const formData = form.getFieldsValue(); + + if(formData.name && (/\s+/g).test(formData.name)){ + message.error(名字不允许有空格) + return + } + if(formData.tel && (/\s+/g).test(formData.tel)){ + message.error(电话不允许有空格) + return + } // 新建时 id 为 undefined formData[id] = store.record.id; http.post(/api/account/role/, formData).then(...) } + function emptyValid() { + const formData = form.getFieldsValue(); + const { name, tel } = formData; + const isNotEmpty = !!(name && tel); + setCanSubmit(isNotEmpty) + } + useEffect(() => { + // 主动触发,否则编辑时即使都有数据               ,`确定`按钮扔不可点 + emptyValid() + }, []) + return ( // Modal 对话框 <Modal title={store.record.id ? 编辑角色 : 新建角色} onCancel={() => store.formVisible = false} confirmLoading={loading} + // ok 按钮 props + okButtonProps={{disabled: !canSubmit}} onOk={handleSubmit}> <Form form={form} initialValues={store.record} labelCol={{ span: 6 }} wrapperCol={{ span: 14 }}> - <Form.Item required name="name" label="角色名称"> + <Form.Item required shouldUpdate={emptyValid} name="name" label="角色名称"> <Input placeholder="请输入角色名称" /> </Form.Item> + {/* shouldUpdate - 自定义字段更新逻辑 */} + {/* 注:需要两个字段都增加 shouldUpdate   。如果只有一个                 ,修改该项则不会触发 emptyValid  ,你可以将 `shouldUpdate={emptyValid}` 放在非必填项中                  。*/} + <Form.Item required shouldUpdate={emptyValid} name="tel" label="手机号"> + <Input placeholder="请输入手机号" /> + </Form.Item> <Form.Item name="desc" label="备注信息"> <Input.TextArea placeholder="请输入角色备注信息" /> </Form.Item>

注:有两点需要注意

需要两个字段都增加 shouldUpdate              。如果只有一个            ,修改该项则不会触发 emptyValid() 组件加载后主动触发 emptyValid()                  ,否则编辑时即使都有数据     ,确定按钮扔不可点 效果

以下演示了新建和编辑时的效果:

当必填项都有值时确定按钮可点         ,否则置灰 必填项都有值时                  ,点击确定按钮做进一步校验(例如名字不能有空格) 编辑时如果都有值        ,则确定按钮可点击

WebSocket

通知

后端系统通常会有通知功能      ,用轮询的方式去和后端要数据不是很好                  ,通常是后端有数据后再告诉前端。

spug 中的通知使用的是 webSocket               。

Tip:WebSockets 是一种先进的技术                 。它可以在用户的浏览器和服务器之间打开交互式通信会话  。使用此 API           ,您可以向服务器发送消息并接收事件驱动的响应   ,而无需通过轮询服务器的方式以获得响应            。

以下是 spug 中通知模块的代码片段:

// spug\src\layout\Notification.js function fetch() { setLoading(true); http.get(/api/notify/) .then(res => { setReads(res.filter(x => !x.unread).map(x => x.id)) setNotifies(res); }) .finally(() => setLoading(false)) } function listen() { if (!X_TOKEN) return; const protocol = window.location.protocol === https: ? wss: : ws:; // Create WebSocket connection. ws = new WebSocket(`${protocol}//${window.location.host}/api/ws/notify/?x-token=${X_TOKEN}`); // onopen - 用于指定连接成功后的回调函数                  。 // Connection opened ws.onopen = () => ws.send(ok); // onmessage - 用于指定当从服务器接受到信息时的回调函数     。 // Listen for messages ws.onmessage = e => { if (e.data !== pong) { fetch(); const {title, content} = JSON.parse(e.data); const key = `open${Date.now()}`; const description = <div style={{whiteSpace: pre-wrap}}>{content}</div>; const btn = <Button type="primary" size="small" onClick={() => notification.close(key)}>知道了</Button>; notification.warning({message: title, description, btn, key, top: 64, duration: null}) } } }

通过 WebSocket 创建 webSocket 连接                  ,然后通过 onmessage 监听服务端的消息         。这里好像是后端告诉前端有新消息              ,前端在通过另一个接口发起 http 请求                  。

服务端

笔者接下来用 node + ws 实现 WebSocket 服务端        。

效果如下(每3秒客户端和服务器都会向对方发送一个消息):

对应的请求字段:

实现如下:

新建项目,安装依赖 $ mkdir websocket-test $ cd websocket-test // 初始化项目               ,生产 package.json $ npm init -y // 安装依赖 $ npm i ws express 新建服务器 server.js const express = require(express) const app = express() app.get(/, function (req, res) { res.sendfile(__dirname + /index.html); }); app.listen(3020); const WebSocketServer = require(ws); const wss = new WebSocketServer.Server({ port: 8080 }); wss.on(connection, function connection(ws) { // 监听来自客户端的消息 ws.on(message, function incoming(message) { console.log( + message); }); setInterval(() => { ws.send(客户端你好); }, 3000) }); 客户端代码 index.html: <body> <script> var ws = new WebSocket(ws://localhost:8080); ws.onopen = function () { ws.send(ok); }; ws.onmessage = function (e) { console.log(e.data) }; setInterval(() => { ws.send(服务器你好); }, 3000) </script> </body> 最后启动服务 node server.js                 ,浏览器访问 http://localhost:3020/

扩展

面包屑

spug 中的面包屑(导航)仅对 antd 面包屑稍作封装  ,不支持点击      。

要实现点击跳转的难点是要有对应的路由            ,而 spug 这里对应的是 404                  ,所以它干脆就不支持跳转

自动构建

笔者代码提交到 gitlab     ,使用其中的 CICD 模块可用于构建流水线                  。以下是 wayland(导入 wayland 官网到内网时发现的         ,开源精神极高                  ,考虑到网友有这个需求           。) 的一个构建截图:

这里不过多展开介绍 gitlab cicd 流水线   。总之通过触发流水线        ,gitlab 就会执行项目下的一个 .yml 脚本      ,我们则可以通过脚本实现编译         、部署                  。

需求:通过流水线实现 myspug 的部署              。

新建入口文件:.gitlab-ci.yml // .gitlab-ci.yml stages: - deploy # 部署到测试环境 deplay_to_test: state: deply tags: # 运行流水线的机器 - ubuntu2004_27.141-myspug rules: # 触发流水线时的变量                  ,EFPLOY_TO_TEST 不为空则运行 deploy-to-test.sh 这个脚本 - if: EFPLOY_TO_TEST != null && $DEPLOY_TO_TEST != "" script: - chmod + x deploy-to-test.sh && ./deploy-to-test.sh # 部署到生产环境 deplay_to_product: state: deply tags: - ubuntu2004_27.141-myspug rules: - if: EFPLOY_TO_product != null && $DEPLOY_TO_product != "" script: - chmod + x deploy-to-product.sh && ./deploy-to-product.sh 部署到生产环境的脚本:deploy-to-product.sh // deploy-to-product.sh #!/bin/bash # 部署到生产环境 # 开启:如果命令以非零状态退出           ,则立即退出 set -e DATETIME=$(date +%Y-%m-%d_%H%M%S) echo DATETIME=$DATETIME SERVERIP=192.168.27.135 SERVERDIR=/data/docker_data/myspug_web BACKDIR=/data/backup/myspug # 将构建的文件传给服务器 zip -r build.zip build scp ./build.zip root@${SERVERIP}:${BACKDIR}/ rm -rf build.zip # 登录生产环境服务器 ssh root${SERVERIP}<< reallssh echo login:${SERVERIP} # 备份目录 [ ! -d "${BACKDIR}/${DATETIME}" ] && mkdir -p "${BACKDIR}/${DATETIME}" echo 备份目录已创建或已存在 # 删除30天以前的包 find ${BACKDIR}/ -mtime +30 -exec rm -rf {} \; # 将包备份一份 cp ${BACKDIR}/build.zip ${BACKDIR}/${DATETIME} mv ${BACKDIR}/build.zip ${SERVERDIR}/ cd ${SERVERDIR}/ rm -rf ./build unzip build.zip rm -rf build.zip echo 部署完成 exit reallssh 完整项目

项目已上传至 github(myspug)。

克隆后执行以下两条命令即可在本地启动服务:

$ npm i $ npm run start

浏览器访问效果如下:

后续

后续有时间还想再写这3部分:

项目文档               。一个系统通常得有对应的文档                 。就像这样:

系统概要设计  。用于其他人快速接手这个项目

交互设计            。spug 中有不少的交互点可以提高相关系统的见识                  。例如这个抽屉交互

其他章节请看:

react 高效高质量搭建后台系统 系列

创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!

展开全文READ MORE
火绒安全软件官方下载电脑版(盘点电脑版火绒安全软件当前尚有的4个版本) 香港服务器免费租用(香港服务器适合什么网站租用)