react 高效高质量搭建后台系统 系列 您所在的位置:网站首页 近期总是遗精 react 高效高质量搭建后台系统 系列

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

2023-02-23 20:44| 来源: 网络整理| 查看: 265

其他章节请看:

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

尾篇

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

项目最终效果:

表单查询

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

效果如下:

spug 中的实现

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

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

随着 input 中输入要搜索的角色名称更改 store 中的 f_name 字段: store.f_name = e.target.value} placeholder="请输入"/>

注: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 ( store.f_name = e.target.value} placeholder="请输入" /> { // 重置为第一页 store.setCurrent(1) store.fetchRecords(); }}>查询 ) }

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 ( {props.children} ) } render() { return ( {this.props.children} ) } } 效果

实现效果如下:

输入关键字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 对话框 store.formVisible = false} confirmLoading={loading} onOk={handleSubmit}> ) }) 然后在入口页中根据 store 中的 formVisible 控制显隐藏表单组件 // myspug\src\pages\system\role\index.js export default observer(function () { return ( + {/* formVisible 控制表单显示 */} + {store.formVisible && } ) }) 点击新建是调用 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 对话框 store.formVisible = false} confirmLoading={loading} + // ok 按钮 props + okButtonProps={{disabled: !canSubmit}} onOk={handleSubmit}> - + + {/* shouldUpdate - 自定义字段更新逻辑 */} + {/* 注:需要两个字段都增加 shouldUpdate。如果只有一个,修改该项则不会触发 emptyValid,你可以将 `shouldUpdate={emptyValid}` 放在非必填项中。*/} + + +

注:有两点需要注意

需要两个字段都增加 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 = {content}; const btn = notification.close(key)}>知道了; 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: 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) 最后启动服务 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}


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有