diff --git a/web-react/src/components/query-table/index.jsx b/web-react/src/components/query-table/index.jsx index 1e51f2d..8b3ba36 100644 --- a/web-react/src/components/query-table/index.jsx +++ b/web-react/src/components/query-table/index.jsx @@ -223,7 +223,7 @@ export default class QueryTable extends Component { columns: (columns || []).filter(p => !p.hidden), bordered: true, size: 'middle', - rowKey: record => record.id || Math.random().toString(16).slice(2), + rowKey: record => record.id || record, ...this.props } diff --git a/web-react/src/pages/system/app/index.jsx b/web-react/src/pages/system/app/index.jsx index fc126e0..053c19f 100644 --- a/web-react/src/pages/system/app/index.jsx +++ b/web-react/src/pages/system/app/index.jsx @@ -250,6 +250,12 @@ export default class index extends Component { onClick={() => this.onOpen(this.addForm)} >新增{name} } + expandedRowRender={ + record => { + console.log(record) + return
123
+ } + } /> diff --git a/web-react/src/pages/system/area/form.jsx b/web-react/src/pages/system/area/form.jsx new file mode 100644 index 0000000..7d5d3ae --- /dev/null +++ b/web-react/src/pages/system/area/form.jsx @@ -0,0 +1,132 @@ +import React, { Component } from 'react' +import { Form, Input, InputNumber, Select, Spin } from 'antd' +import { AntIcon } from 'components' +import { cloneDeep } from 'lodash' +import getDicData from 'util/dic' + +const initialValues = { + sort: 100 +} +export default class form extends Component { + state = { + // 加载状态 + loading: true, + exist: false, + codes: { + dicAreacodeType: [] + } + } + // 表单实例 + 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 codes = await getDicData('dic_areacode_type') + const exist = !!params.record; + this.setState({ + codes, + exist + }) + + this.record = { + ...this.record + } + //#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 从前段转换后端所需格式 + //#endregion + return postData + } + } + + render() { + return ( +
+ }> +
+ {/* 表单控件 */} + + + + + + + + }> + + + + + + + + + + + + + {/* ... */} +
+
+
+ ) + } +} \ No newline at end of file diff --git a/web-react/src/pages/system/area/index.jsx b/web-react/src/pages/system/area/index.jsx new file mode 100644 index 0000000..263c341 --- /dev/null +++ b/web-react/src/pages/system/area/index.jsx @@ -0,0 +1,290 @@ +import React, { Component } from 'react' +import { Button, Card, Form, Input, message as Message, Popconfirm } from 'antd' +import { AntIcon, Auth, Container, ModalForm, QueryTable, QueryTableActions, QueryTreeLayout } from 'components' +import { api } from 'common/api' +import auth from 'components/authorized/handler' +import { toCamelCase } from 'util/format' +import { isEqual } from 'lodash' +import getDicData from 'util/dic' +import FormBody from './form' + +const apiAction = { + tree: api.getAreaTree, + page: api.sysAreaPage, + add: api.sysAreaAdd, + edit: api.sysAreaEdit, + delete: api.sysAreaDelete +} + +const name = '区域' + +export default class index extends Component { + + state = { + codes: { + dicAreacodeType: [] + } + } + + // 表格实例 + table = React.createRef() + + // 新增窗口实例 + addForm = React.createRef() + // 编辑窗口实例 + editForm = React.createRef() + + // 树选中节点 + selectCode = undefined + columns = [ + { + title: '区域类型', + dataIndex: 'levelType', + sorter: true, + render: text => (<>{this.bindCodeValue(text, 'dic_areacode_type')}) + }, + { + title: '区域名称', + dataIndex: 'name', + sorter: true, + }, + { + title: '区域编号', + dataIndex: 'code', + sorter: true, + }, + { + title: '行政编号', + dataIndex: 'adCode', + sorter: true, + }, + { + title: '描述', + dataIndex: 'note', + sorter: false, + }, + { + title: '排序', + dataIndex: 'sort', + sorter: true, + }, + ] + /** + * 构造函数,在渲染前动态添加操作字段等 + * @param {*} props + */ + constructor(props) { + super(props) + + const flag = auth({ sysArea: [['edit'], ['delete']] }) + + if (flag) { + this.columns.push({ + title: '操作', + width: 150, + dataIndex: 'actions', + render: (text, record) => ( + + this.onOpen(this.editForm, record)}>编辑 + + + this.onDelete(record)} + > + 删除 + + + ) + }) + } + } + /** + * 阻止外部组件引发的渲染,提升性能 + * 可自行添加渲染条件 + * [必要] + * @param {*} props + * @param {*} state + * @returns + */ + shouldComponentUpdate(props, state) { + return !isEqual(this.state, state) + } + + /** + * 加载字典数据,之后开始加载表格数据 + * 如果必须要加载字典数据,可直接对表格设置autoLoad=true + */ + componentDidMount() { + this.table.current.onLoading() + getDicData('dic_areacode_type').then(res => { + this.setState({ + codes: res + }, () => { + this.table.current.onLoadData() + }) + }) + } + /** + * 调用加载数据接口,可在调用前对query进行处理 + * [异步,必要] + * @param {*} params + * @param {*} query + * @returns + */ + loadData = async (params, query) => { + + query = { + ...query, + pcode: this.selectCode + } + //首次加载根据code列升序排序 + if (!params.sortField) { + params.sortField = 'code'; + params.sortOrder = 'ascend'; + } + const { data } = await apiAction.page({ + ...params, + ...query, + }) + return data + } + + /** + * 调用树结构数据接口 + * [异步,必要] + * @returns + */ + loadTreeData = async () => { + const { data } = await apiAction.tree() + return data + } + + /** + * 树节点选中事件 + * [必要] + * @param {*} id + */ + onSelectTree(code) { + this.selectCode = code + this.table.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({ + pcode: this.pcode, + record + }) + } + + /** + * 对表格上的操作进行统一处理 + * [异步] + * @param {*} action + * @param {*} successMessage + */ + async onAction(action, successMessage) { + this.table.current.onLoading() + try { + await action + Message.success(successMessage) + this.table.current.onReloadData() + } catch { + this.table.current.onLoaded() + } + } + + /** + * 删除 + * @param {*} record + */ + onDelete(record) { + this.onAction( + apiAction.delete(record), + '删除成功' + ) + } + + render() { + return ( + this.onSelectTree(key)} + replaceFields={{ value: 'code', title: 'name', children: 'children' }} + > + + + + + + + + + + + } + operator={ + + + + } + > + + + + + this.table.current.onReloadData()} + > + + + + this.table.current.onReloadData()} + > + + + + ) + } +} \ No newline at end of file diff --git a/web-react/src/pages/system/log/oplog/index.jsx b/web-react/src/pages/system/log/oplog/index.jsx new file mode 100644 index 0000000..1f2d47b --- /dev/null +++ b/web-react/src/pages/system/log/oplog/index.jsx @@ -0,0 +1,245 @@ +import React, { Component } from 'react' +import { Alert, Button, Card, Descriptions, Form, Popconfirm, Input, message as Message, Select } from 'antd' +import { Auth, Container, QueryTable } from 'components' +import { api } from 'common/api' +import { toCamelCase } from 'util/format' +import { isEqual } from 'lodash' +import getDicData from 'util/dic' +import moment from 'moment' + +const apiAction = { + page: api.sysOpLogPage, + delete: api.sysOpLogDelete +} +export default class index extends Component { + state = { + codes: { + opType: [] + } + } + // 表格实例 + table = React.createRef() + // 表格字段 + columns = [ + { + title: '日志名称', + dataIndex: 'name', + sorter: true, + }, + { + title: '操作类型', + dataIndex: 'opType', + render: text => (<>{this.bindCodeValue(text, 'op_type')}), + sorter: true, + }, + { + title: '是否成功', + dataIndex: 'success', + render: text => (<> {text ? '是' : '否'}), + sorter: true, + }, + { + title: 'ip', + dataIndex: 'ip', + sorter: true, + }, + { + title: '请求地址', + dataIndex: 'url', + sorter: true, + }, + { + title: '操作时间', + dataIndex: 'opTime', + sorter: true, + }, + { + title: '操作人', + dataIndex: 'account', + sorter: true, + }, + ] + + /** + * 阻止外部组件引发的渲染,提升性能 + * 可自行添加渲染条件 + * [必要] + * @param {*} props + * @param {*} state + * @returns + */ + shouldComponentUpdate(props, state) { + return !isEqual(this.state, state) + } + + /** + * 加载字典数据,之后开始加载表格数据 + * 如果必须要加载字典数据,可直接对表格设置autoLoad=true + */ + componentDidMount() { + this.table.current.onLoading() + getDicData('op_type').then(res => { + this.setState({ + codes: res + }, () => { + this.table.current.onLoadData() + }) + }) + } + + /** + * 调用加载数据接口,可在调用前对query进行处理 + * [异步,必要] + * @param {*} params + * @param {*} query + * @returns + */ + loadData = async (params, query) => { + // if (query.dates && query.dates.length) { + // query.searchBeginTime = moment(query.dates[0]).format('YYYY-MM-DD HH:mm:ss'); + // query.searchEndTime = moment(query.dates[1]).format('YYYY-MM-DD HH:mm:ss'); + // delete query.dates; + // } + const { data } = await apiAction.page({ + ...params, + ...query, + }) + return data + } + /** + * 绑定字典数据 + * @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 {*} action + * @param {*} successMessage + */ + async onAction(action, successMessage) { + this.table.current.onLoading() + try { + await action + Message.success(successMessage) + this.table.current.onReloadData() + } catch { + this.table.current.onLoaded() + } + } + + onOpLogClear() { + this.onAction( + apiAction.delete(), + '清空成功' + ) + } + + render() { + return ( + +
+ +
后端bug:任何操作的操作类型都是增加
+
没有记录请求参数.返回结果等信息
+ + } /> +
+ + + + + + + + + + + + + } + operator={ + + this.onOpLogClear()} + > + + + + } + expandable={{ + expandedRowRender: record => + + + {record.methodName} + + + {record.location} + + + {record.browser} + + + {record.os} + + + {record.className} + + + {record.result} + + + {record.param} + + + {record.message} + + + }} + + /> + +
+ ) + } +} \ No newline at end of file diff --git a/web-react/src/pages/system/pos/form.jsx b/web-react/src/pages/system/pos/form.jsx new file mode 100644 index 0000000..ac8ded9 --- /dev/null +++ b/web-react/src/pages/system/pos/form.jsx @@ -0,0 +1,101 @@ +import React, { Component } from 'react' +import { Form, Input, InputNumber, Spin } from 'antd' +import { AntIcon, IconSelector } from 'components' +import { cloneDeep } from 'lodash' + +const initialValues = { + sort: 100 +} + +export default class form extends Component { + state = { + // 加载状态 + loading: true, + } + + // 表单实例 + form = React.createRef() + + iconSelector = 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 从后端转换成前段所需格式 + //#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 从前段转换后端所需格式 + //#endregion + return postData + } + } + + render() { + return ( +
+ }> +
+ + + + + + + + + + + + + +
+
+
+ ) + } +} \ No newline at end of file diff --git a/web-react/src/pages/system/pos/index.jsx b/web-react/src/pages/system/pos/index.jsx new file mode 100644 index 0000000..c12f74c --- /dev/null +++ b/web-react/src/pages/system/pos/index.jsx @@ -0,0 +1,201 @@ +import React, { Component } from 'react' +import { Button, Card, Form, Input, Popconfirm, message as Message } from 'antd' +import { isEqual } from 'lodash' +import { AntIcon, Auth, Container, ModalForm, QueryTable, QueryTableActions } from 'components' +import { api } from "common/api" +import auth from 'components/authorized/handler' +import FormBody from './form' + + +// 配置页面所需接口函数 +const apiAction = { + page: api.sysPosPage, + add: api.sysPosAdd, + edit: api.sysPosEdit, + delete: api.sysPosDelete +} + +// 用于弹窗标题 +const name = '职位' + +export default class index extends Component { + + // 表格实例 + table = React.createRef() + + // 新增窗口实例 + addForm = React.createRef() + // 编辑窗口实例 + editForm = React.createRef() + + columns = [ + { + title: '职位名称', + dataIndex: 'name', + sorter: true, + }, + { + title: '唯一编码', + dataIndex: 'code', + sorter: true, + }, + { + title: '排序', + dataIndex: 'sort', + sorter: true, + }, + { + title: '备注', + dataIndex: 'remark', + sorter: true, + }, + ] + + /** + * 构造函数,在渲染前动态添加操作字段等 + * @param {*} props + */ + constructor(props) { + super(props) + + const flag = auth({ sysPos: [['edit'], ['delete']] }) + + if (flag) { + this.columns.push({ + title: '操作', + width: 150, + dataIndex: 'actions', + render: (text, record) => ( + + this.onOpen(this.editForm, record)}>编辑 + + + this.onDelete(record)} + > + 删除 + + + ) + }) + } + } + + /** + * 阻止外部组件引发的渲染,提升性能 + * 可自行添加渲染条件 + * [必要] + * @param {*} props + * @param {*} state + * @returns + */ + shouldComponentUpdate(props, state) { + return !isEqual(this.state, state) + } + + /** + * 调用加载数据接口,可在调用前对query进行处理 + * [异步,必要] + * @param {*} params + * @param {*} query + * @returns + */ + loadData = async (params, query) => { + const { data } = await apiAction.page({ + ...params, + ...query, + }) + return data + } + + /** + * 打开新增/编辑弹窗 + * @param {*} modal + * @param {*} record + */ + onOpen(modal, record) { + modal.current.open({ + record + }) + } + + /** + * 对表格上的操作进行统一处理 + * [异步] + * @param {*} action + * @param {*} successMessage + */ + async onAction(action, successMessage) { + this.table.current.onLoading() + try { + await action + Message.success(successMessage) + this.table.current.onReloadData() + } catch { + this.table.current.onLoaded() + } + } + + /** + * 删除 + * @param {*} record + */ + onDelete(record) { + this.onAction( + apiAction.delete(record), + '删除成功' + ) + } + + render() { + return ( + +
+ + + + + + + + + + } + operator={ + + } + > + + + + this.table.current.onReloadData()} + > + + + + this.table.current.onReloadData()} + > + + +
+ ) + } +} \ No newline at end of file