diff --git a/web-react/src/pages/system/menu/form.jsx b/web-react/src/pages/system/menu/form.jsx new file mode 100644 index 0000000..e870986 --- /dev/null +++ b/web-react/src/pages/system/menu/form.jsx @@ -0,0 +1,362 @@ +import React, { Component } from 'react' +import { Form, Input, InputNumber, Radio, Select, Spin, Switch, TreeSelect } from 'antd' +import { AntIcon, IconSelector } from 'components' +import { cloneDeep } from 'lodash' +import getDicData from 'util/dic' +import { api } from 'common/api' +import { EMPTY_ID } from 'util/global' + +const initialValues = { + type: '1', + openType: '1', + visible: true, + sort: 100 +} + +export default class form extends Component { + + state = { + // 加载状态 + loading: true, + codes: { + menuType: [], + openType: [] + }, + options: { + appList: [], + parentTreeData: [] + }, + + type: initialValues.type, + openType: initialValues.openType, + icon: '' + } + + // 表单实例 + 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 从后端转换成前段所需格式 + const { menuType, openType } = await getDicData('menu_type', 'open_type') + const appList = await this.onLoadSysApplist() + let parentTreeData = [] + if (params.isParent) { + parentTreeData = await this.onLoadMenuTree(params.parent.application) + } else if (params.record) { + parentTreeData = await this.onLoadMenuTree(params.record.application) + } + const icon = params.record && params.record.icon + this.setState({ + codes: { + menuType, + openType + }, + options: { + appList, + parentTreeData + }, + icon + }) + //#endregion + const form = this.form.current + if (params.isParent) { + form.setFieldsValue({ + pid: params.parent.id, + application: params.parent.application + }) + } else { + form.setFieldsValue(this.record) + } + + this.setState({ + loading: false + }) + + this.onTypeChange() + } + + /** + * 获取数据 + * 可以对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 + } + } + + //#region 自定义方法 + async onLoadSysApplist() { + const { data } = await api.getAppList() + return data + } + + async onLoadMenuTree(application) { + const { data } = await api.getMenuTree({ application }) + return [{ + id: EMPTY_ID, + parentId: undefined, + title: '顶级', + value: EMPTY_ID, + pid: undefined, + children: data, + }] + } + + + onTypeChange() { + this.onTypeChangeGroup() + const form = this.form.current + const { type } = form.getFieldsValue() + if (['0', '2'].includes(type)) { + form.setFieldsValue({ + openType: '0' + }) + } else { + form.setFieldsValue({ + openType: '1' + }) + } + } + + onOpenTypeChange() { + this.onTypeChangeGroup() + } + + onTypeChangeGroup() { + const form = this.form.current + const { type, openType } = form.getFieldsValue() + // if (type == 1 && openType == 2) { + // form.setFieldsValue({ + // component: 'iframe' + // }) + // } else { + // form.setFieldsValue({ + // component: '' + // }) + // } + + this.setState({ + type, + openType + }) + } + + async onApplicationChange(value) { + this.setState({ + loading: true + }) + const parentTreeData = await this.onLoadMenuTree(value) + this.setState({ + loading: false, + options: { + ...this.state.options, + parentTreeData + } + }) + this.form.current.setFieldsValue({ + pid: undefined + }) + } + + onSelectIcon(icon) { + this.form.current.setFieldsValue({ + icon + }) + this.setState({ icon }) + } + //#endregion + + render() { + return ( +
+ }> +
+

基本信息

+
+ + 目录:默认添加在顶级 +
菜单: +
按钮: + + } + rules={[{ required: true, message: '请选择菜单类型' }]} + > + this.onTypeChange(e)}> + { + this.state.codes.menuType.map(item => { + return ( + {item.value} + ) + }) + } + +
+ + + + + + + + + + { + this.state.type != 0 && + + + + } +
+

扩展信息

+
+ { + this.state.type == 1 && + + this.onOpenTypeChange(e)}> + { + this.state.codes.openType.map(item => { + return ( + {item.value} + ) + }) + } + + + } + { + this.state.type == 1 && this.state.openType == 1 && + + + + } + { + this.state.type == 1 && this.state.openType == 2 && + + + + } + { + this.state.type == 1 && this.state.openType == 3 && + + + + } + { + this.state.type == 2 && + + + + } + { + this.state.type == 2 && + + + + } + + + + { + this.state.type != 2 && + + + } + addonAfter={ + + this + .iconSelector + .current + .open(this.form.current.getFieldValue('icon')) + } + /> + } + /> + + } + + + + + + +
+
+
+ + this.onSelectIcon(icon)} /> + + ) + } +} diff --git a/web-react/src/pages/system/menu/index.jsx b/web-react/src/pages/system/menu/index.jsx new file mode 100644 index 0000000..ef3a4b2 --- /dev/null +++ b/web-react/src/pages/system/menu/index.jsx @@ -0,0 +1,274 @@ +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 getDicData from 'util/dic' +import auth from 'components/authorized/handler' +import { toCamelCase } from 'util/format' +import FormBody from './form' + +// 配置页面所需接口函数 +const apiAction = { + page: api.getMenuList, + add: api.sysMenuAdd, + edit: api.sysMenuEdit, + delete: api.sysMenuDelete +} + +// 用于弹窗标题 +const name = '菜单' + +export default class index extends Component { + + state = { + codes: { + menuType: [], + menuWeight: [] + } + } + + // 表格实例 + table = React.createRef() + + // 新增窗口实例 + addForm = React.createRef() + // 编辑窗口实例 + editForm = React.createRef() + + // 表格字段 + columns = [ + { + title: '菜单名称', + width: 220, + dataIndex: 'name', + }, + { + title: '菜单类型', + width: 100, + dataIndex: 'type', + render: text => this.bindCodeValue(text, 'menu_type'), + }, + { + title: '图标', + width: 100, + dataIndex: 'icon', + render: text => text && + }, + { + title: '前端组件', + width: 220, + dataIndex: 'component', + ellipsis: true, + }, + { + title: '权限标识', + width: 220, + dataIndex: 'permission', + ellipsis: true, + }, + { + title: '排序', + width: 100, + dataIndex: 'sort', + } + ] + + /** + * 构造函数,在渲染前动态添加操作字段等 + * @param {*} props + */ + constructor(props) { + super(props) + + const flag = auth({ sysApp: [['edit'], ['delete']] }) + + if (flag) { + this.columns.push({ + title: '操作', + width: 150, + dataIndex: 'actions', + render: (text, record) => ( + { + record.type < 2 && + + this.onOpen(this.addForm, record, true)}>新增子菜单 + + } + + 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('menu_type', 'menu_weight').then(res => { + this.setState({ + codes: res + }, () => { + this.table.current.onLoadData() + }) + }) + } + + /** + * 调用加载数据接口,可在调用前对query进行处理 + * [异步,必要] + * @param {*} params + * @param {*} query + * @returns + */ + loadData = async (params, query) => { + 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 {*} modal + * @param {*} record + */ + onOpen(modal, record, isParent = false) { + const params = isParent ? { + parent: record, + isParent + } : { + record, + isParent + } + + modal.current.open(params) + } + + /** + * 对表格上的操作进行统一处理 + * [异步] + * @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), + '删除成功' + ) + } + + //#region 自定义方法 + async onSetDefault(record) { + this.onAction( + apiAction.setDefault(record), + '设置成功' + ) + } + //#endregion + + render() { + return ( + +
+ + + + + } + /> + + + this.table.current.onReloadData()} + > + + + + this.table.current.onReloadData()} + > + + +
+ ) + } +}