diff --git a/web-react/package.json b/web-react/package.json index de8fd9b..e42c0a7 100644 --- a/web-react/package.json +++ b/web-react/package.json @@ -37,7 +37,10 @@ "extends": [ "react-app", "react-app/jest" - ] + ], + "rules": { + "eqeqeq": "off" + } }, "browserslist": { "production": [ @@ -51,4 +54,4 @@ "last 1 safari version" ] } -} +} \ No newline at end of file diff --git a/web-react/src/components/query-list/index.jsx b/web-react/src/components/query-list/index.jsx index 25e08e1..637480b 100644 --- a/web-react/src/components/query-list/index.jsx +++ b/web-react/src/components/query-list/index.jsx @@ -1,11 +1,204 @@ import React, { Component } from 'react' +import { Button, Form, List, Pagination, Spin } from 'antd' +import { AntIcon } from 'components' export default class QueryList extends Component { - render() { - return ( -
+ state = { + loading: false, + dataSource: [] + } + + // 查询表单实例 + form = React.createRef() + + // 查询值 + query = {} + + // 分页器配置 + pagination = { + current: 1, + pageSize: 10, + total: 0, + size: 'small', + showSizeChanger: true, + showQuickJumper: true, + showTotal: (total) => `总共${total}条数据` + } + + // 默认选中页码 + pageIndex = 1 + // 默认页面尺寸 + pageSize = 10 + + constructor(props) { + super(props) + + this.autoLoad = typeof this.props.autoLoad === 'boolean' ? this.props.autoLoad : true + this.loadData = typeof this.props.loadData === 'function' ? this.props.loadData : async () => { } + + if (this.props.pageIndex) { + this.pageIndex = this.props.pageIndex + this.pagination.current = this.pageIndex + } + if (this.props.pageSize) { + this.pageSize = this.props.pageSize + this.pagination.pageSize = this.pageSize + } + } + + /** + * 自动加载数据 + */ + componentDidMount() { + if (this.autoLoad) { + this.onLoadData() + } + } + + /** + * 加载数据 + * 调用外部传入的loadData函数,可在loadData中自行改变参数 + */ + async onLoadData() { + this.onLoading() + + const res = await this.props.loadData({ + pageIndex: this.pagination.current, + pageSize: this.pagination.pageSize + }, this.query) + + this.setState({ + dataSource: res.rows || res.data || res.items + }) + + this.pagination.total = res.totalCount + + this.onLoaded() + } + + /** + * 数据开始加载 + */ + onLoading() { + this.setState({ loading: true }) + } + + /** + * 数据加载完成 + */ + onLoaded() { + this.setState({ loading: false }) + } + + /** + * 进行查询 + * 返回表单字段值,加载数据,并且返回到第一页 + * @param {*} values + */ + onQuery(values) { + this.query = values + this.onReloadData(true) + } + + /** + * 重置查询 + * 初始化表单字段值,加载数据,并返回到第一页 + */ + onResetQuery() { + this.form.current.resetFields() + this.query = {} + this.onReloadData(true) + } + + /** + * 重新加载表格数据 + * @param {Boolean} resetPage 是否重置页码 + */ + onReloadData(resetPage = false) { + if (resetPage) { + this.pagination = { + ...this.pagination, + current: this.pageIndex + } + } + this.onLoadData() + } + + onListChange(current, pageSize) { + this.pagination = { + ...this.pagination, + current, + pageSize + } + this.onLoadData() + } + + /** + * 渲染查询栏 + * @returns + */ + renderQueryBar() { + + const { query, moreQuery } = this.props + + return ( +
+
this.onQuery(value)} + > + {query} + + + + + + { + moreQuery && + } + +
) } + + render() { + + const props = { + dataSource: this.state.dataSource, + rowKey: record => record.id || Math.random().toString(16).slice(2), + ...this.props + } + + return ( +
+ {this.props.query && this.renderQueryBar()} +
+
+ {this.props.operator} +
+
+ + + +
+
+
+ }> + + { + !!this.state.dataSource && !!this.state.dataSource.length && + this.onListChange(current, pageSize)} + /> + } + +
+
+ ) + } } diff --git a/web-react/src/components/query-tree-layout/index.jsx b/web-react/src/components/query-tree-layout/index.jsx index f7d4bf5..ef12820 100644 --- a/web-react/src/components/query-tree-layout/index.jsx +++ b/web-react/src/components/query-tree-layout/index.jsx @@ -214,10 +214,12 @@ export default class QueryTreeLayout extends Component { render() { + const { dataSource, expandedKeys, autoExpandParent } = this.state + const props = { - treeData: this.state.dataSource, - expandedKeys: this.state.expandedKeys, - autoExpandParent: this.state.autoExpandParent + treeData: dataSource, + expandedKeys, + autoExpandParent } const on = { diff --git a/web-react/src/pages/system/app/index.jsx b/web-react/src/pages/system/app/index.jsx index 515fece..fc126e0 100644 --- a/web-react/src/pages/system/app/index.jsx +++ b/web-react/src/pages/system/app/index.jsx @@ -23,6 +23,12 @@ const name = '应用' export default class index extends Component { + state = { + codes: { + commonStatus: [] + } + } + // 表格实例 table = React.createRef() @@ -162,7 +168,7 @@ export default class index extends Component { name = toCamelCase(name) const codes = this.state.codes[name] if (codes) { - const c = codes.find((p) => p.code == code) + const c = codes.find((p) => p.code === code) if (c) { return c.value } diff --git a/web-react/src/pages/system/org/form.jsx b/web-react/src/pages/system/org/form.jsx index d3fe0ef..63b94d3 100644 --- a/web-react/src/pages/system/org/form.jsx +++ b/web-react/src/pages/system/org/form.jsx @@ -165,6 +165,7 @@ export default class form extends Component {
}>
diff --git a/web-react/src/pages/system/org/index.jsx b/web-react/src/pages/system/org/index.jsx index 34c8a4d..de26cad 100644 --- a/web-react/src/pages/system/org/index.jsx +++ b/web-react/src/pages/system/org/index.jsx @@ -20,6 +20,12 @@ const name = '机构' export default class index extends Component { + state = { + codes: { + orgType: [] + } + } + // 表格实例 table = React.createRef() @@ -174,7 +180,7 @@ export default class index extends Component { name = toCamelCase(name) const codes = this.state.codes[name] if (codes) { - const c = codes.find((p) => p.code == code) + const c = codes.find((p) => p.code === code) if (c) { return c.value } diff --git a/web-react/src/pages/system/user/form.jsx b/web-react/src/pages/system/user/form.jsx new file mode 100644 index 0000000..63b94d3 --- /dev/null +++ b/web-react/src/pages/system/user/form.jsx @@ -0,0 +1,227 @@ +import React, { Component } from 'react' +import { Cascader, Form, Input, InputNumber, Select, Spin, TreeSelect } from 'antd' +import { AntIcon } from 'components' +import { cloneDeep } from 'lodash' +import getDicData from 'util/dic' +import { EMPTY_ID } from 'util/global' +import { api } from 'common/api' + +const initialValues = { + sort: 100 +} + +export default class form extends Component { + + state = { + // 加载状态 + loading: true, + + codes: { + orgType: [] + }, + + options: { + orgData: [], + areaData: [] + } + } + + // 表单实例 + form = React.createRef() + + // 初始化数据 + record = {} + + /** + * mount后回调 + */ + componentDidMount() { + this.props.created && this.props.created(this) + } + + /** + * 填充数据 + * 可以在设置this.record之后对其作出数据结构调整 + * [异步,必要] + * @param {*} params + */ + async fillData(params) { + + this.record = cloneDeep(params.record) + //#region 从后端转换成前段所需格式 + const orgData = await this.loadOrgData() + const areaData = await this.loadAreaData() + + const codes = await getDicData('org_type') + this.setState({ + codes, + options: { + orgData, + areaData + } + }) + + const areaCode = []; + const findCode = (data, level) => { + level = level || 0; + for (let i = 0; i < data.length; i++) { + const item = data[i]; + areaCode[level] = item.code; + + if (item.code === params.record.areaCode) { + areaCode.length = level + 1; + return true; + } + + if (item.children && item.children.length) { + const found = findCode(item.children, level + 1); + if (found) { + return true; + } + } + } + }; + + if (params.record && params.record.areaCode) { + findCode(areaData); + } + + this.record = { + pid: params.orgId, + ...this.record, + areaCode + } + this.record.areaCode = areaCode + //#endregion + + this.form.current.setFieldsValue(this.record) + + this.setState({ + loading: false + }) + } + + /** + * 获取数据 + * 可以对postData进行数据结构调整 + * [异步,必要] + * @returns + */ + async getData() { + const form = this.form.current + + const valid = await form.validateFields() + if (valid) { + const postData = form.getFieldsValue() + if (this.record) { + postData.id = this.record.id + } + //#region 从前段转换后端所需格式 + postData.areaCode = postData.areaCode[postData.areaCode.length - 1] + //#endregion + return postData + } + } + + //#region 自定义方法 + async loadOrgData() { + const { data } = await api.getOrgTree() + return [{ + id: EMPTY_ID, + parentId: undefined, + title: '顶级', + value: EMPTY_ID, + pid: undefined, + children: data, + }] + } + + async loadAreaData() { + const { data } = await api.getAreaTree() + const clearChiildren = (data) => { + data.forEach((item) => { + if (item.children && item.children.length) { + clearChiildren(item.children); + } else { + delete item.children; + } + }); + }; + clearChiildren(data); + return data + } + + onAreaCodeChange(selectedOptions) { + const data = selectedOptions[selectedOptions.length - 1] + this.form.current.setFieldsValue({ + name: data.name, + code: data.code + }) + } + //#endregion + + render() { + return ( + + }> +
+ + this.onAreaCodeChange(selectedOptions)} + /> + + + + + + + + + + + + + + + + + + + +
+
+ + ) + } +} diff --git a/web-react/src/pages/system/user/index.jsx b/web-react/src/pages/system/user/index.jsx new file mode 100644 index 0000000..e3e284e --- /dev/null +++ b/web-react/src/pages/system/user/index.jsx @@ -0,0 +1,319 @@ +import React, { Component } from 'react' +import { Button, Card, Descriptions, Form, Input, List, message as Message, Popconfirm, Select, Switch } from 'antd' +import { AntIcon, Auth, Container, Image, ModalForm, QueryList, QueryTreeLayout } from 'components' +import { api } from 'common/api' +import { toCamelCase } from 'util/format' +import { isEqual } from 'lodash' +import getDicData from 'util/dic' +import FormBody from './form' + +// 配置页面所需接口函数 +const apiAction = { + tree: api.getOrgTree, + page: api.getUserPage, + add: api.sysUserAdd, + edit: api.sysUserEdit, + delete: api.sysUserDelete, + + changeStatus: api.sysUserChangeStatus, + resetPwd: api.sysUserResetPwd +} + +// 用于弹窗标题 +const name = '用户' + +export default class index extends Component { + + state = { + codes: { + sex: [], + commonStatus: [] + } + } + + // 表格实例 + list = React.createRef() + + // 新增窗口实例 + addForm = React.createRef() + // 编辑窗口实例 + editForm = React.createRef() + + // 树选中节点 + selectId = undefined + + /** + * 阻止外部组件引发的渲染,提升性能 + * 可自行添加渲染条件 + * [必要] + * @param {*} props + * @param {*} state + * @returns + */ + shouldComponentUpdate(props, state) { + return !isEqual(this.state, state) + } + + /** + * 加载字典数据,之后开始加载表格数据 + * 如果必须要加载字典数据,可直接对表格设置autoLoad=true + */ + componentDidMount() { + this.list.current.onLoading() + getDicData('sex', 'common_status').then(res => { + this.setState({ + codes: res + }, () => { + this.list.current.onLoadData() + }) + }) + } + + /** + * 调用加载数据接口,可在调用前对query进行处理 + * [异步,必要] + * @param {*} params + * @param {*} query + * @returns + */ + loadData = async (params, query) => { + + query = { + ...query, + sysEmpParam: { + orgId: this.selectId + } + } + + const { data } = await apiAction.page({ + ...params, + ...query, + }) + return data + } + + /** + * 调用树结构数据接口 + * [异步,必要] + * @returns + */ + loadTreeData = async () => { + const { data } = await apiAction.tree() + return data + } + + /** + * 树节点选中事件 + * [必要] + * @param {*} id + */ + onSelectTree(id) { + this.selectId = id + this.list.current.onReloadData() + } + + /** + * 绑定字典数据 + * @param {*} code + * @param {*} name + * @returns + */ + bindCodeValue(code, name) { + name = toCamelCase(name) + const codes = this.state.codes[name] + if (codes) { + const c = codes.find((p) => p.code == code) + if (c) { + return c.value + } + } + return null + } + + /** + * 打开新增/编辑弹窗 + * @param {*} modal + * @param {*} record + */ + onOpen(modal, record) { + modal.current.open({ + orgId: this.selectId, + record + }) + } + + /** + * 对表格上的操作进行统一处理 + * [异步] + * @param {*} action + * @param {*} successMessage + */ + async onAction(action, successMessage) { + this.list.current.onLoading() + try { + await action + Message.success(successMessage) + this.list.current.onReloadData() + } catch { + this.list.current.onLoaded() + } + } + + /** + * 删除 + * @param {*} record + */ + onDelete(record) { + this.onAction( + apiAction.delete(record), + '删除成功' + ) + } + + //#region 自定义方法 + renderItem(record) { + return ( + + this.onOpen(this.editForm, record)}>编辑 + , + + this.onDelete(record)} + > + 删除 + + , + + this.onResetPassword(record)}>重置密码 + + ] + } + > + } + /> + } + title={record.nickName || record.name} + description={record.account} + /> + + {this.bindCodeValue(record.sex, 'sex')} + {record.phone || '未设置'} + {record.email || '未设置'} + +
+ +
+ this.onSetUserStatus(record, checked)} + /> +
+
+
+
+ ) + + } + + onSetUserStatus(record, checked) { + this.onAction( + apiAction.changeStatus({ id: record.id, status: +!checked }), + '设置成功' + ) + } + + onResetPassword(record) { + this.onAction( + apiAction.resetPwd(record), + '重置成功' + ) + } + //#endregion + + render() { + return ( + this.onSelectTree(key)} + > + + + + + + + + + + + } + operator={ + + } + renderItem={(record) => this.renderItem(record)} + /> + + + + this.list.current.onReloadData()} + > + + + + this.list.current.onReloadData()} + > + + + + ) + } +} diff --git a/web-react/src/store/index.js b/web-react/src/store/index.js index 101e479..0dae1fe 100644 --- a/web-react/src/store/index.js +++ b/web-react/src/store/index.js @@ -29,11 +29,11 @@ store.subscribe = (...args) => { if (path) { const resultState = cloneDeep(result(state, path)) if (!isEqual(snapshot, resultState)) { - listener.apply(this, [...args, resultState]) + listener.apply(this, [resultState, ...args]) } snapshot = resultState } else { - listener.apply(this, [...args, state]) + listener.apply(this, [state, ...args]) } }) } diff --git a/web-react/src/store/reducer.js b/web-react/src/store/reducer.js index 4bb1151..86c6d66 100644 --- a/web-react/src/store/reducer.js +++ b/web-react/src/store/reducer.js @@ -11,7 +11,9 @@ const user = (state = {}, action) => { } } -const layout = (state = {}, action) => { +const layout = (state = { + siderCollapsed: false +}, action) => { switch (action.type) { // 打开窗口 case 'OPEN_WINDOW': @@ -24,7 +26,8 @@ const layout = (state = {}, action) => { return state // 侧边收起状态 case 'TOGGLE_COLLAPSED': - return state + const _state = { ...state, siderCollapsed: action.siderCollapsed } + return _state default: return state }