From 27a4733c159d77159ec872d7039d812e9b5e9aed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=87=AA=E5=B8=A6=E5=A4=A7=E4=BD=AC=E6=B0=94=E5=9C=BA?= <188633308@qq.com> Date: Tue, 15 Jun 2021 15:15:05 +0800 Subject: [PATCH] =?UTF-8?q?update=20=E8=A7=92=E8=89=B2=E7=AE=A1=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/authority-view/index.jsx | 235 +++++++++++++++- web-react/src/pages/system/role/data.jsx | 169 ++++++++++++ web-react/src/pages/system/role/form.jsx | 104 ++++++++ web-react/src/pages/system/role/index.jsx | 250 ++++++++++++++++++ web-react/src/pages/system/role/menu.jsx | 89 +++++++ 5 files changed, 845 insertions(+), 2 deletions(-) create mode 100644 web-react/src/pages/system/role/data.jsx create mode 100644 web-react/src/pages/system/role/form.jsx create mode 100644 web-react/src/pages/system/role/index.jsx create mode 100644 web-react/src/pages/system/role/menu.jsx diff --git a/web-react/src/components/authority-view/index.jsx b/web-react/src/components/authority-view/index.jsx index e071a1b..2fcddd7 100644 --- a/web-react/src/components/authority-view/index.jsx +++ b/web-react/src/components/authority-view/index.jsx @@ -1,10 +1,241 @@ import React, { Component } from 'react' +import { Checkbox, Descriptions, Empty, Spin, Tooltip } from 'antd' +import { AntIcon } from 'components' +import { EMPTY_ID } from 'util/global' export default class AuthorityView extends Component { + + state = { + loading: false, + dataSource: [] + } + + list = [] + + 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 () => { } + } + + /** + * 自动加载数据 + */ + componentDidMount() { + if (this.autoLoad) { + this.onLoadData() + } + } + + async onLoadData() { + this.setState({ loading: true }) + + const res = await this.loadData() + + this.list = [] + this.generateList(res) + + if (this.props.defaultSelectedKeys) { + this.list.map(item => { + if (this.props.defaultSelectedKeys.includes(item.id) && (!item.children || !item.children.length)) { + this.onSelect(true, item) + } + }) + } + + this.setState({ + dataSource: res, + loading: false + }) + } + + onReloadData() { + this.onLoadData() + } + + onChange(e, item) { + this.onSelect(e.target.checked, item) + + const visible = this.getVisible() + + if (this.props.onSelect) { + this.props.onSelect( + // 返回所有选中 + this.list.filter(item => item.checked).map(item => item.id), + // 返回所有选中和半选 + this.list.filter(item => item.checked || item.indeterminate).map(item => item.id), + // 返回所有选中和半选,但是不返回没有子级选中visibleParent的半选 + visible + ) + } + } + + onSelect(check, item) { + item.checked = check + item.indeterminate = false + if (item.children && item.children.length) { + this.onChangeChildren(item.checked, item.children) + } + if (item.parentId) { + this.onChangeParent(item.checked, item.parentId) + } + + this.setState({ + dataSource: this.list.filter(item => item.parentId === EMPTY_ID) + }) + } + + onChangeParent(checked, parentId) { + const parent = this.list.find(p => p.id === parentId) + if (parent) { + const checkedCount = parent.children.filter(p => p.checked).length + const indeterminateCount = parent.children.filter(p => p.indeterminate).length + if (checkedCount === parent.children.length) { + // 全选 + parent.checked = true + parent.indeterminate = false + } else if (!checkedCount && !indeterminateCount) { + // 全不选 + parent.checked = false + parent.indeterminate = false + } else { + // 半选 + parent.checked = false + parent.indeterminate = true + } + this.onChangeParent(checked, parent.parentId) + } + } + + onChangeChildren(checked, children) { + children.forEach(p => { + p.checked = checked + p.indeterminate = false + if (p.children && p.children.length) { + this.onChangeChildren(checked, p.children) + } + }) + } + + generateList(data) { + data.forEach(item => { + if (item.children && item.children.length) { + this.generateList(item.children) + } + this.list.push(item) + }) + } + + getVisible() { + const checked = this.list.filter(item => item.checked) + const caseChildren = checked.filter(item => item.visibleParent || item.type != 2) + const visibleParents = [] + // 递归寻找父级 + const findVisibleParents = (children) => { + const parents = [] + children.forEach(item => { + if (item.parentId) { + const parent = this.list.find(item => item.id === item.parentId) + if (parent) { + parents.push(parent) + visibleParents.push(parent) + } + } + }) + if (parents.length) { + findVisibleParents(parents) + } + } + + findVisibleParents(caseChildren) + + const checkedIds = checked.map(item => item.id) + const visibleParentsIds = visibleParents.map(item => item.id) + + const result = checkedIds + visibleParentsIds.forEach(item => { + if (!result.includes(item)) { + result.push(item) + } + }) + + return result + } + + renderDescriptions(data) { + return data.map(item => { + return item.children && item.children.length ? this.renderItem(item) : this.renderCheckbox(item) + }) + } + + renderItem(data) { + return ( + + this.onChange(e, data)} + >{data.title} + } + > + {this.renderDescriptions(data.children)} + + + ) + } + + renderCheckbox(data) { + return ( +
+ { + data.visibleParent && data.type == 2 && + + + + } + this.onChange(e, data)} + >{data.title} +
+ ) + } + render() { return ( -
- +
+ }> + { + !this.state.loading ? + + { + this.state.dataSource.map(item => { + return ( + this.onChange(e, item)} + >{item.title} + } + > + {this.renderDescriptions(item.children)} + + ) + }) + } + + : + + } +
) } diff --git a/web-react/src/pages/system/role/data.jsx b/web-react/src/pages/system/role/data.jsx new file mode 100644 index 0000000..f38b8a4 --- /dev/null +++ b/web-react/src/pages/system/role/data.jsx @@ -0,0 +1,169 @@ +import React, { Component } from 'react' +import { Form, Select, Spin, TreeSelect } from 'antd' +import { AntIcon } from 'components' +import { cloneDeep } from 'lodash' +import { api } from 'common/api' +import getDicData from 'util/dic' + +const { SHOW_PARENT } = TreeSelect + +const initialValues = {} + +export default class data extends Component { + + state = { + // 加载状态 + loading: true, + dataScopeType: [], + orgTreeData: [], + arerTreeData: [], + orgCheckedKeys: [], + + isDefine: false + } + + // 表单实例 + 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 { dataScopeType } = await getDicData('data_scope_type') + const orgTreeData = await this.onLoadOrgTreeData() + const arerTreeData = await this.onLoadAreaTreeData() + const orgCheckedKeys = await this.onLoadRoleOwn(this.record.id) + this.setState({ + dataScopeType, + orgTreeData, + arerTreeData, + orgCheckedKeys + }) + //#endregion + this.form.current.setFieldsValue({ + dataScopeType: this.record.dataScopeType.toString() + }) + + this.onChange(this.record.dataScopeType) + + 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 + } + } + + //#region 自定义方法 + async onLoadOrgTreeData() { + const { data } = await api.getOrgTree() + return data + } + + async onLoadAreaTreeData() { + const { data } = await api.getAreaTree() + return data + } + + async onLoadRoleOwn(id) { + const { data } = await api.sysRoleOwnData({ id }) + return data + } + + onChange(value) { + if (value == 5) { + this.setState({ + isDefine: true + }) + } else { + this.setState({ + isDefine: false + }) + } + } + //#endregion + + render() { + return ( +
+ }> +
+ + + + { + this.state.isDefine && + <> + + + + + + + + } +
+
+
+ ) + } +} diff --git a/web-react/src/pages/system/role/form.jsx b/web-react/src/pages/system/role/form.jsx new file mode 100644 index 0000000..594deee --- /dev/null +++ b/web-react/src/pages/system/role/form.jsx @@ -0,0 +1,104 @@ +import React, { Component } from 'react' +import { Form, Input, InputNumber, Spin } from 'antd' +import { AntIcon } from 'components' +import { cloneDeep } from 'lodash' + +const initialValues = { + sort: 100 +} + +export default class form extends Component { + + state = { + // 加载状态 + loading: true, + } + + // 表单实例 + 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 从后端转换成前段所需格式 + //#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 + } + } + + //#region 自定义方法 + //#endregion + + render() { + return ( +
+ }> +
+ + + + + + + + + + + + +
+
+
+ ) + } +} diff --git a/web-react/src/pages/system/role/index.jsx b/web-react/src/pages/system/role/index.jsx new file mode 100644 index 0000000..e9edb04 --- /dev/null +++ b/web-react/src/pages/system/role/index.jsx @@ -0,0 +1,250 @@ +import React, { Component } from 'react' +import { Button, Card, Dropdown, Form, Input, Menu, message as Message, Popconfirm } from 'antd' +import { AntIcon, Auth, Container, ModalForm, QueryTable, QueryTableActions } from 'components' +import { api } from 'common/api' +import auth from 'components/authorized/handler' +import { isEqual } from 'lodash' +import FormBody from './form' +import MenuForm from './menu' +import DataForm from './data' + +// 配置页面所需接口函数 +const apiAction = { + page: api.getRolePage, + add: api.sysRoleAdd, + edit: api.sysRoleEdit, + delete: api.sysRoleDelete, + + grantMenu: api.sysRoleGrantMenu, + grantData: api.sysRoleGrantData +} + +// 用于弹窗标题 +const name = '角色' + +export default class index extends Component { + + // 表格实例 + table = React.createRef() + + // 新增窗口实例 + addForm = React.createRef() + // 编辑窗口实例 + editForm = React.createRef() + + menuForm = React.createRef() + dataForm = React.createRef() + + columns = [ + { + title: '角色名', + dataIndex: 'name', + sorter: true, + }, + { + title: '唯一编码', + dataIndex: 'code', + sorter: true, + }, + { + title: '排序', + dataIndex: 'sort', + sorter: true, + } + ] + + /** + * 构造函数,在渲染前动态添加操作字段等 + * @param {*} props + */ + constructor(props) { + super(props) + + const flag = auth({ sysRole: [['edit'], ['delete']] }) + + if (flag) { + this.columns.push({ + title: '操作', + width: 150, + dataIndex: 'actions', + render: (text, record) => ( + + this.onOpen(this.editForm, record)}>编辑 + + + this.onDelete(record)} + > + 删除 + + + + + + + this.onOpen(this.menuForm, record)}>授权菜单 + + + + + this.onOpen(this.dataForm, 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), + '删除成功' + ) + } + + //#region 自定义方法 + //#endregion + + render() { + return ( + +
+ + + + + + + + + + } + operator={ + + + + } + /> + + + this.table.current.onReloadData()} + > + + + + this.table.current.onReloadData()} + > + + + + this.table.current.onReloadData()} + > + + + + this.table.current.onReloadData()} + > + + +
+ ) + } +} diff --git a/web-react/src/pages/system/role/menu.jsx b/web-react/src/pages/system/role/menu.jsx new file mode 100644 index 0000000..6c3eb76 --- /dev/null +++ b/web-react/src/pages/system/role/menu.jsx @@ -0,0 +1,89 @@ +import React, { Component } from 'react' +import { cloneDeep } from 'lodash' +import { AntIcon, AuthorityView } from 'components' +import { api } from 'common/api' +import { Empty, Spin } from 'antd' + +export default class form extends Component { + + state = { + // 加载状态 + loading: true, + defaultSelectedKeys: [] + } + + selectedKeys = [] + + view = 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 { data } = await api.sysRoleOwnMenu({ id: this.record.id }) + this.setState({ + defaultSelectedKeys: data + }) + this.view.current.onLoadData() + //#endregion + + this.setState({ + loading: false + }) + } + + /** + * 获取数据 + * 可以对postData进行数据结构调整 + * [异步,必要] + * @returns + */ + async getData() { + const postData = {} + if (this.record) { + postData.id = this.record.id + } + //#region 从前段转换后端所需格式 + postData.grantMenuIdList = this.selectedKeys + //#endregion + return postData + } + + //#region 自定义方法 + async loadData() { + const { data } = await api.SysMenuTreeForGrant() + return data + } + //#endregion + + render() { + return ( + }> + this.selectedKeys = s3} + ref={this.view} + /> + { this.state.loading && } + + ) + } +}