角色权限模块、未读提醒、接口请求token拼接、项目样式修改
| | |
| | | { |
| | | path: '/setting', |
| | | // layout: false, |
| | | name:'系统设置', |
| | | name: '系统设置', |
| | | routes: [ |
| | | { |
| | | name: '职位管理', |
| | | path: '/setting/career', |
| | | component: './setting/career/index', |
| | | }, |
| | | { |
| | | name: '角色管理', |
| | | path: '/setting/role', |
| | | component: './setting/role', |
| | | }, |
| | | ], |
| | | }, |
| | | { |
| | | path: '/work-order', |
| | | // layout: false, |
| | | name:'工单事项管理', |
| | | name: '工单事项管理', |
| | | routes: [ |
| | | { |
| | | name: '工单事项配置', |
| | |
| | | { |
| | | path: '/message-notification', |
| | | // layout: false, |
| | | name:'消息通知', |
| | | name: '消息通知', |
| | | routes: [ |
| | | { |
| | | name: '消息通知', |
| | |
| | | import { errorConfig } from './requestErrorConfig'; |
| | | const loginPath = '/login'; |
| | | import '../public/font.css' |
| | | import { useEffect, useState } from 'react'; |
| | | |
| | | |
| | | /** |
| | |
| | | |
| | | // ProLayout 支持的api https://procomponents.ant.design/components/layout |
| | | export const layout: RunTimeLayoutConfig = ({ initialState, setInitialState }) => { |
| | | const [title, setTitle] = useState(''); |
| | | |
| | | useEffect(() => { |
| | | const updateTitle = () => { |
| | | const now = new Date(); |
| | | const days = ['周日', '周一', '周二', '周三', '周四', '周五', '周六']; |
| | | const formattedDate = `${now.getFullYear()}-${now.getMonth() + 1}-${now.getDate()} ${now.getHours()}:${now.getMinutes()} ${days[now.getDay()]}`; |
| | | setTitle(formattedDate); |
| | | }; |
| | | updateTitle(); |
| | | const interval = setInterval(updateTitle, 1000); // 每分钟更新一次 |
| | | return () => clearInterval(interval); // 清除定时器 |
| | | }, []); |
| | | return { |
| | | title, |
| | | logo: false, |
| | | avatarProps: { |
| | | title: <AvatarName />, |
| | |
| | | import { outLogin } from './service'; |
| | | import { outLogin, getUnreadCount } from './service'; |
| | | import { LogoutOutlined } from '@ant-design/icons'; |
| | | import { useModel } from '@umijs/max'; |
| | | import React from 'react'; |
| | | import React, { useEffect, useState } from 'react'; |
| | | import { Access, history, useAccess } from 'umi'; |
| | | import { flushSync } from 'react-dom'; |
| | | import './style.less'; |
| | | |
| | |
| | | |
| | | const { initialState, setInitialState } = useModel('@@initialState'); |
| | | |
| | | const [unreadCount, setUnreadCount] = useState(0); |
| | | useEffect(() => { |
| | | const timer = setInterval(() => { |
| | | getUnreadCount().then((res: any) => { |
| | | setUnreadCount(res.data || 0); |
| | | }); |
| | | }, 1000 * 5) |
| | | |
| | | return () => clearInterval(timer); |
| | | }, []); |
| | | |
| | | |
| | | const onMenuClick = async () => { |
| | | await outLogin(); |
| | |
| | | } |
| | | |
| | | return <div style={{ display: 'flex', alignItems: 'center', color: '#000' }}> |
| | | <div className='unread' > |
| | | <div>未读提醒</div> |
| | | <div style={{ border: '1px solid red', borderRadius: '50%', width: '18px', lineHeight: '18px', marginLeft: '5px', textAlign: 'center', color: 'red' }}>1</div> |
| | | </div> |
| | | { |
| | | unreadCount > 0 && |
| | | <div className='unread' onClick={() => { history.push('/message-notification/list') }}> |
| | | <div>未读提醒</div> |
| | | <div style={{ border: '1px solid red', borderRadius: '50%', width: '18px', lineHeight: '16px', marginLeft: '5px', textAlign: 'center', color: 'red', flexShrink: 0 }}>{unreadCount}</div> |
| | | </div> |
| | | } |
| | | <div className="logoOut" onClick={onMenuClick}>退出登录<LogoutOutlined style={{ marginLeft: '5px' }} /></div> |
| | | </div> |
| | | }; |
| | |
| | | import { request } from '@umijs/max'; |
| | | |
| | | // 退出登录 |
| | | export async function outLogin(data) { |
| | | return request('/api/huacheng-sangeshenbian/systemUser/logout', { |
| | | method: 'POST', |
| | | data, |
| | | }); |
| | | } |
| | | |
| | | // 获取未读数量 |
| | | export async function getUnreadCount(data) { |
| | | return request('/api/huacheng-sangeshenbian/messageNotification/unreadCount', { |
| | | method: 'GET', |
| | | data, |
| | | }); |
| | | } |
| | |
| | | } |
| | | } |
| | | } |
| | | |
| | | .ant-pro-global-header-header-actions-avatar>div:hover { |
| | | background: #fff; |
| | | } |
| | | |
| | | :where(.css-dev-only-do-not-override-17md30i).ant-menu-light .ant-menu-submenu-selected>.ant-menu-submenu-title, |
| | | :where(.css-dev-only-do-not-override-17md30i).ant-menu-light>.ant-menu .ant-menu-submenu-selected>.ant-menu-submenu-title{ |
| | | color: rgba(0, 0, 0, 0.95); |
| | | } |
New file |
| | |
| | | import { Form, Input, Modal, Tree, Button, Spin } from 'antd'; |
| | | import { forwardRef, useImperativeHandle, useState } from 'react'; |
| | | import { useEffect } from 'react'; |
| | | import { getTree, getAddTree } from '../service'; |
| | | const formItemLayout = { |
| | | labelCol: { span: 6 }, |
| | | wrapperCol: { span: 18 }, |
| | | }; |
| | | |
| | | |
| | | const AddAndEdit = ({ visible, onSave, onUpdate, onCancel }, ref) => { |
| | | const [form] = Form.useForm(); |
| | | const [data, setData] = useState({}) |
| | | const [treeData, setTreeData] = useState([]);//权限树 |
| | | const [treeSeletKeys, setTreeSeletKeys] = useState([]); //勾选权限 |
| | | const [detailType, setDetailType] = useState(false);//是否详情 |
| | | const [spinning, setSpinning] = useState(false); |
| | | |
| | | useEffect(() => { |
| | | // 获取权限树 |
| | | getAddTree().then(res => { |
| | | setTreeData(res.data) |
| | | }) |
| | | }, []) |
| | | |
| | | useImperativeHandle(ref, () => { |
| | | return { |
| | | refreshData: (data, type) => { |
| | | setDetailType(type || false) |
| | | if (data.id) { |
| | | // 获取角色的权限树 |
| | | getTree(data.id).then(res => { |
| | | setTreeSeletKeys(res.data.systemMenuIds || []); |
| | | }) |
| | | } |
| | | setData(data) |
| | | form.setFieldsValue(data); |
| | | }, |
| | | clean: () => { |
| | | setSpinning(false) |
| | | }, |
| | | }; |
| | | }); |
| | | |
| | | // 保存 |
| | | const okHandle = () => { |
| | | form.validateFields().then((values) => { |
| | | setSpinning(true) |
| | | values.menuIds = treeSeletKeys |
| | | delete values.tree |
| | | if (data.id) { |
| | | values.id = data.id |
| | | onUpdate(values) |
| | | } else { |
| | | onSave(values); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | // 选中树 |
| | | const onCheck = (e, row) => { |
| | | let seletKeys = e.checked; |
| | | treeData.find(item => { |
| | | if (item.children) { |
| | | let arr1 = item.children.find((item1) => {//循环子路由 |
| | | if (item1.children) { |
| | | let arr2 = item1.children.find((item2) => {//循环子路由下按钮 |
| | | return item2.id == row.node.id; |
| | | }); |
| | | if (row.checked) { |
| | | if (arr2 && !seletKeys.includes(item1.id)) { |
| | | seletKeys.push(item1.id); |
| | | } |
| | | if (arr2 && !seletKeys.includes(item.id)) { |
| | | seletKeys.push(item.id); |
| | | } |
| | | } else { |
| | | let isCheck = item1.children.find((item2) => seletKeys.includes(item2.id)); |
| | | if (!isCheck) { |
| | | let arr = seletKeys.filter((it) => it != item1.id); |
| | | seletKeys = arr; |
| | | } |
| | | } |
| | | } |
| | | return item1.id == row.node.id; |
| | | }); |
| | | if (row.checked) { |
| | | if (arr1 && !seletKeys.includes(item.id)) { |
| | | seletKeys.push(item.id); |
| | | } |
| | | } else { |
| | | let isCheck = item.children.find((item1) => seletKeys.includes(item1.id)); |
| | | if (!isCheck) { |
| | | let arr = seletKeys.filter((it) => it != item.id); |
| | | seletKeys = arr; |
| | | } |
| | | } |
| | | } |
| | | return item.id == row.node.id; |
| | | }); |
| | | if (row.node.children.length != 0) { |
| | | row.node.children.map((item) => { |
| | | if (row.checked) { |
| | | if (!seletKeys.includes(item.id)) { |
| | | seletKeys.push(item.id); |
| | | } |
| | | } else { |
| | | seletKeys = seletKeys.filter((item1) => item1 != item.id); |
| | | } |
| | | if (item.children.length != 0) { |
| | | item.children.map((item1) => { |
| | | if (row.checked) { |
| | | if (!seletKeys.includes(item1.id)) { |
| | | seletKeys.push(item1.id); |
| | | } |
| | | } else { |
| | | seletKeys = seletKeys.filter((item2) => item2 != item1.id); |
| | | } |
| | | }); |
| | | } |
| | | }); |
| | | } |
| | | setTreeSeletKeys(seletKeys); |
| | | }; |
| | | |
| | | return ( |
| | | <Modal |
| | | getContainer={false} |
| | | width="20%" |
| | | destroyOnClose |
| | | title={detailType ? '角色详情' : data.id ? '编辑角色' : '添加角色'} |
| | | open={visible} |
| | | onCancel={() => onCancel(false)} |
| | | afterClose={() => { |
| | | form.resetFields() |
| | | setTreeSeletKeys([]) |
| | | }} |
| | | footer={ |
| | | !detailType ? |
| | | [ |
| | | <Button key="back" onClick={() => onCancel(false)}>取消</Button>, |
| | | <Button key="submit" type="primary" onClick={okHandle}> |
| | | 确定 |
| | | </Button> |
| | | ] |
| | | : |
| | | <Button key="back" onClick={() => onCancel(false)}>关闭</Button> |
| | | } |
| | | > |
| | | <Form layout="horizontal" {...formItemLayout} form={form} scrollToFirstError> |
| | | <Form.Item |
| | | name="name" |
| | | label="角色名称" |
| | | rules={[{ required: true, message: '请输入角色名称' }]} |
| | | > |
| | | <Input disabled={detailType} placeholder='请输入角色名称' /> |
| | | </Form.Item> |
| | | <Form.Item |
| | | name="tree" |
| | | label="角色权限" |
| | | rules={[{ |
| | | required: true, |
| | | validator: (rule, value) => { |
| | | return new Promise((resolve, reject) => { |
| | | if (value) { |
| | | resolve(''); |
| | | } else { |
| | | if (treeSeletKeys.length === 0) { |
| | | reject(new Error('请选择角色权限')); |
| | | } |
| | | resolve(''); |
| | | } |
| | | }) |
| | | } |
| | | }]}> |
| | | <Tree |
| | | checkable |
| | | checkStrictly |
| | | checkedKeys={treeSeletKeys} |
| | | treeData={treeData} |
| | | disabled={detailType} |
| | | fieldNames={{ title: 'name', key: 'id' }} |
| | | onCheck={onCheck} |
| | | /> |
| | | </Form.Item> |
| | | </Form> |
| | | <Spin spinning={spinning} fullscreen /> |
| | | </Modal > |
| | | ); |
| | | }; |
| | | |
| | | export default forwardRef(AddAndEdit); |
New file |
| | |
| | | import { PageContainer, ProTable } from '@ant-design/pro-components'; |
| | | import { buildProTableDataSource, sendRequest, showDelConfirm } from '@/utils/antdUtils'; |
| | | import { Button, Space } from 'antd'; |
| | | import { useRef, useState } from 'react'; |
| | | import { Access, history, useAccess } from 'umi'; |
| | | import { getList, del, edit, add } from './service' |
| | | import AddAndEdit from './components/addAndEdit' |
| | | const Role = () => { |
| | | |
| | | const actionRef = useRef(); |
| | | const addViewRef = useRef(); |
| | | const [modalVisible, handleModalVisibles] = useState(false); |
| | | const access = useAccess(); |
| | | |
| | | const columns = [ |
| | | { |
| | | title: '角色名称', |
| | | dataIndex: 'name', |
| | | }, |
| | | { |
| | | title: '操作', |
| | | hideInSearch: true, |
| | | render: (text, record) => { |
| | | return ( |
| | | <Space> |
| | | { |
| | | record.roleId != 1 && |
| | | // <Access accessible={access.settings_role_edit}> |
| | | <Button |
| | | type="link" |
| | | onClick={() => { |
| | | addViewRef.current.refreshData(record); |
| | | handleModalVisibles(true) |
| | | }} |
| | | > |
| | | 编辑 |
| | | </Button> |
| | | // </Access> |
| | | } |
| | | { |
| | | record.roleId != 1 && |
| | | // <Access accessible={access.settings_role_detele}> |
| | | <Button |
| | | type="link" |
| | | onClick={() => { |
| | | |
| | | showDelConfirm(async () => { |
| | | let status = await sendRequest(del, record.id) |
| | | if (status) { |
| | | actionRef.current.reload(); |
| | | } |
| | | }, '确认删除所选信息吗?'); |
| | | }} |
| | | > |
| | | 删除 |
| | | </Button> |
| | | // </Access> |
| | | } |
| | | {/* <Access accessible={access.settings_role_detail}> */} |
| | | <Button |
| | | type="link" |
| | | onClick={() => { |
| | | addViewRef.current.refreshData(record, true); |
| | | handleModalVisibles(true) |
| | | }} |
| | | > |
| | | 查看详情 |
| | | </Button> |
| | | {/* </Access> */} |
| | | </Space > |
| | | ); |
| | | }, |
| | | }, |
| | | ] |
| | | |
| | | return <PageContainer title='角色管理'> |
| | | <ProTable |
| | | rowKey='id' |
| | | actionRef={actionRef} |
| | | columns={columns} |
| | | pagination={{ |
| | | showSizeChanger: true, |
| | | showQuickJumper: true, |
| | | defaultPageSize: 10, |
| | | }} |
| | | request={(params) => buildProTableDataSource(getList, params)} |
| | | toolBarRender={(action, selectRows) => [ |
| | | // <Access accessible={access.settings_role_add}> |
| | | <Space> |
| | | <Button |
| | | type="primary" |
| | | onClick={() => { |
| | | addViewRef.current.refreshData({}); |
| | | handleModalVisibles(true) |
| | | }} |
| | | > |
| | | 添加 |
| | | </Button> |
| | | </Space> |
| | | // </Access> |
| | | ]} |
| | | /> |
| | | <AddAndEdit |
| | | ref={addViewRef} |
| | | visible={modalVisible} |
| | | onSave={async (fileds) => { |
| | | let success = await sendRequest(add, fileds); |
| | | if (success) { |
| | | handleModalVisibles(false); |
| | | actionRef.current.reload(); |
| | | } |
| | | addViewRef.current.clean(); |
| | | }} |
| | | onUpdate={async (fileds) => { |
| | | let success = await sendRequest(edit, fileds); |
| | | if (success) { |
| | | handleModalVisibles(false); |
| | | actionRef.current.reload(); |
| | | } |
| | | addViewRef.current.clean(); |
| | | }} |
| | | onCancel={() => handleModalVisibles(false)} |
| | | /> |
| | | </PageContainer> |
| | | }; |
| | | |
| | | export default Role; |
New file |
| | |
| | | import { request } from '@umijs/max'; |
| | | |
| | | // 列表 |
| | | export const getList = async (params) => { |
| | | return request(`/api/huacheng-sangeshenbian/systemRole/list`, { |
| | | method: 'GET', |
| | | params |
| | | }); |
| | | } |
| | | |
| | | // 编辑获取角色权限树 |
| | | export const getTree = async (id) => { |
| | | return request(`/api/huacheng-sangeshenbian/systemRole/getSystemRoleInfo/${id}`, { |
| | | method: 'GET', |
| | | }); |
| | | } |
| | | |
| | | // 新增获取权限树 |
| | | export const getAddTree = async (id) => { |
| | | return request(`/api/huacheng-sangeshenbian/systemMenu/getSystemMenuList`, { |
| | | method: 'GET', |
| | | }); |
| | | } |
| | | |
| | | // 新增 |
| | | export const add = async (data) => { |
| | | return request('/api/huacheng-sangeshenbian/systemRole/add', { |
| | | method: 'POST', |
| | | data, |
| | | }); |
| | | } |
| | | |
| | | // 编辑 |
| | | export const edit = async (data) => { |
| | | return request('/api/huacheng-sangeshenbian/systemRole/edit', { |
| | | method: 'POST', |
| | | data, |
| | | }); |
| | | } |
| | | |
| | | // 删除 |
| | | export const del = async (id) => { |
| | | return request(`/api/huacheng-sangeshenbian/systemRole/delete/${id}`, { |
| | | method: 'DELETE', |
| | | }); |
| | | } |
| | | |
| | |
| | | |
| | | o.headers = { |
| | | ...config.headers, |
| | | Authorization, |
| | | Authorization:'Bearer ' + Authorization, |
| | | } |
| | | // } |
| | | // 拦截请求配置,进行个性化处理。 |
| | |
| | | } |
| | | |
| | | export async function buildProTableDataSource(fun, params) { |
| | | params.pageCurr = params.current; |
| | | params.pageNum = params.current; |
| | | delete params.current |
| | | const response = await fun(params); |
| | | const data = Promise.resolve({ |
| | | data: response.data.list || [], |
| | | data: response.data.records || [], |
| | | total: response.data.total || 0, |
| | | success: true, |
| | | pageSize: response.data.size || 10, |