update 迁移table和modal-form组件,迁移app管理

This commit is contained in:
2021-06-11 22:09:33 +08:00
parent f5bd5e73c8
commit 16a94b7c5a
17 changed files with 1099 additions and 71 deletions

View File

@@ -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

View File

@@ -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 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 { export default class Auth extends Component {
render() {
return (
<div>
</div> 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 <></>
} }
} }

View File

@@ -1,31 +1,13 @@
import AntIcon from './ant-icon' export { default as AntIcon } from 'components/ant-icon'
import AuthorityView from './authority-view' export { default as AuthorityView } from './authority-view'
import Auth from './authorized' export { default as Auth } from './authorized'
import ComponentDynamic from './component-dynamic' export { default as ComponentDynamic } from './component-dynamic'
import Container from './container' export { default as Container } from './container'
import IconSelector from './icon-selector' export { default as IconSelector } from './icon-selector'
import Image from './image' export { default as Image } from './image'
import ModalForm from './modal-form' export { default as ModalForm } from './modal-form'
import PhotoSwipe from './photo-swipe' export { default as PhotoSwipe } from './photo-swipe'
import QueryList from './query-list' export { default as QueryList } from './query-list'
import QueryTable from './query-table' export { default as QueryTable } from './query-table'
import QueryTableActions from './query-table-actions' export { default as QueryTableActions } from './query-table-actions'
import QueryTreeLayout from './query-tree-layout' export { default as QueryTreeLayout } from './query-tree-layout'
const components = {
AntIcon,
AuthorityView,
Auth,
ComponentDynamic,
Container,
IconSelector,
Image,
ModalForm,
PhotoSwipe,
QueryList,
QueryTable,
QueryTableActions,
QueryTreeLayout
}
export default components

View File

@@ -1,11 +1,195 @@
import React, { Component } from 'react' import React, { Component } from 'react'
import { message as Message, Modal } from 'antd'
import { cloneDeep, isEqual } from 'lodash'
export default class ModalForm extends Component { export default class ModalForm extends Component {
render() {
return (
<div>
</div> 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 <Modal className="yo-modal-form" {...props} {...on}>
{childWithProps}
</Modal>
}
/**
* 渲染抽屉
* @param {*} props
* @param {*} on
* @param {*} childWithProps
* @returns
*/
renderDrawer(props, on, childWithProps) {
return <div></div>
}
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)
} }
} }

View File

@@ -1,10 +1,28 @@
import React, { Component } from 'react' import React, { Component } from 'react'
import { Divider } from 'antd'
export default class QueryTableActions extends Component { 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(<Divider type="vertical" key={i} />)
}
}) : (actions.push(children))
return actions
}
render() { render() {
return ( return (
<div> <div className="yo-table-actions">
<div className="yo-table-actions--inner">
{this.renderActions()}
</div>
</div> </div>
) )
} }

View File

@@ -1,11 +1,248 @@
import React, { Component } from 'react' 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 { export default class QueryTable extends Component {
render() {
return (
<div>
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: <AntIcon type="loading" />
}
})
}
/**
* 数据加载完成
*/
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 (
<div className="yo-query-bar">
<Form
layout="inline"
ref={this.form}
onFinish={(value) => this.onQuery(value)}
>
{query}
<Form.Item>
<Button.Group className="mr-xs">
<Button htmlType="submit" type="primary">查询</Button>
<Button onClick={() => this.onResetQuery()}>重置</Button>
</Button.Group>
{
moreQuery && <Button>更多查询条件</Button>
}
</Form.Item>
</Form>
</div> </div>
) )
} }
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 (
<section>
{query && this.renderQueryBar()}
<div className="yo-action-bar">
<div className="yo-action-bar--actions">
{operator}
</div>
<div className="yo-action-bar--actions">
<Button.Group>
<Button>刷新</Button>
</Button.Group>
</div>
</div>
<Table className="yo-table" {...props} {...on} />
</section>
)
}
} }

View File

