Commit c0072c51 authored by 前端-许佳敏's avatar 前端-许佳敏

新增系统权限管理

parent 6464e49b
const AuthConfigRoute = {
path: '/memberCenter/systemSetting',
name: 'systemSetting',
key: 'systemSetting',
icon: 'smile',
routes: [
{
path: '/memberCenter/systemSetting/authConfig',
key: 'authConfig',
name: 'authConfig',
routes: [
{
path: '/memberCenter/systemSetting/authConfig/organ',
name: 'organ',
component: '@/pages/authConfig/organ',
},
{
path: '/memberCenter/systemSetting/authConfig/memberSystem',
name: 'memberSystem',
component: '@/pages/authConfig/memberSystem',
},
{
path: '/memberCenter/systemSetting/authConfig/memberSystem/memberDetail',
name: 'memberDetail',
component: '@/pages/authConfig/memberSystem/memberDetail',
hideInMenu: true,
hidePageHeader: true
},
{
path: '/memberCenter/systemSetting/authConfig/userSystem',
name: 'userSystem',
component: '@/pages/authConfig/userSystem',
},
{
path: '/memberCenter/systemSetting/authConfig/userSystem/userDetail',
name: 'userSystem',
component: '@/pages/authConfig/userSystem/userDetail',
hideInMenu: true,
hidePageHeader: true
},
]
}
],
}
export default AuthConfigRoute
\ No newline at end of file
......@@ -11,8 +11,9 @@ import ChannelRoute from './channelRoute' // 渠道能力路由
import TranactionRoute from './tranactionRoute' // 交易能力路由
import LogisticsRoute from './logisticsRoutes' // 物流能力路由
import PayandSettleRoute from './payandSettle' //支付与结算
import AuthConfigRoute from './authConfigRoute'
const routes = [CommodityRoute, MemberRoute, ShopRoute, ChannelRoute, TranactionRoute,PayandSettleRoute ,LogisticsRoute]
const routes = [CommodityRoute, MemberRoute, ShopRoute, ChannelRoute, TranactionRoute,PayandSettleRoute ,LogisticsRoute, AuthConfigRoute]
const memberCenterRoute = {
path: '/memberCenter',
......
import React, { useState } from 'react'
import { Tag, Row } from 'antd'
import { useFormEffects } from '@formily/antd'
const TableTagList = (props) => {
const { value = [], mutators, editable } = props
const { extra = null, callback = null } = props.props['x-component-props'] ? props.props['x-component-props'] : {}
const handleClose = (id) => {
callback && callback(id)
mutators.remove(value.findIndex(v => v.id === id))
}
return (
<div className="table-tag-list" style={{width: '100%'}}>
<Row style={{flexWrap: 'wrap'}}>
{
value.map(v => <Tag closable={editable} onClose={() => handleClose(v.id)} color="#4279DF" key={v.id} style={{marginBottom: 8}}>{v.roleName}</Tag>)
}
</Row>
{extra}
</div>
)
}
TableTagList.defaultProps = {}
TableTagList.isFieldComponent = true;
export default TableTagList
\ No newline at end of file
......@@ -22,6 +22,7 @@ import CircleBox from './components/CircleBox';
import Phone from './components/Phone';
import CustomRadio from './components/CustomRadio';
import SearchSelect from './components/SearchSelect';
import TableTagList from './components/TableTagList';
import './index.less'
import { Checkbox } from '@formily/antd-components';
import cx from 'classnames'
......@@ -141,6 +142,7 @@ export const componentExport = {
Phone,
SearchSelect,
Input,
TableTagList,
}
const NiceForm: React.FC<NiceFormProps> = props => {
const { children, components, ...reset } = props;
......
......@@ -4,14 +4,14 @@ import { PlayCircleOutlined,PauseCircleOutlined } from '@ant-design/icons'
export interface StatusSwitchProps {
record: any,
fieldNames?: string, // 自定义字段名称 默认'state'
fieldNames?: string, // 自定义字段名称 默认'status'
expectTrueValue?: boolean|number|string, //期望为ture(有效)的值 默认1
handleConfirm?(),
handleCancel?()
}
const StatusSwitch:React.FC<StatusSwitchProps> = (props) => {
const { record, fieldNames = 'state', expectTrueValue = 1 } = props
const { record, fieldNames = 'status', expectTrueValue = 1 } = props
return (
<Popconfirm
title="确定要执行这个操作?"
......
......@@ -51,7 +51,7 @@ export interface RenderIconsProps {
export const useTreeActions = (action?): TabTreeActions => {
const actionRef = useRef<any>(null);
actionRef.current = actionRef.current || {};
actionRef.current = actionRef.current || action || createTreeActions();
return actionRef.current;
};
......
......@@ -149,6 +149,9 @@ h6 {
background: none;
}
}
.white-wrapper {
background-color: #fff;
}
//addonAfter 有选择弹窗时
.input_addonAfter{
......
import React, { createContext } from 'react'
export const historyContainer = createContext<any>(null);
......@@ -123,4 +123,13 @@ export default {
'menu.payandSettle': '支付',
'menu.payandSettle.paySetting': '支付方式管理',
'menu.payandSettle.paySetting.payParamsSetting': '会员支付参数配置',
// 权限管理
'menu.systemSetting': '系统',
'menu.systemSetting.authConfig': '平台权限',
'menu.systemSetting.authConfig.organ': '组织机构',
'menu.systemSetting.authConfig.memberSystem': '角色管理',
'menu.systemSetting.authConfig.memberDetail': '角色详情',
'menu.systemSetting.authConfig.userSystem': '用户管理',
'menu.systemSetting.authConfig.userDetail': '用户详情',
};
\ No newline at end of file
.menu-role-tree {
padding: 24px;
border: 1px solid #F4F5F7;
}
\ No newline at end of file
import React, { ReactNode, useRef } from 'react';
import { history, Link } from 'umi'
import { Button, Popconfirm, Card } from 'antd';
import {
PlusCircleOutlined,
PlayCircleOutlined,
PauseCircleOutlined,
PlusOutlined,
EyeOutlined
} from '@ant-design/icons';
import {StandardTable} from 'god'
import {ColumnType} from 'antd/lib/table/interface'
import { IButtonFilter, IFormFilter } from 'god/dist/src/standard-table/TableController';
import { PublicApi } from '@/services/api';
import StatusSwitch from '@/components/StatusSwitch';
import EyePreview from '@/components/EyePreview';
import { STATUS_ENUM } from '@/constants';
import { PageHeaderWrapper } from '@ant-design/pro-layout';
const fetchData = async (params) => {
const { data } = await PublicApi.getMemberRolePage(params)
return data
}
const MemberSystem: React.FC<{}> = () => {
const ref = useRef<any>({})
const deleteItem = async (record) => {
// 删除该项
await PublicApi.postMemberRoleDelete({
memberRoleId: record.id
})
ref.current.reload()
}
const updateItem = (record) => {
history.push(`/memberCenter/systemSetting/authConfig/memberSystem/memberDetail?id=${record.id}&preview=0`)
}
const handleStatus = async (record) => {
await PublicApi.postMemberRoleUpdatestatus({
id: record.id,
status: record.status === 1 ? 0 : 1
})
ref.current.reload()
}
const columns: ColumnType<any>[] = [
{
title: 'ID',
dataIndex: 'id',
align: 'center',
key: 'id',
},
{
title: '角色名称',
dataIndex: 'roleName',
align: 'center',
key: 'roleName',
className: 'commonPickColor',
render: (text, record) => <EyePreview url={`/memberCenter/systemSetting/authConfig/memberSystem/memberDetail?id=${record.id}&preview=1`}>{text}</EyePreview>
},
{
title: '描述',
align: 'center',
dataIndex: 'remark',
key: 'remark',
},
{
title: '状态',
align: 'center',
dataIndex: 'status',
key: 'status',
render: (text: any, record:any) => <StatusSwitch record={record} handleConfirm={() => handleStatus(record)}/>
},
{
title: '操作',
dataIndex: 'option',
align: 'center',
render: (text:any, record:any) => {
return record.status === 0 ? (
<>
<Popconfirm
title="确定要执行这个操作?"
onConfirm={() => deleteItem(record)}
okText="是"
cancelText="否"
>
<Button type='link'>删除</Button>
</Popconfirm>
<Button type='link' onClick={()=>updateItem(record)}>修改</Button>
</>
) : null
}
}
];
return (
<PageHeaderWrapper>
<Card className="common-wrapper">
<StandardTable
columns={columns}
currentRef={ref}
fetchTableData={(params:any) => fetchData(params)}
formilyLayouts={{
justify: 'space-between'
}}
formilyChilds={{
layouts: {
order: 2
},
children: <Button style={{width: 140}} icon={<PlusOutlined/>} onClick={() => history.push('/memberCenter/systemSetting/authConfig/memberSystem/memberDetail')} type='primary'>新建</Button>
}}
formilyProps={{
layouts: {
order: 3
},
ctx: {
effects: ($) => {
$('onFieldInputChange', 'status').subscribe(() => {
ref.current.reload()
})
},
schema: {
type: 'object',
properties: {
roleName: {
type: 'Search',
"x-component-props": {
placeholder: '请输入角色名称'
}
},
status: {
type: 'string',
enum: STATUS_ENUM,
"x-component-props": {
placeholder: '请选择状态'
}
}
}
}
}
}}
/>
</Card>
</PageHeaderWrapper>
)
}
export default MemberSystem
import React, { useContext, useState, useEffect, useRef, useLayoutEffect, useCallback } from 'react';
import { Row, Col, Button, Form, Input, Space, Tabs, message, Badge, Card } from 'antd';
import {IntegrateTree} from 'god'
import { history } from 'umi';
import { historyContainer } from '@/hooks/useHistoryContainer';
import NiceForm from '@/components/NiceForm';
import TabTree, { useTreeActions, createTreeActions } from '@/components/TabTree';
import { PublicApi } from '@/services/api';
import styled from './index.less'
import CheckboxTree from '@/components/CheckBoxTree';
import { useTreeTabs } from '@/hooks/useTreeTabs';
import { createFormActions } from '@formily/antd';
import { usePageStatus, PageStatus } from '@/hooks/usePageStatus';
import DetailPage from '@/components/DetailPage';
import { PageHeaderWrapper } from '@ant-design/pro-layout';
import ReutrnEle from '@/components/ReturnEle';
const pageTitles = [
'新增',
'编辑',
'预览'
]
const TabFormErrors = (props) => {
return (
<Badge dot={props.dot} offset={[5,-5]}>
{props.children}
</Badge>
)
}
const fetchMenuData = async () => {
const res = await PublicApi.getMemberRoleAuthTree()
return res
}
const TabsItem = Tabs.TabPane
const menuActions = createFormActions()
const treeActions = createTreeActions()
const MemberDetail: React.FC<{}> = () => {
const [form] = Form.useForm();
const value = useContext(historyContainer)
const {
treeData,
handleSelect,
nodeRecord,
isEditForm,
setIsEditForm,
getTreeMaps,
} = useTreeTabs({
fetchMenuData,
fetchItemDetailData: ({id}) => PublicApi.getMemberRoleAuthButton({
menuId: id
})
})
const actionRef = useRef<any>({})
const formInitValue = nodeRecord ? getTreeMaps(nodeRecord.key) : {}
const [errors, setErrors] = useState<boolean>(false)
const [formValue, setFormValue] = useState<any>(null)
const {
pageStatus,
id
} = usePageStatus()
// 编辑和预览模式下需回显数据
const fetchRoleMenuDetail = async (id) => {
// 10秒缓存
const res = await PublicApi.getMemberRoleGet({
memberRoleId: id
}, { useCache: true, ttl: 10 * 1000 })
return res
}
// 储存的按钮数据
const [buttonInfos, setButtonInfos] = useState<any>([])
useEffect(() => {
if (!id) return ;
fetchRoleMenuDetail(id).then(res => {
const { data } = res
setFormValue(data)
})
}, [])
useEffect(() => {
if (formInitValue) {
// 显示右侧checkbox
setButtonInfos(formInitValue.buttons || [])
// 回显右侧checkbox的值
if (actionRef.current.setSelected) {
actionRef.current.setSelected()
}
}
}, [getTreeMaps])
const handleSubmit = () => {
menuActions.submit().then(async ({values}) => {
setErrors(false)
// 如果未点击过操作权限tab, 则无法获取到actionRef实例, 需补充手动补充回显的ids, 新增的时候如果未设置按钮,则返回空数组
const buttonCheckIds = actionRef.current.selected || (formValue && formValue.ids) || []
const treeCheckIds = treeActions.getSelectKeys()
const menuIds = [...buttonCheckIds, ...treeCheckIds]
if (pageStatus === PageStatus.EDIT) {
await PublicApi.postMemberRoleUpdate({
...values,
memberRoleId: id,
menuIds
})
} else {
await PublicApi.postMemberRoleAdd({
...values,
menuIds
})
}
history.goBack(-1)
}).catch(err => {
console.log(err)
if (Array.isArray(err)) {
setErrors(true)
}
})
}
const changeTabs = (key) => {
if (key === '2' && id) {
fetchRoleMenuDetail(id).then(res => {
const { data } = res
// 获取菜单id选中的集合
const {
checkIds,
...reset
} = data
treeActions.setSelectKeys(checkIds)
})
}
}
const extraButtons = (
<Space>
<Button type='primary' disabled={pageStatus === PageStatus.PREVIEW} onClick={handleSubmit}>保存</Button>
</Space>
)
return (
<PageHeaderWrapper
onBack={() => history.goBack()}
backIcon={<ReutrnEle description="返回"/>}
className='addRepository'
title={pageTitles[pageStatus]}
extra={[
extraButtons
]}
>
<Card>
<Tabs type='card' className='black-tabs' onChange={changeTabs}>
<TabsItem tab={<TabFormErrors dot={errors}>基本信息</TabFormErrors>} key='1'>
<NiceForm
labelCol={4}
wrapperCol={12}
initialValues={formValue}
labelAlign='left'
actions={menuActions}
editable={pageStatus !== PageStatus.PREVIEW}
previewPlaceholder=' '
schema={{
type: 'object',
properties: {
roleName: {
type: 'string',
title: '角色名称',
required: true
},
remark: {
type: 'textarea',
title: '备注',
"x-rules": [
{
limitByte: true,
maxByte: 120
}
],
"x-component-props": {
rows: 4
}
},
state: {
type: 'number',
title: '状态',
"x-component": 'CustomStatus',
default: 1
}
}
}}
>
</NiceForm>
</TabsItem>
<TabsItem tab='操作权限' key='2'>
<Row justify='space-between'>
<Col span={17} className={styled['menu-role-tree']}>
<TabTree
title='菜单访问权限'
fetchData={params => fetchMenuData()}
checkable
actions={treeActions}
treeData={treeData}
handleSelect={handleSelect}
customKey='id'
disabled={pageStatus === PageStatus.PREVIEW}
/>
</Col>
<Col span={6} className={styled['menu-role-tree']}>
<CheckboxTree
actions={actionRef}
disabled={pageStatus === PageStatus.PREVIEW}
checkedNodes={buttonInfos}
title='菜单接口访问权限'
/>
</Col>
</Row>
</TabsItem>
</Tabs>
</Card>
</PageHeaderWrapper>
)
}
export default MemberDetail
import React, {useState, ReactText, useEffect} from 'react';
import { Row, Col, Tree, Form, Table, InputNumber, Popconfirm, Button, Input, Modal, Card } from 'antd';
import {
CarryOutOutlined,
FormOutlined,
PlusOutlined
} from '@ant-design/icons';
import TabTree, { useTreeActions, createTreeActions } from '@/components/TabTree';
import SchemaForm, { createFormActions, LifeCycleTypes, FormEffectHooks } from '@formily/antd';
import { menuSchema } from './schema';
import { PublicApi } from '@/services/api';
import { omit } from '@/utils';
import { useMap, useBoolean } from '@umijs/hooks';
import { useTreeTabs } from '@/hooks/useTreeTabs';
import { isObject } from '@antv/util';
// import "./index.less"
const { ON_FORM_INPUT_CHANGE } = LifeCycleTypes
const { onFieldInputChange$ } = FormEffectHooks
enum FormState {
FREE, // 空闲状态
EDIT, // 编辑状态
ADD, // 新增状态
}
const formActions = createFormActions()
const treeActions = createTreeActions()
const fetchMenuData = async (params?) => {
const res = await PublicApi.getMemberOrgTree()
return res
}
const Organ: React.FC<{}> = () => {
const {
treeStatus,
setTreeStatus,
treeData,
setIsEditForm,
nodeRecord,
setNodeRecord,
handleSelect,
getTreeMaps,
setTreeMaps,
resetMenu,
toolsRender,
handleDeleteMenu
} = useTreeTabs({
treeActions,
formActions,
deleteMenu: PublicApi.postMemberOrgDelete,
fetchMenuData: fetchMenuData,
fetchItemDetailData: PublicApi.getMemberOrgGet
})
// 当拥有节点数据并且当前状态是编辑状态时 需回显表单
const formInitValue = (nodeRecord && treeStatus === FormState.EDIT) ? getTreeMaps(nodeRecord.key) : {}
const handleSubmitAllSetting = () => {
formActions.submit()
}
// 保存设置提交
const handleSubmit = (value) => {
// 去掉模拟的key, 为true的时候是编辑
const editOrAdd = nodeRecord && treeStatus === FormState.EDIT
const params = editOrAdd ? { ...value, parentId: nodeRecord.id } : {
...value,
parentId: nodeRecord ? nodeRecord.parentId : 0,
}
const fn = editOrAdd ? PublicApi.postMemberOrgUpdate : PublicApi.postMemberOrgAdd
fn(params).then(res => {
resetMenu()
setTreeStatus(FormState.FREE)
setNodeRecord(undefined)
// 保存后要将是否填写过表单设为false
setIsEditForm(false)
})
}
return <Card className="common-wrapper white-wrapper">
<Row gutter={[36, 36]}>
<Col span={8}>
<h3 className="commonPanelTitle mb-30">选择要编辑的项目</h3>
{
treeData && treeData.length > 0
? <TabTree
fetchData = {params => fetchMenuData(params)}
treeData={treeData}
toolsRender={toolsRender}
actions={treeActions}
customKey='id'
handleSelect={(key, node) => handleSelect(key, node)}
/>
:
<Button block type='primary' onClick={() => handleSelect()}>暂无菜单, 开始新增</Button>
}
</Col>
<Col span={16}>
{treeStatus !== FormState.FREE && <>
<h3 className="commonPanelTitle mb-30">{treeStatus === FormState.ADD ? '新增' : '编辑'}</h3>
<SchemaForm
schema={menuSchema}
value={formInitValue}
actions={formActions}
effects={($) => {
$(ON_FORM_INPUT_CHANGE).subscribe(() => {
setIsEditForm(true)
})
$('onFormReset').subscribe(() => {
console.log('mount')
})
}}
onSubmit={handleSubmit}
>
</SchemaForm>
<Button onClick={handleSubmitAllSetting} type="primary" style={{ marginTop: 32, marginBottom: 16, marginRight: 24}}>
保存设置
</Button>
<Popconfirm title="确定要删除吗?" okText="是" cancelText="否" onConfirm={handleDeleteMenu}>
<Button style={{ marginTop: 32, marginBottom: 16}}>
删除菜单
</Button>
</Popconfirm>
</>
}
</Col>
</Row>
</Card>
}
export default Organ
import { ISchema } from '@formily/antd';
export const menuSchema: ISchema = {
type: 'object',
properties: {
MEGA_LAYOUT: {
type: 'object',
"x-component": 'mega-layout',
"x-component-props": {
labelAlign: 'top',
wrapperCol: 12
},
properties: {
code: {
type: 'string',
title: '组织代码',
required: true,
},
title: {
type: 'string',
title: '组织机构',
required: true
},
remark: {
type: 'textarea',
title: '描述',
"x-component-props": {
rows: 4
}
}
}
}
}
}
\ No newline at end of file
.user-system {
.ant-input-group-addon {
padding: 0;
border: none;
}
}
import React, { ReactNode, useRef } from 'react';
import { history, Link } from 'umi'
import { Button, Popconfirm, Card } from 'antd';
import {
PlusCircleOutlined,
PlayCircleOutlined,
PauseCircleOutlined,
PlusOutlined,
EyeOutlined
} from '@ant-design/icons';
import {StandardTable} from 'god'
import {ColumnType} from 'antd/lib/table/interface'
import { IButtonFilter, IFormFilter } from 'god/dist/src/standard-table/TableController';
import { PublicApi } from '@/services/api';
import EyePreview from '@/components/EyePreview';
import StatusSwitch from '@/components/StatusSwitch';
import { STATUS_ENUM } from '@/constants';
import { PageHeaderWrapper } from '@ant-design/pro-layout';
// 模拟请求
const fetchData = async (params) => {
const { data } = await PublicApi.getMemberUserPage(params)
return data
}
const UserSystem: React.FC<{}> = () => {
const ref = useRef<any>({})
const addItem = () => {
history.push('/memberCenter/systemSetting/authConfig/userSystem/userDetail')
}
const deleteItem = (record) => {
// 删除该项
PublicApi.postMemberUserDelete({
userId: record.userId
}).then(() => {
ref.current.reload()
})
}
const updateItem = (record) => {
history.push(`/memberCenter/systemSetting/authConfig/userSystem/userDetail?id=${record.userId}&preview=0`)
}
const handleStatus = (record) => {
PublicApi.postMemberUserUpdatestatus({
userId: record.userId,
status: record.status === 1 ? 0 : 1
}).then(res => {
ref.current.reload()
})
}
const columns: ColumnType<any>[] = [
{
title: '账号',
dataIndex: 'account',
align: 'center',
key: 'account',
className: 'commonPickColor',
render: (text, record) => <EyePreview url={`/memberCenter/systemSetting/authConfig/userSystem/userDetail?id=${record.userId}&preview=1`}>{text}</EyePreview>
},
{
title: '用户姓名',
dataIndex: 'name',
align: 'center',
key: 'name',
},
{
title: '所属机构',
dataIndex: 'orgName',
align: 'center',
key: 'orgName'
},
{
title: '绑定手机号码',
align: 'center',
dataIndex: 'phone',
key: 'phone',
},
{
title: '所属角色',
align: 'center',
dataIndex: 'roleName',
key: 'roleName',
},
{
title: '最后登录时间',
align: 'center',
dataIndex: 'lastLoginTime',
key: 'lastLoginTime',
},
{
title: '状态',
align: 'center',
dataIndex: 'status',
key: 'status',
render: (text: any, record:any) => <StatusSwitch handleConfirm={() => handleStatus(record)} record={record}/>
},
{
title: '操作',
dataIndex: 'option',
align: 'center',
render: (text:any, record:any) => {
return record.status === 0 && (
<>
<Popconfirm
title="确定要执行这个操作?"
onConfirm={() => deleteItem(record)}
okText="是"
cancelText="否"
>
<Button type='link'>删除</Button>
</Popconfirm>
<Button type='link' onClick={()=>updateItem(record)}>修改</Button>
</>
)
}
}
];
return (
<PageHeaderWrapper>
<Card className="common-wrapper">
<StandardTable
columns={columns}
currentRef={ref}
fetchTableData={(params:any) => fetchData(params)}
formilyLayouts={{
justify: 'space-between'
}}
formilyChilds={{
layouts: {
order: 2
},
children: <Button style={{width: 140}} icon={<PlusOutlined/>} onClick={addItem} type='primary'>新建</Button>
}}
formilyProps={{
layouts: {
order: 3
},
ctx: {
effects: ($) => {
$('onFieldInputChange', 'status').subscribe(() => {
ref.current.reload()
})
},
schema: {
type: 'object',
properties: {
account: {
type: 'Search',
"x-component-props": {
placeholder: '请输入账号'
}
},
name: {
type: 'Search',
"x-component-props": {
placeholder: '请输入用户姓名'
}
},
status: {
type: 'string',
enum: STATUS_ENUM,
"x-component-props": {
placeholder: '请选择状态'
}
}
}
}
}
}}
/>
</Card>
</PageHeaderWrapper>
)
}
export default UserSystem
import { ISchema } from '@formily/antd';
import { PATTERN_MAPS } from '@/constants/regExp';
export const UserDetailSchema:ISchema = {
type: 'object',
properties: {
MEGA_LAYOUT: {
type: 'object',
"x-component": 'mega-layout',
"x-component-props": {
labelCol: 6,
labelAlign: 'left',
full: true,
wrapperCol: 12
},
properties: {
account: {
type: 'string',
title: '登录账号',
"x-rules": [
{
pattern: /^\w{6,20}$/,
message: '请输入由数字字母或者下划线组成的6-20位账号'
}
],
required: true
},
password: {
type: 'password',
title: '登录密码',
"x-rules": [
{
pattern: PATTERN_MAPS.password,
message: '请输入由大小写字母和数字组成的8位密码'
}
],
required: true
},
name: {
type: 'string',
title: '姓名',
maxLength: 16,
required: true
},
phoneLayout: {
type: 'object',
"x-component": 'flex-box',
title: '手机号',
"x-component-props": {
labelcol: 6,
wrappercol: 12
},
required: true,
properties: {
countryCode: {
required: true,
type: 'string',
enum: ['+86'],
"x-mega-props": {
wrapperCol: 24
},
"x-component-props": {
flexcol: {
span: 6
}
}
},
phone: {
type: 'number',
required: true,
"x-mega-props": {
wrapperCol: 24,
full: true
},
"x-rules": [
{
pattern: PATTERN_MAPS.phone,
message: '请输入正确的手机号'
}
],
"x-component-props": {
flexcol: {
flex: 1
}
}
}
}
},
idCardNo: {
type: 'string',
title: '身份证号'
},
email: {
type: 'string',
title: '邮箱',
"x-rules": [
{
pattern: PATTERN_MAPS.email,
message: '请输入正确的邮箱'
}
]
},
jobTitle: {
type: 'string',
title: '职位',
maxLength: 20,
},
orgName: {
type: 'string',
title: '所属组织机构',
required: true,
'x-component-props': {
disabled: true,
addonAfter: "{{connectCategory}}"
},
},
orgId: {
type: 'string',
visible: false
},
memberRoleIds: {
required: true,
type: 'array:string',
"x-component": 'tableTagList',
"x-component-props": {
extra: "{{addRoles}}",
callback: "{{callback}}"
},
title: '关联角色'
},
}
}
}
}
\ No newline at end of file
import React, { useRef, useState, useEffect } from 'react';
import { Button, Modal, Card } from 'antd';
import { createFormActions, FormButtonGroup } from '@formily/antd'
import {LinkOutlined } from '@ant-design/icons';
import {ColumnType} from 'antd/lib/table/interface';
import { PublicApi } from '@/services/api';
import { history } from 'umi';
import { usePageStatus, PageStatus } from '@/hooks/usePageStatus';
import DetailPage from '@/components/DetailPage';
import NiceForm from '@/components/NiceForm';
import { UserDetailSchema } from './schema';
import './index.less'
import ModalTable from '@/components/ModalTable';
import { useRowSelectionTable } from '@/hooks/useRowSelectionTable';
import { findItemAndDelete, omit, getParentTreeTitles } from '@/utils';
import TabTree, { useTreeActions, createTreeActions } from '@/components/TabTree';
import { useTreeTabs } from '@/hooks/useTreeTabs';
import { useHttpRequest } from '@/hooks/useHttpRequest';
import { PageHeaderWrapper } from '@ant-design/pro-layout';
import ReutrnEle from '@/components/ReturnEle';
const titleRender = (title) => {
if (title === PageStatus.PREVIEW) return '查看用户'
if (title === PageStatus.ADD) return '新增用户'
if (title === PageStatus.EDIT) return '编辑用户'
return ''
}
const fetchOriginTreeData = async (params?) => { // 平台后台树
const res = await PublicApi.getMemberOrgTree({}, { ttl: 10, useCache: true })
return res
}
const userActions = createFormActions()
const originTreeActions = createTreeActions()
const AddUser: React.FC<{}> = () => {
const [originVisible, setOriginVisible] = useState(false)
const [roleVisible, setRoleVisible] = useState(false)
const ref = useRef<any>({})
const [formData, setFormData] = useState<any>(null)
const { id, pageStatus } = usePageStatus()
const [roleSelection, roleSelectCtl] = useRowSelectionTable()
const [originSelectNode, setOriginSelectNode] = useState<any>()
const { data, loading, err, run } = useHttpRequest(id ? PublicApi.postMemberUserUpdate : PublicApi.postMemberUserAdd)
const {
treeData: originTreeData,
} = useTreeTabs({
fetchMenuData: fetchOriginTreeData,
})
useEffect(() => {
if (id) {
PublicApi.getMemberUserGet({
userId: id
}).then(async res => {
const { data } = res
fetchOriginTreeData().then(({data: dataSource}) => {
setFormData({...data, orgName: getParentTreeTitles(dataSource, data.orgId), memberRoleIds: data.memberRoleIds.map((v,i) => {
return {
id: v,
roleName: data.memberRoleNames[i]
}
})})
})
})
}
}, [])
const onFinish = async (values:any) => {
values.memberRoleIds = values.memberRoleIds.map(v => v.id)
const omitValue = omit(values, ['orgName'])
const params = id ? {
...omitValue,
userId: Number(id)
} : omitValue
await run(params)
setTimeout(() => {
history.goBack(-1)
}, 300)
};
// 角色确认弹窗
const roleConfirm = () => {
setRoleVisible(false)
userActions.setFieldValue('memberRoleIds', roleSelectCtl.selectRow)
}
const handleSelectCancel = () => {
setRoleVisible(false)
}
const handleRoleBtn = () => {
setRoleVisible(true)
const selectRoles = userActions.getFieldValue('memberRoleIds')
roleSelectCtl.setSelectRow(selectRoles)
roleSelectCtl.setSelectedRowKeys(selectRoles.map(v => v.id))
}
// 模拟请求
const fetchUserList = async (params:any) => {
const data = await PublicApi.getMemberRolePage(params)
return data.data
}
const columns: ColumnType<any>[] = [
{
title: '角色ID',
dataIndex: 'id',
align: 'center',
key: 'id',
},
{
title: '角色名称',
dataIndex: 'roleName',
align: 'center',
key: 'roleName',
},
{
title: '描述',
align: 'center',
dataIndex: 'remark',
key: 'remark',
ellipsis: true
}
]
const handleOrigin = () => {
setOriginVisible(false)
if(originSelectNode?.id){
userActions.setFieldValue('orgName', originTreeActions.getParentPath(originSelectNode.id))
userActions.setFieldValue('orgId', originSelectNode.id)
}
}
const handleAddRole = () => {
setRoleVisible(true);
}
const handlePlateformSelect = (key, node) => {
setOriginSelectNode({id: key*1, name: node._title})
}
const openOriginTree = () => {
setOriginVisible(true)
}
const connectCategory = pageStatus !== PageStatus.PREVIEW ?
<div className='connectBtn' onClick={openOriginTree}><LinkOutlined style={{marginRight: 4}}/>关联</div>
:
''
const addRoles = pageStatus !== PageStatus.PREVIEW ? <Button block onClick={handleRoleBtn}>添加角色</Button> : ''
return (
<PageHeaderWrapper
onBack={() => history.goBack()}
backIcon={<ReutrnEle description="返回"/>}
className='addRepository'
title={titleRender(pageStatus)}
>
<Card>
<div className="common-wrapper user-system useConnectBtnWrapper">
<NiceForm
onSubmit={onFinish}
schema={UserDetailSchema}
initialValues={formData}
actions={userActions}
editable={pageStatus !== PageStatus.PREVIEW}
effects={($, {setFieldState}) => {
$('onFormInit').subscribe(() => {
if (id) {
setFieldState('password', state => {
state.visible = false
})
}
})
}}
expressionScope={{
connectCategory,
addRoles,
callback: (id) => {
roleSelectCtl.setSelectRow(findItemAndDelete(roleSelectCtl.selectRow, id))
roleSelectCtl.setSelectedRowKeys(findItemAndDelete(roleSelectCtl.selectedRowKeys, id))
}
}}
>
<FormButtonGroup offset={6}>
<Button htmlType='submit' type='primary' hidden={pageStatus === PageStatus.PREVIEW} loading={loading}>提交</Button>
<Button onClick={() => history.goBack()}>取消</Button>
</FormButtonGroup>
</NiceForm>
<ModalTable
modalTitle='选择角色'
visible={roleVisible}
confirm={roleConfirm}
cancel={handleSelectCancel}
columns={columns}
rowSelection={roleSelection}
currentRef={ref}
fetchTableData={(params:any) => fetchUserList(params)}
tableProps={{ rowKey: 'id' }}
formilyProps={{
layouts: {
order: 3
},
ctx: {
schema: {
type: 'object',
properties: {
roleName: {
type: 'Search',
"x-component-props": {
placeholder: '请输入角色名称'
}
}
}
}
}
}
}
/>
<Modal
title="选择组织机构"
visible={originVisible}
onOk={handleOrigin}
onCancel={() => setOriginVisible(false)}
okText="确认"
cancelText="取消"
getContainer='#root'
// destroyOnClose={true}
>
<TabTree
fetchData = {params => fetchOriginTreeData(params)}
treeData={originTreeData}
handleSelect={(key, node) => handlePlateformSelect(key, node)}
actions={originTreeActions}
customKey="id"
/>
</Modal>
</div>
</Card>
</PageHeaderWrapper>
);
};
export default AddUser
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment