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={
+
+ }
+ onClick={() => this.onOpen(this.addForm)}
+ >新增{name}
+
+ }
+ >
+
+
+
+
+ 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={
+ }
+ onClick={() => this.onOpen(this.addForm)}
+ >新增{name}
+ }
+ >
+
+
+
+ this.table.current.onReloadData()}
+ >
+
+
+
+ this.table.current.onReloadData()}
+ >
+
+
+
+ )
+ }
+}
\ No newline at end of file