@@ -2,9 +2,7 @@ import React, { Component } from 'react'
import ReactDOM from 'react-dom' import ReactDOM from 'react-dom'
import { Row, Col, Card, Anchor } from 'antd' import { Row, Col, Card, Anchor } from 'antd'
import { defaultsDeep } from 'lodash' import { defaultsDeep } from 'lodash'
import Components from 'components' import { ComponentDynamic, Container } from 'components'
const { ComponentDynamic, Container } = Components
const parts = [ const parts = [
{ {

View File

@@ -1,9 +1,7 @@
import React, { Component } from 'react' import React, { Component } from 'react'
import { Tabs, Button, message } from 'antd' import { Tabs, Button, message } from 'antd'
import { defaultsDeep } from 'lodash' import { defaultsDeep } from 'lodash'
import Components from 'components' import { ComponentDynamic, Container } from 'components'
const { ComponentDynamic, Container } = Components
const tabs = [ const tabs = [
{ {

View File

@@ -2,14 +2,12 @@ import React, { Component } from 'react'
import { Row, Col, Divider } from 'antd' import { Row, Col, Divider } from 'antd'
import { isEqual } from 'lodash' import { isEqual } from 'lodash'
import store from 'store' import store from 'store'
import Components from 'components' import { Container, Image, AntIcon } from 'components'
import moment from 'moment' import moment from 'moment'
import './index.less' import './index.less'
const { getState, subscribe } = store const { getState, subscribe } = store
const { Container, Image, AntIcon } = Components
const storePath = 'user' const storePath = 'user'
export default class index extends Component { export default class index extends Component {

View File

@@ -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 (
<Form
initialValues={initialValues}
ref={this.form}
>
<Spin spinning={this.state.loading} indicator={<AntIcon type="loading" />}>
<div className="yo-form-group">
<Form.Item label="应用名称" name="name" rules={[{ required: true, message: '请输入应用名称' }]}>
<Input autoComplete="off" placeholder="请输入应用名称" />
</Form.Item>
<Form.Item label="唯一编码" name="code" rules={[{ required: true, message: '请输入唯一编码' }]}>
<Input autoComplete="off" placeholder="请输入唯一编码" />
</Form.Item>
</div>
</Spin>
</Form>
)
}
}

View File

@@ -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 && <Auth auth="sysApp:setAsDefault">
<QueryTableActions>
<span></span>
<Popconfirm
placement="topRight"
title="是否确认设置为默认应用"
onConfirm={() => this.onSetDefault(record)}
>
<a>设为默认</a>
</Popconfirm>
</QueryTableActions>
</Auth>
}
</>)
},
{
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) => (<QueryTableActions>
<Auth auth="sysApp:edit">
<a onClick={() => this.onOpen(this.editForm, record)}>编辑</a>
</Auth>
<Auth auth="sysApp:delete">
<Popconfirm
placement="topRight"
title="是否确认删除"
onConfirm={() => this.onDelete(record)}
>
<a>删除</a>
</Popconfirm>
</Auth>
</QueryTableActions>)
})
}
}
/**
* 阻止外部组件引发的渲染,提升性能
* 可自行添加渲染条件
* [必要]
* @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 (
<Container mode="fluid">
<br />
<Card bordered={false}>
<QueryTable
ref={this.table}
autoLoad={false}
loadData={this.loadData}
columns={this.columns}
query={
<Auth auth="sysApp:page">
<Form.Item label="应用名称" name="name">
<Input autoComplete="off" placeholder="请输入应用名称" />
</Form.Item>
<Form.Item label="唯一编码" name="code">
<Input autoComplete="off" placeholder="请输入唯一编码" />
</Form.Item>
</Auth>
}
operator={
<Button
icon={<AntIcon type="plus" />}
onClick={() => this.onOpen(this.addForm)}
>新增应用</Button>
}
/>
</Card>
<ModalForm
title={`新增${name}`}
action={apiAction.add}
ref={this.addForm}
onSuccess={() => this.table.current.onReloadData()}
>
<FormBody />
</ModalForm>
<ModalForm
title={`编辑${name}`}
action={apiAction.edit}
ref={this.editForm}
onSuccess={() => this.table.current.onReloadData()}
>
<FormBody />
</ModalForm>
</Container>
)
}
}

View File

@@ -2,16 +2,16 @@ import { createStore } from 'redux'
import { cloneDeep, result, isEqual } from 'lodash' import { cloneDeep, result, isEqual } from 'lodash'
import reducer from './reducer' import reducer from './reducer'
const store = createStore(reducer) const _store = createStore(reducer)
const exStore = cloneDeep(store) const store = cloneDeep(_store)
/** /**
* 允许传入第一个参数path,只监听指定属性路径的对象 * 允许传入第一个参数path,只监听指定属性路径的对象
* @param {...any} args * @param {...any} args
* @returns * @returns
*/ */
exStore.subscribe = (...args) => { store.subscribe = (...args) => {
let path, let path,
listener, listener,
snapshot snapshot
@@ -19,13 +19,13 @@ exStore.subscribe = (...args) => {
if (typeof args[0] === 'string' && typeof args[1] === 'function') { if (typeof args[0] === 'string' && typeof args[1] === 'function') {
path = args[0] path = args[0]
listener = args[1] listener = args[1]
snapshot = cloneDeep(result(store.getState(), path)) snapshot = cloneDeep(result(_store.getState(), path))
} else { } else {
listener = args[0] listener = args[0]
} }
return store.subscribe((...args) => { return _store.subscribe((...args) => {
const state = store.getState() const state = _store.getState()
if (path) { if (path) {
const resultState = cloneDeep(result(state, path)) const resultState = cloneDeep(result(state, path))
if (!isEqual(snapshot, resultState)) { if (!isEqual(snapshot, resultState)) {
@@ -43,12 +43,12 @@ exStore.subscribe = (...args) => {
* @param {*} path * @param {*} path
* @returns * @returns
*/ */
exStore.getState = (path) => { store.getState = (path) => {
if (path) { if (path) {
return result(store.getState(), path) return result(_store.getState(), path)
} }
return store.getState() return _store.getState()
} }
export default exStore export default store

View File

@@ -14,7 +14,8 @@ const user = (state = {}, action) => {
const dicData = (state = {}, action) => { const dicData = (state = {}, action) => {
switch (action.type) { switch (action.type) {
case 'ADD_DIC_DATA': case 'ADD_DIC_DATA':
return state const _state = { ...state, ...action.value }
return _state
default: default:
return state return state
} }

View File

@@ -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

View File

@@ -16,4 +16,29 @@ export const numberToChinese = (val) => {
const chinanum = overWan ? getWan(overWan) + '万' + getWan(noWan) : getWan(num) const chinanum = overWan ? getWan(overWan) + '万' + getWan(noWan) : getWan(num)
return chinanum 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) => {
} }

View File

@@ -1,11 +1,9 @@
import React, { Component } from 'react' import React, { Component } from 'react'
import { Layout, Badge } from 'antd' import { Layout, Badge } from 'antd'
import Components from 'components' import { AntIcon, Container } from 'components'
import Logo from '../logo' import Logo from '../logo'
import User from './user' import User from './user'
const { AntIcon, Container } = Components
export default class index extends Component { export default class index extends Component {
render() { render() {
return ( return (

View File

@@ -5,12 +5,10 @@ import { isEqual } from 'lodash'
import store from 'store' import store from 'store'
import { api } from 'common/api' import { api } from 'common/api'
import { token } from 'common/token' import { token } from 'common/token'
import Components from 'components' import { AntIcon, Image } from 'components'
const { getState, subscribe } = store const { getState, subscribe } = store
const { AntIcon, Image } = Components
const storePath = 'user' const storePath = 'user'
let userOpenTimer, userCloseTimer let userOpenTimer, userCloseTimer