From 218602870aa19c3bf170a3023858310b71a983dd Mon Sep 17 00:00:00 2001 From: mmc <1556375442@qq.com> Date: Fri, 24 Apr 2026 15:28:16 +0800 Subject: [PATCH] =?UTF-8?q?=E7=BB=84=E7=BB=87=E6=9E=B6=E6=9E=84=E4=BC=98?= =?UTF-8?q?=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Layout.tsx | 21 +- src/pages/MenuList.tsx | 368 +++++++++++++++++------------------ src/pages/Organization.tsx | 159 ++++++++++++++++ src/pages/Roles.tsx | 301 ++++++++++++++++++++--------- src/pages/StationManage.tsx | 290 ++++++++++++++++++++++++++++ src/pages/Users.tsx | 371 +++++++++++++++++++++++++++--------- src/router/index.tsx | 14 ++ 7 files changed, 1158 insertions(+), 366 deletions(-) create mode 100644 src/pages/Organization.tsx create mode 100644 src/pages/StationManage.tsx diff --git a/src/components/Layout.tsx b/src/components/Layout.tsx index 0d766bc..6e1370f 100644 --- a/src/components/Layout.tsx +++ b/src/components/Layout.tsx @@ -36,6 +36,7 @@ const { Header, Sider, Content } = Layout; export default function AppLayout() { const [collapsed, setCollapsed] = useState(false); const [time, setTime] = useState(new Date().toLocaleString()); + const [menuList, setMenuList] = useState([]); const navigate = useNavigate(); const location = useLocation(); const { userInfo } = useSelector((state: RootState) => state.user); @@ -43,7 +44,7 @@ export default function AppLayout() { const getMenu = () => { getMenuList().then(res => { if (res.code == 200) { - message.success("获取菜单成功") + setMenuList(res.data); } else { message.error("获取菜单失败:" + res.msg) } @@ -74,14 +75,28 @@ export default function AppLayout() { { key: '/video', icon: , label: '在线视频', roles: ['admin', 'operator', 'viewer'] }, { key: '/alerts', icon: , label: '警报中心', roles: ['admin', 'operator'] }, { key: '/downloads', icon: , label: '视频下载', roles: ['admin', 'operator'] }, - { key: '/users', icon: , label: '用户管理', roles: ['admin'] }, - { key: '/roles', icon: , label: '角色权限', roles: ['admin'] }, { key: '/serialNumGenerate', icon: , label: '序列号生成', roles: ['admin'] }, + { key: '/users', icon: , label: '用户管理', roles: ['admin'] }, + + { key: '/roles', icon: , label: '角色管理', roles: ['admin'] }, + { key: '/menuList', icon: , label: '菜单管理', roles: ['admin'] + }, + { + key: '/organization', + icon: , + label: '组织管理', + roles: ['admin'] + }, + { + key: '/station', + icon: , + label: '场站管理', + roles: ['admin'] } ]; diff --git a/src/pages/MenuList.tsx b/src/pages/MenuList.tsx index b29218d..805f87f 100644 --- a/src/pages/MenuList.tsx +++ b/src/pages/MenuList.tsx @@ -1,166 +1,160 @@ -import React, { useState, useEffect } from 'react'; +import React, { useState } from 'react'; import { - Table, - Button, - Modal, - Form, - Input, - Select, - TreeSelect, - Space, - Popconfirm, - message, - Card, - Typography + Table, Button, Typography, Space, Modal, Form, Tree, message, + Input, Popconfirm, Select } from 'antd'; -import { PlusOutlined, EditOutlined, DeleteOutlined } from '@ant-design/icons'; -import type { MenuItem, RoleItem } from '@/types/menu'; +import { + PlusOutlined, EditOutlined, DeleteOutlined, SettingOutlined +} from '@ant-design/icons'; const { Title } = Typography; const { Option } = Select; -// 模拟系统角色 -const roleOptions: RoleItem[] = [ - { label: '系统管理员', value: 'admin' }, - { label: '操作员', value: 'operator' }, - { label: '观察员', value: 'viewer' }, -]; - -// 模拟菜单数据 -const mockMenuData: MenuItem[] = [ +// 1. 完整菜单树 +const menuData = [ { - id: '1', - name: '控制台', - path: '/', - parentId: null, - sort: 1, - roles: ['admin', 'operator', 'viewer'], - children: [], + id: 1, + title: '控制台', + path: '/dashboard', + parentId: 0, + children: [ + { id: 11, title: '概览', path: '/dashboard/overview', parentId: 1 }, + ], }, { - id: '2', - name: '设备管理', - path: '/devices', - parentId: null, - sort: 2, - roles: ['admin', 'operator'], - children: [], + id: 2, + title: '设备管理', + path: '/device', + parentId: 0, + children: [ + { id: 21, title: '机器人管理', path: '/device/robot', parentId: 2 }, + { id: 22, title: '视频监控', path: '/device/video', parentId: 2 }, + ], }, { - id: '3', - name: '视频监控', - path: '/video', - parentId: '2', - sort: 1, - roles: ['admin', 'operator', 'viewer'], - children: [], + id: 3, + title: '系统管理', + path: '/system', + parentId: 0, + children: [ + { id: 31, title: '用户管理', path: '/system/user', parentId: 3 }, + { id: 32, title: '角色管理', path: '/system/role', parentId: 3 }, + { id: 33, title: '菜单管理', path: '/system/menu', parentId: 3 }, + ], }, ]; -const MenuList: React.FC = () => { - const [form] = Form.useForm(); - const [menuList, setMenuList] = useState([]); - const [loading, setLoading] = useState(false); - const [modalVisible, setModalVisible] = useState(false); - const [title, setTitle] = useState('新增菜单'); - const [currentRecord, setCurrentRecord] = useState(null); +// 2. 角色列表 +const roleList = [ + { name: '平台admin', key: 'admin' }, + { name: '区域机构管理员', key: 'region_admin' }, + { name: '单个场站管理员', key: 'site_admin' }, + { name: '场站员工', key: 'site_staff' }, +]; - // 加载菜单 - useEffect(() => { - setLoading(true); - setTimeout(() => { - setMenuList(mockMenuData); - setLoading(false); - }, 300); - }, []); - - // 打开新增 - const handleAdd = () => { - form.resetFields(); - setTitle('新增菜单'); - setCurrentRecord(null); - setModalVisible(true); +// 3. 扁平化菜单 +const flattenMenu = (menus) => { + let arr = []; + const loop = (list, parentPath = '') => { + list.forEach(item => { + const fullPath = parentPath ? `${parentPath}/${item.path}` : item.path; + arr.push({ ...item, fullPath }); + if (item.children?.length) loop(item.children, item.path); + }); }; + loop(menus); + return arr; +}; + +// 存储角色权限 +const rolePermissionMap = {}; + +export default function Roles() { + const [menus, setMenus] = useState(flattenMenu(menuData)); + const [menuTree, setMenuTree] = useState(menuData); + + // 新增/编辑菜单 + const [menuModal, setMenuModal] = useState(false); + const [menuForm] = Form.useForm(); + const [editMenu, setEditMenu] = useState(null); + + // 权限配置 + const [permModal, setPermModal] = useState(false); + const [selectedRole, setSelectedRole] = useState(''); + const [checkedKeys, setCheckedKeys] = useState([]); // 打开编辑 - const handleEdit = (record: MenuItem) => { - form.setFieldsValue(record); - setTitle('编辑菜单'); - setCurrentRecord(record); - setModalVisible(true); + const handleEdit = (record) => { + setEditMenu(record); + menuForm.setFieldsValue(record); + setMenuModal(true); }; - // 删除 - const handleDelete = (id: string) => { - setMenuList(prev => prev.filter(item => item.id !== id)); - message.success('删除成功'); - }; - - // 提交保存 - const handleSubmit = () => { - form.validateFields().then(values => { - const params: MenuItem = { - ...values, - id: currentRecord?.id || Date.now().toString(), - }; - - if (currentRecord) { + // 保存菜单(新增+编辑) + const handleSaveMenu = () => { + menuForm.validateFields().then(values => { + if (editMenu) { // 编辑 - setMenuList(prev => prev.map(m => m.id === currentRecord.id ? params : m)); + setMenus(menus.map(m => m.id === editMenu.id ? { ...m, ...values } : m)); message.success('编辑成功'); } else { // 新增 - setMenuList(prev => [...prev, params]); - message.success('新增成功'); + const newMenu = { + ...values, + id: Date.now(), + parentId: 0, + }; + setMenus([...menus, newMenu]); + message.success('新增菜单成功'); } - setModalVisible(false); + setMenuModal(false); }); }; - // 表格列 + // 删除菜单 + const handleDelete = (id) => { + setMenus(menus.filter(m => m.id !== id)); + message.success('删除成功'); + }; + + // 打开权限配置 + const handlePermOpen = () => { + if (!selectedRole) { + message.warning('请先选择角色'); + return; + } + // 回显已保存权限 + const saved = rolePermissionMap[selectedRole] || []; + setCheckedKeys(saved); + setPermModal(true); + }; + + // 保存权限 + const handlePermSave = () => { + rolePermissionMap[selectedRole] = checkedKeys; + message.success(`【${selectedRole}】权限保存成功`); + setPermModal(false); + }; + const columns = [ - { - title: '菜单名称', - dataIndex: 'name', - key: 'name', - }, - { - title: '路由地址', - dataIndex: 'path', - key: 'path', - }, - { - title: '父级菜单', - key: 'parentName', - render: (_, record) => { - if (!record.parentId) return '一级菜单'; - const p = menuList.find(m => m.id === record.parentId); - return p?.name || '-'; - }, - }, - { - title: '关联角色', - key: 'roles', - render: (_, record) => ( -
- {record.roles.map(r => { - const ro = roleOptions.find(o => o.value === r); - return {ro?.label}; - })} -
- ), - }, + { title: '菜单名称', dataIndex: 'title', key: 'title', width: 180 }, + { title: '路由地址', dataIndex: 'fullPath', key: 'fullPath' }, { title: '操作', key: 'action', + width: 220, render: (_, record) => ( - + handleDelete(record.id)} > - + ), @@ -168,75 +162,87 @@ const MenuList: React.FC = () => { ]; return ( - -
- 菜单管理 - +
+
+ 菜单权限管理 + + + + +
+ {/* 菜单 新增/编辑 弹窗 */} setModalVisible(false)} - onOk={handleSubmit} - maskClosable={false} + title={editMenu ? '编辑菜单' : '新增菜单'} + open={menuModal} + onCancel={() => setMenuModal(false)} + onOk={handleSaveMenu} + width={500} > -
- - + + + - - - + + - - - ({ - title: m.name, - value: m.id, - disabled: m.id === currentRecord?.id, - }))} - /> - - - - - -
- - ); -}; -export default MenuList; + {/* 权限配置弹窗 */} + setPermModal(false)} + onOk={handlePermSave} + > + + + + ); +} diff --git a/src/pages/Organization.tsx b/src/pages/Organization.tsx new file mode 100644 index 0000000..22e140b --- /dev/null +++ b/src/pages/Organization.tsx @@ -0,0 +1,159 @@ +import React, { useState } from 'react'; +import { + Card, Table, Button, Typography, Space, Modal, Form, Tree, message, + Input, Popconfirm, Select, TreeSelect +} from 'antd'; +import { + PlusOutlined, EditOutlined, DeleteOutlined, BuildOutlined +} from '@ant-design/icons'; + +const { Title } = Typography; +const { Option } = Select; + +// 模拟组织数据 +const orgData = [ + { + id: 1, + name: '华东区域总部', + code: 'EAST_REGION', + parentId: 0, + status: '0', + + }, + { + id: 2, + name: '华北区域总部', + code: 'NORTH_REGION', + parentId: 0, + status: '0', + }, +]; + +// 转平数组用于表格 +const flatten = (list) => { + let arr = []; + const loop = (data) => { + data.forEach(item => { + arr.push(item); + if (item.children) loop(item.children); + }); + }; + loop(list); + return arr; +}; + +export default function Organization() { + const [orgList, setOrgList] = useState(flatten(orgData)); + const [treeData] = useState(orgData); + + const [modalVisible, setModalVisible] = useState(false); + const [form] = Form.useForm(); + const [editRecord, setEditRecord] = useState(null); + + // 打开新增 + const handleAdd = () => { + form.resetFields(); + setEditRecord(null); + setModalVisible(true); + }; + + // 打开编辑 + const handleEdit = (record) => { + form.setFieldsValue(record); + setEditRecord(record); + setModalVisible(true); + }; + + // 删除 + const handleDelete = (id) => { + setOrgList(orgList.filter(i => i.id !== id)); + message.success('删除成功'); + }; + + // 保存 + const handleSave = () => { + form.validateFields().then(values => { + if (editRecord) { + setOrgList(orgList.map(i => i.id === editRecord.id ? { ...i, ...values } : i)); + message.success('编辑成功'); + } else { + setOrgList([...orgList, { ...values, id: Date.now() }]); + message.success('新增成功'); + } + setModalVisible(false); + }); + }; + + const columns = [ + { title: '组织名称', dataIndex: 'name' }, + { title: '组织编码', dataIndex: 'code' }, + { + title: '状态', + dataIndex: 'status', + render: s => s === '0' ? + : + + }, + { + title: '操作', + render: (_, r) => ( + + + handleDelete(r.id)}> + + + + ) + } + ]; + + return ( +
+
+ 组织管理 + +
+ + +
+ + + setModalVisible(false)} + onOk={handleSave} + width={500} + > +
+ + + + + + + + + + + + + +
+ + ); +} diff --git a/src/pages/Roles.tsx b/src/pages/Roles.tsx index 2b178d0..e90672f 100644 --- a/src/pages/Roles.tsx +++ b/src/pages/Roles.tsx @@ -1,103 +1,220 @@ -import React from 'react'; -import { - Card, - Row, - Col, - Button, - Typography, - Tag, - Space, - Divider, - List +import React, { useState, useEffect } from 'react'; +import { + Card, Row, Col, Button, Typography, Space, Modal, Form, Tree, message, + Input, Popconfirm, Divider } from 'antd'; -import { - SafetyCertificateOutlined, - LockOutlined, - CheckCircleOutlined, - PlusOutlined, - TeamOutlined +import { + PlusOutlined, LockOutlined, SettingOutlined, EditOutlined, DeleteOutlined } from '@ant-design/icons'; const { Text, Title } = Typography; +// 默认角色 +const defaultRoles = [ + { name: '超级管理员', key: 'admin', desc: '系统全权限', level: 'Level 1' }, + { name: '机构管理员', key: 'operator', desc: '设备/警报/视频管理', level: 'Level 2' }, + { name: '场站管理员', key: 'site_admin', desc: '场站管理权限', level: 'Level 2' }, + { name: '场站操作员', key: 'viewer', desc: '只读查看', level: 'Level 3' }, +]; + +// 模拟菜单树 +const defaultMenuTree = [ + { title: '控制台', value: 'dashboard', key: '1' }, + { + title: '设备管理', value: 'device', key: '2', + children: [ + { title: '机器人管理', value: 'robot', key: '2-1' }, + { title: '视频监控', value: 'video', key: '2-2' }, + ] + }, + { title: '告警管理', value: 'alarm', key: '3' }, + { title: '用户管理', value: 'user', key: '4' }, + { title: '角色管理', value: 'role', key: '5' }, + { title: '菜单管理', value: 'menu', key: '6' }, +]; + export default function Roles() { - const roles = [ - { name: '超级管理员', key: 'admin', desc: '系统全权限,支持资源分配与权限审计', level: 'Level 1' }, - { name: '系统操作员', key: 'operator', desc: '设备策略下发,实时指令干预及警报处理', level: 'Level 2' }, - { name: '访客观察员', key: 'viewer', desc: '只读权限,支持实时视频预览与报表查阅', level: 'Level 3' }, - ]; + const [roles, setRoles] = useState(defaultRoles); + const [menuTree, setMenuTree] = useState(defaultMenuTree); + const [checkedKeys, setCheckedKeys] = useState([]); + const [permVisible, setPermVisible] = useState(false); + const [currentRole, setCurrentRole] = useState(null); - const permissions = [ - { name: '设备控制权限', sectors: ['远程重启', '策略部署', '紧急停机'] }, - { name: '视频管理权限', sectors: ['实时监控', '云台控制', '录像下载'] }, - { name: '核心准入权限', sectors: ['审计日志', '成本报表', '用户配置'] }, - ]; + // 新增/编辑角色弹窗 + const [roleModalVisible, setRoleModalVisible] = useState(false); + const [roleForm] = Form.useForm(); + const [editRole, setEditRole] = useState(null); - return ( -
-
- 系统角色权限矩阵 - -
+ // 打开权限配置 + const handleOpenPerm = async (role) => { + setCurrentRole(role); + setCheckedKeys([]); + setPermVisible(true); + }; - - {roles.map((role) => ( -
- }>管理成员 - ]} - > - - - - } - title={{role.name}} - description={ - - {role.level} - {role.desc} - - } - /> - - - ))} - + // 保存权限 + const handleSavePerm = () => { + message.success(`【${currentRole.name}】菜单权限配置成功!`); + setPermVisible(false); + }; - 功能模块权限分配 (细分原子操作)} - style={{ borderRadius: 2 }} - > - - {permissions.map((p) => ( - -
- {p.name} - ( - -
- {item} - -
-
- )} - /> -
- - ))} - - - - ); + // 打开新增角色 + const handleAddRole = () => { + roleForm.resetFields(); + setCheckedKeys([]); + setEditRole(null); + setRoleModalVisible(true); + }; + + // 打开编辑角色 + const handleEditRole = (record) => { + setEditRole(record); + roleForm.setFieldsValue(record); + setCheckedKeys(record.menuKeys || []); + setRoleModalVisible(true); + }; + + // 删除角色 + const handleDeleteRole = (key) => { + setRoles(roles.filter(item => item.key !== key)); + message.success('删除成功'); + }; + + // 提交保存角色 + const handleSaveRole = () => { + roleForm.validateFields().then(values => { + const roleData = { + ...values, + menuKeys: checkedKeys, // 保存菜单权限 + level: editRole ? editRole.level : 'Level Custom' + }; + + if (editRole) { + // 编辑 + setRoles(roles.map(r => r.key === editRole.key ? { ...r, ...roleData } : r)); + message.success('编辑成功'); + } else { + // 新增 + const newRole = { + ...roleData, + key: values.key || Date.now().toString(), + }; + setRoles([...roles, newRole]); + message.success('新增角色成功'); + } + setRoleModalVisible(false); + }); + }; + + return ( +
+
+ 角色管理 + +
+ + + {roles.map(role => ( +
+ } + onClick={() => handleOpenPerm(role)} + > + 菜单权限 + , + , + handleDeleteRole(role.key)} + > + + + ]} + > + + {role.level} + {role.desc} + + } + /> + + + ))} + + + {/* ========== 菜单权限配置弹窗 ========== */} + setPermVisible(false)} + onOk={handleSavePerm} + > + + + + {/* ========== 新增/编辑角色弹窗(带菜单选择) ========== */} + setRoleModalVisible(false)} + onOk={handleSaveRole} + maskClosable={false} + width={550} + > +
+ + + + + + + + + + + + + {/* ========== 新增角色时直接选择菜单权限 ========== */} + 菜单权限配置 + + +
+ + ); } diff --git a/src/pages/StationManage.tsx b/src/pages/StationManage.tsx new file mode 100644 index 0000000..5b5a3f1 --- /dev/null +++ b/src/pages/StationManage.tsx @@ -0,0 +1,290 @@ +import React, { useState } from 'react'; +import { + Card, Table, Button, Typography, Space, Modal, Form, + Input, Popconfirm, Select, Switch, Divider, Upload, message, + Row, Col, Tag +} from 'antd'; +import { + PlusOutlined, EditOutlined, DeleteOutlined, + BgColorsOutlined, UserOutlined, SettingOutlined +} from '@ant-design/icons'; + +const { Title } = Typography; +const { Option } = Select; + +// 模拟场站数据 +const stationData = [ + { + id: 1, + name: '上海浦东光伏场站', + code: 'PD001', + address: '上海市浦东新区光伏大道', + status: 0, + contact: '张三', + phone: '13800138000', + maintainCompany: '上海新能源运维公司', + areaCount: 3, + hasMap: true, + }, + { + id: 2, + name: '苏州昆山储能电站', + code: 'KS001', + address: '苏州市昆山市能源路', + status: 1, + contact: '李四', + phone: '13900139000', + maintainCompany: '苏州智慧运维有限公司', + areaCount: 2, + hasMap: false, + }, +]; + +// 场站分区模拟 +const areaData = [ + { id: 1, stationId: 1, name: 'A区', type: '清扫区', status: 0 }, + { id: 2, stationId: 1, name: 'B区', type: '巡检区', status: 0 }, + { id: 3, stationId: 1, name: 'C区', type: '禁飞区', status: 1 }, +]; + +export default function StationManage() { + const [stationList, setStationList] = useState(stationData); + const [currentStation, setCurrentStation] = useState(null); + + // 主弹窗 + const [modalVisible, setModalVisible] = useState(false); + const [form] = Form.useForm(); + const [editRecord, setEditRecord] = useState(null); + + // 分区弹窗 + const [areaVisible, setAreaVisible] = useState(false); + // 边界绘制弹窗 + const [mapVisible, setMapVisible] = useState(false); + + // 打开新增 + const handleAdd = () => { + form.resetFields(); + setEditRecord(null); + setModalVisible(true); + }; + + // 打开编辑 + const handleEdit = (record) => { + form.setFieldsValue(record); + setEditRecord(record); + setModalVisible(true); + }; + + // 删除场站 + const handleDelete = (id) => { + setStationList(stationList.filter(s => s.id !== id)); + message.success('删除成功'); + }; + + // 保存场站 + const handleSave = () => { + form.validateFields().then(values => { + const data = { ...values, id: editRecord?.id || Date.now() }; + if (editRecord) { + setStationList(stationList.map(s => s.id === editRecord.id ? data : s)); + message.success('编辑成功'); + } else { + setStationList([...stationList, data]); + message.success('新增场站成功'); + } + setModalVisible(false); + }); + }; + + // 打开分区 + const handleOpenArea = (record) => { + setCurrentStation(record); + setAreaVisible(true); + }; + + // 打开地图边界 + const handleOpenMap = (record) => { + setCurrentStation(record); + setMapVisible(true); + }; + + const columns = [ + { title: '场站名称', dataIndex: 'name' }, + { title: '场站编号', dataIndex: 'code' }, + { title: '地址', dataIndex: 'address' }, + { + title: '状态', + dataIndex: 'status', + render: (s) => s === 0 ? + 启用 : + 停用 + }, + { title: '负责人', dataIndex: 'contact' }, + { title: '运维单位', dataIndex: 'maintainCompany' }, + { + title: '分区数量', + dataIndex: 'areaCount', + }, + { + title: '地图数据', + render: (_, r) => r.hasMap ? + 已维护 : + 未维护 + }, + { + title: '操作', + width: 400, + render: (_, record) => ( + + + + + + + + handleDelete(record.id)}> + + + + ) + } + ]; + + return ( +
+
+ 场站管理 + +
+ + +
+ + + {/* ====================== 1. 新增/编辑场站 ====================== */} + setModalVisible(false)} + onOk={handleSave} + width={700} + destroyOnClose + > +
+ +
+ + + + + + + + + + + + + + + + 联系人与运维单位 + + + + + + + + + + + + + + + + + + + + + + + + + + + {/* ====================== 2. 场站分区设置 ====================== */} + setAreaVisible(false)} + footer={null} + > +
+ +
+
r.status === 0 ? '正常' : '禁用' + }, + { + title: '操作', render: () => ( + + + + + ) + } + ]} + dataSource={areaData.filter(a => a.stationId === currentStation?.id)} + /> + + + {/* ====================== 3. 场站边界绘制 & 地图维护 ====================== */} + setMapVisible(false)} + footer={null} + > +
+ {/**/} +
地图组件 / 百度地图 / 高德地图 区域绘制
+
支持:边界点绘制、电子围栏、导入KML、保存GeoJSON
+ + + + + + +
+
+ + ); +} diff --git a/src/pages/Users.tsx b/src/pages/Users.tsx index 9ee9604..eb83a18 100644 --- a/src/pages/Users.tsx +++ b/src/pages/Users.tsx @@ -1,97 +1,288 @@ -import React from 'react'; -import { - Table, - Tag, - Space, - Button, - Input, - Typography, - Card, - Avatar +import React, { useState } from 'react'; +import { + Card, Table, Button, Typography, Space, Modal, Form, + Input, Select, Popconfirm, message, Tag, TreeSelect, Divider } from 'antd'; -import { - PlusOutlined, - SearchOutlined, - EditOutlined, - DeleteOutlined, - UserOutlined +import { + PlusOutlined, EditOutlined, DeleteOutlined, UserOutlined, + KeyOutlined, TeamOutlined } from '@ant-design/icons'; -import { MANAGED_USERS } from '../constants'; -const { Text, Title } = Typography; +const { Title } = Typography; +const { Option } = Select; -export default function Users() { - const columns = [ - { - title: '用户信息', - key: 'userinfo', - render: (record: any) => ( - - } /> - - {record.name} - {record.id} - - - ), - }, - { - title: '系统权限', - dataIndex: 'role', - key: 'role', - render: (role: string) => ( - - {role} - - ), - }, - { - title: '账户状态', - key: 'status', - render: () => ACTIVE, - }, - { - title: '最近登录', - key: 'lastLogin', - render: () => 2024-03-24 10:22:45, - }, - { - title: '操作', - key: 'action', - align: 'right' as const, - render: () => ( - - - - - ), - }, - ]; +// ---------------------- 1. 角色配置(和你的权限表对齐) ---------------------- +const roleOptions = [ + { + label: '平台admin', + value: 'admin', + desc: '全局配置、机构/菜单/设备权限分配、核心参数配置、平台级异常审核' + }, + { + label: '区域机构管理员', + value: 'region_admin', + desc: '区域场站/账号管理、跨站机器人调度、区域报表统计' + }, + { + label: '单个场站管理员', + value: 'site_admin', + desc: '场站配置、设备/机器人管理、场站告警处置、运维记录审核' + }, + { + label: '场站员工', + value: 'site_staff', + desc: '机器人启停/参数调整、任务执行、巡检文件上传、故障处置' + }, +]; - return ( -
-
- 用户管理权限控制 - - } - placeholder="搜索用户名或工号..." - style={{ width: 250, borderRadius: 2 }} - /> - - -
+// 模拟机构/场站数据(数据权限用) +const orgTreeData = [ + { + title: '华东区域', + value: 'region_east', + children: [ + { title: '上海光伏场站', value: 'site_shanghai' }, + { title: '苏州风电场站', value: 'site_suzhou' }, + ] + }, + { + title: '华北区域', + value: 'region_north', + children: [ + { title: '张家口光伏场站', value: 'site_zhangjiakou' }, + ] + } +]; - -
({ ...u, key: u.id }))} - pagination={false} - /> - - - ); +// 模拟用户数据(带角色+数据权限) +const initUserList = [ + { + id: 1, + username: 'admin', + nickName: '平台超级管理员', + role: 'admin', + roleLabel: '平台admin', + dataScope: ['region_east', 'region_north'], + status: '0' + }, + { + id: 2, + username: 'region_shanghai', + nickName: '华东区域管理员', + role: 'region_admin', + roleLabel: '区域机构管理员', + dataScope: ['region_east'], + status: '0' + }, + { + id: 3, + username: 'site_shanghai01', + nickName: '上海场站管理员', + role: 'site_admin', + roleLabel: '单个场站管理员', + dataScope: ['site_shanghai'], + status: '0' + }, + { + id: 4, + username: 'staff_zhang', + nickName: '上海场站运维员', + role: 'site_staff', + roleLabel: '场站员工', + dataScope: ['site_shanghai'], + status: '0' + }, +]; + +export default function UserList() { + const [userList, setUserList] = useState(initUserList); + const [loading, setLoading] = useState(false); + + // 新增/编辑弹窗 + const [modalVisible, setModalVisible] = useState(false); + const [form] = Form.useForm(); + const [currentUser, setCurrentUser] = useState(null); + + // 打开新增 + const handleAdd = () => { + form.resetFields(); + setCurrentUser(null); + setModalVisible(true); + }; + + // 打开编辑 + const handleEdit = (record) => { + form.setFieldsValue(record); + setCurrentUser(record); + setModalVisible(true); + }; + + // 删除用户 + const handleDelete = (id) => { + setUserList(userList.filter(u => u.id !== id)); + message.success('删除成功'); + }; + + // 提交保存 + const handleSubmit = () => { + form.validateFields().then(values => { + // 角色标签回填,方便表格展示 + const roleInfo = roleOptions.find(r => r.value === values.role); + const payload = { + ...values, + roleLabel: roleInfo?.label + }; + + if (currentUser) { + // 编辑 + setUserList(userList.map(u => u.id === currentUser.id ? { ...u, ...payload } : u)); + message.success('编辑成功'); + } else { + // 新增 + const newUser = { ...payload, id: Date.now() }; + setUserList([...userList, newUser]); + message.success('新增用户成功'); + } + setModalVisible(false); + }); + }; + + // 表格列(增强角色+数据权限展示) + const columns = [ + { + title: '用户信息', + key: 'userInfo', + render: (_, record) => ( + + +
+
{record.nickName}
+
账号:{record.username}
+
+
+ ) + }, + { + title: '角色', + dataIndex: 'roleLabel', + key: 'roleLabel', + render: (text) => {text} + }, + { + title: '数据权限', + dataIndex: 'dataScope', + key: 'dataScope', + render: (dataScope) => { + if (!dataScope?.length) return '-'; + const count = dataScope.length; + return 已分配 {count} 个机构/场站; + } + }, + { + title: '状态', + dataIndex: 'status', + key: 'status', + render: (s) => s === '0' ? + 正常 : + 禁用 + }, + { + title: '操作', + key: 'action', + render: (_, record) => ( + + + handleDelete(record.id)}> + + + + ) + } + ]; + + return ( + +
+ 用户管理(角色+数据权限) + +
+ +
+ + {/* 新增/编辑弹窗:角色+数据权限双配置 */} + setModalVisible(false)} + onOk={handleSubmit} + maskClosable={false} + width={600} + > +
+ + + + + + + + + 权限配置 + + + + + + + + + + + + + +
+ + ); } diff --git a/src/router/index.tsx b/src/router/index.tsx index 61a25f7..719e233 100644 --- a/src/router/index.tsx +++ b/src/router/index.tsx @@ -12,6 +12,8 @@ import Roles from '../pages/Roles'; import Users from '../pages/Users'; import SerialNumGenerate from '../pages/SerialNumGenerate'; import MenuList from '../pages/MenuList'; +import Organization from '../pages/Organization'; +import StationManage from '../pages/StationManage'; export const router = createBrowserRouter([ { @@ -61,6 +63,18 @@ export const router = createBrowserRouter([ path: 'menuList', element: , }, + { + path: 'menuList', + element: , + }, + { + path: 'organization', + element: , + }, + { + path: 'station', + element: , + }, { path: '*', element:
模块开发中...
,