From 16a94b7c5a3dbb146d7d4d61b9407a8a7f08095d 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: Fri, 11 Jun 2021 22:09:33 +0800 Subject: [PATCH] =?UTF-8?q?update=20=E8=BF=81=E7=A7=BBtable=E5=92=8Cmodal-?= =?UTF-8?q?form=E7=BB=84=E4=BB=B6,=E8=BF=81=E7=A7=BBapp=E7=AE=A1=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/authorized/handler.js | 131 +++++++++ web-react/src/components/authorized/index.jsx | 73 ++++- web-react/src/components/index.js | 44 +-- web-react/src/components/modal-form/index.jsx | 192 ++++++++++++- .../components/query-table-actions/index.jsx | 22 +- .../src/components/query-table/index.jsx | 243 +++++++++++++++- .../business/house/info/form/base/index.jsx | 4 +- .../pages/business/house/info/form/index.jsx | 4 +- web-react/src/pages/home/index.jsx | 4 +- web-react/src/pages/system/app/form.jsx | 87 ++++++ web-react/src/pages/system/app/index.jsx | 268 ++++++++++++++++++ web-react/src/store/index.js | 20 +- web-react/src/store/reducer.js | 3 +- web-react/src/util/dic/index.js | 42 +++ web-react/src/util/format/index.js | 25 ++ .../src/views/main/_layout/header/index.jsx | 4 +- .../src/views/main/_layout/header/user.jsx | 4 +- 17 files changed, 1099 insertions(+), 71 deletions(-) create mode 100644 web-react/src/components/authorized/handler.js create mode 100644 web-react/src/pages/system/app/form.jsx create mode 100644 web-react/src/pages/system/app/index.jsx create mode 100644 web-react/src/util/dic/index.js diff --git a/web-react/src/components/authorized/handler.js b/web-react/src/components/authorized/handler.js new file mode 100644 index 0000000..28267b7 --- /dev/null +++ b/web-react/src/components/authorized/handler.js @@ -0,0 +1,131 @@ +import store from 'store' + +const { getState } = store + +const stroePath = 'user' + +const authByArray = (auth, permissions) => { + + const flags = [] + + auth.forEach(p => { + switch (p.constructor) { + case String: + flags.push([permissions.includes(p), '&&']) + break + case Array: + flags.push([authByArray(p, permissions), '||']) + break + case Boolean: + flags.push([p, '&&']) + break + } + }) + + let result + + flags.forEach((p, i) => { + if (p[1] === '&&') { + if (i === 0) { + result = true + } + if (result) { + result = p[0] + } + } else { + if (i === 0) { + result = false + } + if (!result) { + result = p[0] + } + } + //result = p[1] === '&&' ? result && p[0] : result || p[0] + }) + + return result +} + +const authByJson = (auth, permissions) => { + + let result = true + + const flags = [] + + const deepName = (arr, key) => { + arr.forEach((p, i) => { + switch (p.constructor) { + case String: + arr[i] = `${key}:${p}` + break + case Array: + p = deepName(p, key) + break + default: + break + } + }) + return arr + } + + for (let key in auth) { + const app = auth[key] + switch (app.constructor) { + case String: + flags.push(permissions.includes(`${key}:${app}`)) + break + case Array: + flags.push(authByArray(deepName(app, key), permissions)) + break + } + } + + flags.forEach(p => { + result = result && p + }) + + return result + +} + +const auth = (auth) => { + + let info = this + + if (!info || !Object.keys(info).length) { + info = getState(stroePath) + } + + if (!info) { + return false + } + + /** + * 超级管理员 + */ + if (info.adminType === 1) { + return true + } + + const permissions = info.permissions + + let flag = false + + if (auth) { + switch (auth.constructor) { + case String: + flag = permissions.includes(auth) + break + case Array: + flag = authByArray(auth, permissions) + break + case Object: + flag = authByJson(auth, permissions) + break + } + } + + return flag +} + +export default auth \ No newline at end of file diff --git a/web-react/src/components/authorized/index.jsx b/web-react/src/components/authorized/index.jsx index fca95f4..a325429 100644 --- a/web-react/src/components/authorized/index.jsx +++ b/web-react/src/components/authorized/index.jsx @@ -1,11 +1,74 @@ +/** + * auth: 允许的权限 + * authExclude: 排除的权限 + * + * auth的几种传值方式 + * 1.String + * 例: auth="sysApp:page" + * 直接传入字符串,对单项权限进行验证 + * + * 2.Array + * 2.1.单项权限 + * 例: auth={['sysApp:page']} + * 2.2.并且关系多项权限 + * 例: auth={['sysApp:page', 'sysApp:add']} + * 数组中传入多个字符串 + * 此时验证的是同时拥有"sysApp:page"和"sysApp:add"两项权限才会渲染 + * 2.3.或者关系多项权限 + * 例: auth={[['sysApp:page', 'sysApp:add'], ['sysApp:edit']]} + * 二维数组结构,内部数组之间为并且关系 + * 此时验证的是"sysApp:page"&"sysApp:add"||"sysApp:edit" + * 注意:或者的条件必须包括在数组中,暴露在外则判定为并且 + * 2.4.可直接传入布尔值 + * 例: auth={['sysApp:page', 1 === 1]} + * auth={[['sysApp:page', 'sysApp:add'], [1 === 1]]} + * + * 3.Json + * 如果觉得多项权限时每次都要写应用编号比较繁琐,可对Array形式进行简化 + * 3.1.单项权限 + * 例: auth={{ sysApp: 'page' }} + * 3.2.并且关系多项权限 + * 例: auth={{ sysApp: ['page', 'add'] }} + * 3.3.或者关系多项权限 + * 例: auth={{ sysApp: [['page', 'add'], ['edit']]}} + * 3.4.可直接传入布尔值 + * 例: auth={{ sysApp: ['page', 1 === 1] }} + * auth={{ sysApp: [['page', 'add'], [1 === 1]] }} + * + */ + import React, { Component } from 'react' +import store from 'store' +import auth from './handler' + +const { getState, subscribe } = store + +const stroePath = 'user' export default class Auth extends Component { - render() { - return ( -
-
- ) + state = getState(stroePath) + + constructor(props) { + super(props) + + this.unsubscribe = subscribe(stroePath, () => { + this.setState(getState(stroePath)) + }) + } + + componentWillUnmount() { + this.unsubscribe() + } + + render() { + + const flag = auth.call(this.state, this.props.auth) + + if (flag) { + return this.props.children + } + + return <> } } diff --git a/web-react/src/components/index.js b/web-react/src/components/index.js index fcf95f5..e27657e 100644 --- a/web-react/src/components/index.js +++ b/web-react/src/components/index.js @@ -1,31 +1,13 @@ -import AntIcon from './ant-icon' -import AuthorityView from './authority-view' -import Auth from './authorized' -import ComponentDynamic from './component-dynamic' -import Container from './container' -import IconSelector from './icon-selector' -import Image from './image' -import ModalForm from './modal-form' -import PhotoSwipe from './photo-swipe' -import QueryList from './query-list' -import QueryTable from './query-table' -import QueryTableActions from './query-table-actions' -import QueryTreeLayout from './query-tree-layout' - -const components = { - AntIcon, - AuthorityView, - Auth, - ComponentDynamic, - Container, - IconSelector, - Image, - ModalForm, - PhotoSwipe, - QueryList, - QueryTable, - QueryTableActions, - QueryTreeLayout -} - -export default components \ No newline at end of file +export { default as AntIcon } from 'components/ant-icon' +export { default as AuthorityView } from './authority-view' +export { default as Auth } from './authorized' +export { default as ComponentDynamic } from './component-dynamic' +export { default as Container } from './container' +export { default as IconSelector } from './icon-selector' +export { default as Image } from './image' +export { default as ModalForm } from './modal-form' +export { default as PhotoSwipe } from './photo-swipe' +export { default as QueryList } from './query-list' +export { default as QueryTable } from './query-table' +export { default as QueryTableActions } from './query-table-actions' +export { default as QueryTreeLayout } from './query-tree-layout' \ No newline at end of file diff --git a/web-react/src/components/modal-form/index.jsx b/web-react/src/components/modal-form/index.jsx index 6fbcc0f..040d6e2 100644 --- a/web-react/src/components/modal-form/index.jsx +++ b/web-react/src/components/modal-form/index.jsx @@ -1,11 +1,195 @@ import React, { Component } from 'react' +import { message as Message, Modal } from 'antd' +import { cloneDeep, isEqual } from 'lodash' export default class ModalForm extends Component { - render() { - return ( -
-
+ state = { + // 弹窗显示/隐藏 + visible: false, + // 提交状态 + confirmLoading: false + } + + // 子元素实例 + childNode = React.createRef() + + // 弹窗类型 + type = 'modal' + + // 从外部传入的数据 + data = null + // 数据结构调整后的快照 + snapshot = null + + // 完成操作 + action = async () => { } + + // 是否在关闭时校验数据改变 + compareOnClose = true + + + constructor(props) { + super(props) + + if (this.props.type) { + if (!['modal', 'drawer'].includes(this.props.type)) + throw new Error('props [type] error') + this.type = this.props.type + } + + if (this.props.action) { + this.action = this.props.action + } + + if (typeof this.props.compareOnClose === 'boolean') { + this.compareOnClose = this.props.compareOnClose + } + } + + /** + * 打开弹窗 + * @param {*} data + */ + open(data) { + this.data = data + this.setState({ visible: true }) + } + + /** + * 关闭弹窗 + */ + close() { + this.setState({ visible: false }) + } + + /** + * 子元素创建后回调 + * 对子元素数据进行填充,(如需关闭时对比)之后再获取结构调整后的数据快照 + * @returns + */ + async onCreated() { + const body = this.childNode.current + if (!body || !body.fillData) return + + await body.fillData(this.data) + // 保存此时的form内容为快照 + if (this.compareOnClose) { + this.snapshot = cloneDeep(body.form.current.getFieldsValue()) + } + } + + /** + * 取消编辑 + * (如需关闭时对比)获取当前数据结构与快照对比 + * @returns + */ + async onClose() { + const body = this.childNode.current + if (!body) return + + if (this.compareOnClose) { + const formData = body.form.current.getFieldsValue() + if (!isEqual(this.snapshot, formData)) { + Modal.confirm({ + title: '是否确认关闭', + content: '当前内容已更改,是否确认不保存并且关闭', + onOk: () => { + this.close() + } + }) + return + } + } + + this.close() + } + + /** + * 完成编辑 + * 校验并获取结构调整后的数据,调用this.action进行操作 + * @returns + */ + async onOk() { + const body = this.childNode.current + if (!body || !this.action || !body.getData) return + + this.setState({ confirmLoading: true }) + try { + const postData = await body.getData() + + const { success } = await this.action(postData) + if (success) { + Message.success(this.props.successMessage || '保存成功') + this.close() + if (typeof this.props.onSuccess === 'function') { + this.props.onSuccess(postData) + } + } + + } finally { + this.setState({ confirmLoading: false }) + } + } + + /** + * 渲染对话框 + * @param {*} props + * @param {*} on + * @param {*} childWithProps + * @returns + */ + renderModal(props, on, childWithProps) { + + on = { + ...on, + onCancel: () => this.onClose(this.compareOnClose) + } + + return + {childWithProps} + + + } + + /** + * 渲染抽屉 + * @param {*} props + * @param {*} on + * @param {*} childWithProps + * @returns + */ + renderDrawer(props, on, childWithProps) { + return
+ } + + render() { + + const props = { + ...this.props, + visible: this.state.visible, + destroyOnClose: true, + + confirmLoading: this.state.confirmLoading + } + + const on = { + onOk: () => this.onOk() + } + + const childWithProps = React.cloneElement( + React.Children.only(this.props.children), + { + created: childNode => { + this.childNode.current = childNode + this.onCreated() + } + } ) + + return this.type === 'modal' ? + this.renderModal(props, on, childWithProps) + : + this.renderDrawer(props, on, childWithProps) } } diff --git a/web-react/src/components/query-table-actions/index.jsx b/web-react/src/components/query-table-actions/index.jsx index 517f975..6d562a4 100644 --- a/web-react/src/components/query-table-actions/index.jsx +++ b/web-react/src/components/query-table-actions/index.jsx @@ -1,10 +1,28 @@ import React, { Component } from 'react' +import { Divider } from 'antd' export default class QueryTableActions extends Component { + + renderActions() { + const { children } = this.props + const actions = [] + + Array.isArray(children) ? children.forEach((action, i) => { + actions.push(action) + if (i < this.props.children.length - 1) { + actions.push() + } + }) : (actions.push(children)) + + return actions + } + render() { return ( -
- +
+
+ {this.renderActions()} +
) } diff --git a/web-react/src/components/query-table/index.jsx b/web-react/src/components/query-table/index.jsx index 6eeae05..83f3fa6 100644 --- a/web-react/src/components/query-table/index.jsx +++ b/web-react/src/components/query-table/index.jsx @@ -1,11 +1,248 @@ import React, { Component } from 'react' +import { Form, Button, Table } from 'antd' +import { AntIcon } from 'components' + +const clearChildren = (data) => { + data.forEach(p => { + if (p.children) { + if (p.children.length) { + p.children = clearChildren(p.children) + } else { + delete p.children + } + } + }) + return data +} export default class QueryTable extends Component { - render() { - return ( -
+ state = { + // 加载状态 + loading: false, + // 表格类型 + type: '', + // 数据 + 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 + + // 排序字段 + sorter = { + sortField: '', + sortOrder: '', + } + + 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.sorter + }, this.query) + if (res.rows || res.data || res.items) { + this.setState({ + type: 'table', + dataSource: res.rows || res.data || res.items + }) + + this.pagination.total = res.totalCount + } else if (res) { + this.setState({ + type: 'tree', + dataSource: clearChildren(res) + }) + + this.pagination = false + } + + this.onLoaded() + } + + /** + * 数据开始加载 + */ + onLoading() { + this.setState({ + loading: { + indicator: + } + }) + } + + /** + * 数据加载完成 + */ + 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() + } + + /** + * 表格分页/筛选/排序 + * @param {*} pagination + * @param {*} filters + * @param {*} sorter + */ + onTableChange(pagination, filters, sorter) { + this.pagination = pagination + this.sorter = { + sortField: sorter.field, + sortOrder: sorter.order, + } + this.onLoadData() + } + + /** + * 渲染查询栏 + * @returns + */ + renderQueryBar() { + + const { query, moreQuery } = this.props + + return ( +
+
this.onQuery(value)} + > + {query} + + + + + + { + moreQuery && + } + +
) } + + render() { + + const { loading, dataSource } = this.state + + const { query, operator, columns } = this.props + + const props = { + loading, + pagination: this.pagination, + dataSource, + columns: (columns || []).filter(p => !p.hidden), + bordered: true, + size: 'middle', + rowKey: record => record.id || Math.random().toString(16).slice(2), + scroll: { x: 'max-content' } + } + + const on = { + onChange: (...args) => this.onTableChange.apply(this, args) + } + + return ( +
+ {query && this.renderQueryBar()} +
+
+ {operator} +
+
+ + + +
+
+ + + ) + } } diff --git a/web-react/src/pages/business/house/info/form/base/index.jsx b/web-react/src/pages/business/house/info/form/base/index.jsx index 54e38c2..b16c947 100644 --- a/web-react/src/pages/business/house/info/form/base/index.jsx +++ b/web-react/src/pages/business/house/info/form/base/index.jsx @@ -2,9 +2,7 @@ import React, { Component } from 'react' import ReactDOM from 'react-dom' import { Row, Col, Card, Anchor } from 'antd' import { defaultsDeep } from 'lodash' -import Components from 'components' - -const { ComponentDynamic, Container } = Components +import { ComponentDynamic, Container } from 'components' const parts = [ { diff --git a/web-react/src/pages/business/house/info/form/index.jsx b/web-react/src/pages/business/house/info/form/index.jsx index ae25b41..9c4eeba 100644 --- a/web-react/src/pages/business/house/info/form/index.jsx +++ b/web-react/src/pages/business/house/info/form/index.jsx @@ -1,9 +1,7 @@ import React, { Component } from 'react' import { Tabs, Button, message } from 'antd' import { defaultsDeep } from 'lodash' -import Components from 'components' - -const { ComponentDynamic, Container } = Components +import { ComponentDynamic, Container } from 'components' const tabs = [ { diff --git a/web-react/src/pages/home/index.jsx b/web-react/src/pages/home/index.jsx index f6c7aba..f2b5a96 100644 --- a/web-react/src/pages/home/index.jsx +++ b/web-react/src/pages/home/index.jsx @@ -2,14 +2,12 @@ import React, { Component } from 'react' import { Row, Col, Divider } from 'antd' import { isEqual } from 'lodash' import store from 'store' -import Components from 'components' +import { Container, Image, AntIcon } from 'components' import moment from 'moment' import './index.less' const { getState, subscribe } = store -const { Container, Image, AntIcon } = Components - const storePath = 'user' export default class index extends Component { diff --git a/web-react/src/pages/system/app/form.jsx b/web-react/src/pages/system/app/form.jsx new file mode 100644 index 0000000..f857c23 --- /dev/null +++ b/web-react/src/pages/system/app/form.jsx @@ -0,0 +1,87 @@ +import React, { Component } from 'react' +import { Form, Input, Spin } from 'antd' +import { AntIcon } from 'components' +import { cloneDeep } from 'lodash' + +const initialValues = {} + +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 {*} record + */ + async fillData(record) { + + this.record = cloneDeep(record) + /** */ + 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 + } + /** */ + return postData + } + } + + //#region 自定义方法 + //#endregion + + render() { + return ( + + }> +
+ + + + + + +
+
+ + ) + } +} diff --git a/web-react/src/pages/system/app/index.jsx b/web-react/src/pages/system/app/index.jsx new file mode 100644 index 0000000..e4e21a5 --- /dev/null +++ b/web-react/src/pages/system/app/index.jsx @@ -0,0 +1,268 @@ +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.getAppPage, + add: api.sysAppAdd, + edit: api.sysAppEdit, + delete: api.sysAppDelete, + + setDefault: api.sysAppSetAsDefault +} + +// 用于弹窗标题 +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: 'active', + sorter: true, + render: (text, record) => (<> + {text ? '是' : '否'} + { + !record.active && + + + this.onSetDefault(record)} + > + 设为默认 + + + + } + ) + }, + { + title: '状态', + dataIndex: 'status', + sorter: true, + render: text => (<>{this.bindCodeValue(text, 'common_status')}) + }, + { + title: '排序', + dataIndex: 'sort', + sorter: true, + }, + ] + + /** + * 构造函数,在渲染前动态添加操作字段等 + * @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) => ( + + 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('common_status').then(res => { + this.setState({ + codes: res + }, () => { + this.table.current.onLoadData() + }) + }) + } + + /** + * 调用加载数据接口,可在调用前对query进行处理 + * [异步,必要] + * @param {*} params + * @param {*} query + * @returns + */ + async loadData(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) { + 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 自定义方法 + async onSetDefault(record) { + this.onAction( + apiAction.setDefault(record), + '设置成功' + ) + } + //#endregion + + render() { + return ( + +
+ + + + + + + + + + } + operator={ + + } + /> + + + this.table.current.onReloadData()} + > + + + + this.table.current.onReloadData()} + > + + +
+ ) + } +} diff --git a/web-react/src/store/index.js b/web-react/src/store/index.js index 9771438..101e479 100644 --- a/web-react/src/store/index.js +++ b/web-react/src/store/index.js @@ -2,16 +2,16 @@ import { createStore } from 'redux' import { cloneDeep, result, isEqual } from 'lodash' import reducer from './reducer' -const store = createStore(reducer) +const _store = createStore(reducer) -const exStore = cloneDeep(store) +const store = cloneDeep(_store) /** * 允许传入第一个参数path,只监听指定属性路径的对象 * @param {...any} args * @returns */ -exStore.subscribe = (...args) => { +store.subscribe = (...args) => { let path, listener, snapshot @@ -19,13 +19,13 @@ exStore.subscribe = (...args) => { if (typeof args[0] === 'string' && typeof args[1] === 'function') { path = args[0] listener = args[1] - snapshot = cloneDeep(result(store.getState(), path)) + snapshot = cloneDeep(result(_store.getState(), path)) } else { listener = args[0] } - return store.subscribe((...args) => { - const state = store.getState() + return _store.subscribe((...args) => { + const state = _store.getState() if (path) { const resultState = cloneDeep(result(state, path)) if (!isEqual(snapshot, resultState)) { @@ -43,12 +43,12 @@ exStore.subscribe = (...args) => { * @param {*} path * @returns */ -exStore.getState = (path) => { +store.getState = (path) => { if (path) { - return result(store.getState(), path) + return result(_store.getState(), path) } - return store.getState() + return _store.getState() } -export default exStore \ No newline at end of file +export default store \ No newline at end of file diff --git a/web-react/src/store/reducer.js b/web-react/src/store/reducer.js index 16202aa..de2c558 100644 --- a/web-react/src/store/reducer.js +++ b/web-react/src/store/reducer.js @@ -14,7 +14,8 @@ const user = (state = {}, action) => { const dicData = (state = {}, action) => { switch (action.type) { case 'ADD_DIC_DATA': - return state + const _state = { ...state, ...action.value } + return _state default: return state } diff --git a/web-react/src/util/dic/index.js b/web-react/src/util/dic/index.js new file mode 100644 index 0000000..10a6763 --- /dev/null +++ b/web-react/src/util/dic/index.js @@ -0,0 +1,42 @@ +import { api } from 'common/api' +import { mapKeys } from 'lodash' +import store from 'store' +import { toCamelCase } from 'util/format' + +const { getState, dispatch } = store + +const getDicData = async (...args) => { + const dicData = getState('dicData') + let result = {} + const code = [] + for (let i = 0; i < args.length; i++) { + const codeName = toCamelCase(args[i]) + if (!dicData.hasOwnProperty(codeName)) { + code.push(args[i]) + } else { + result[codeName] = dicData[codeName] + } + } + + if (code.length) { + try { + const value = mapKeys((await api.sysDictTypeDropDowns({ code })).data, (value, key) => { + return toCamelCase(key) + }) + + dispatch({ + type: 'ADD_DIC_DATA', + value + }) + + result = { ...result, ...value } + + return result + } + catch { } + } + + return dicData +} + +export default getDicData \ No newline at end of file diff --git a/web-react/src/util/format/index.js b/web-react/src/util/format/index.js index 3b0a568..86fd9b1 100644 --- a/web-react/src/util/format/index.js +++ b/web-react/src/util/format/index.js @@ -16,4 +16,29 @@ export const numberToChinese = (val) => { const chinanum = overWan ? getWan(overWan) + '万' + getWan(noWan) : getWan(num) return chinanum +} + +/** + * 下划线转驼峰 + * @param {String} str + */ +export const toCamelCase = (str) => { + if (typeof str === 'string') { + return str.toLowerCase().split('_').map((p, i) => { + if (i > 0) { + return p[0].toUpperCase() + p.slice(1) + } else { + return p + } + }).join('') + } + return str +} + +/** + * 驼峰转下划线 + * @param {String} str + */ +export const toUnderScoreCase = (str) => { + } \ No newline at end of file diff --git a/web-react/src/views/main/_layout/header/index.jsx b/web-react/src/views/main/_layout/header/index.jsx index 1d423d3..a25b9e4 100644 --- a/web-react/src/views/main/_layout/header/index.jsx +++ b/web-react/src/views/main/_layout/header/index.jsx @@ -1,11 +1,9 @@ import React, { Component } from 'react' import { Layout, Badge } from 'antd' -import Components from 'components' +import { AntIcon, Container } from 'components' import Logo from '../logo' import User from './user' -const { AntIcon, Container } = Components - export default class index extends Component { render() { return ( diff --git a/web-react/src/views/main/_layout/header/user.jsx b/web-react/src/views/main/_layout/header/user.jsx index 4926212..77d00d8 100644 --- a/web-react/src/views/main/_layout/header/user.jsx +++ b/web-react/src/views/main/_layout/header/user.jsx @@ -5,12 +5,10 @@ import { isEqual } from 'lodash' import store from 'store' import { api } from 'common/api' import { token } from 'common/token' -import Components from 'components' +import { AntIcon, Image } from 'components' const { getState, subscribe } = store -const { AntIcon, Image } = Components - const storePath = 'user' let userOpenTimer, userCloseTimer