import XinTable from "@/components/XinTable"; import type {XinTableColumn} from "@/components/XinTable/typings.ts"; import {Button, Card, type CardProps, Col, message, Row, Switch, Table, type TableProps, Tag, Tooltip, Tree, type TreeProps, Typography} from "antd"; import {type RuleFieldsList, rulesList, setRule, statusRole, users as usersApi} from "@/api/system/sys_role"; import type {ISysRole} from "@/domain/iSysRole.ts"; import React, {useEffect, useRef, useState} from "react"; import type ISysUser from "@/domain/iSysUser.ts"; import {KeyOutlined, SaveOutlined, SmileOutlined, TeamOutlined} from "@ant-design/icons"; import { useTranslation } from "react-i18next"; import { isArray } from "lodash"; import dayjs from "dayjs"; import relativeTime from "dayjs/plugin/relativeTime"; dayjs.extend(relativeTime); const { Title, Text } = Typography; const Role = () => { const {t} = useTranslation(); // 状态管理 const [selectedRoleId, setSelectedRoleId] = useState(); const [activeTab, setActiveTab] = useState('users'); const [roleUsers, setRoleUsers] = useState([]); const [roleUsersTotal, setRoleUsersTotal] = useState(0); const [isLoadingUsers, setIsLoadingUsers] = useState(false); const [ruleFields, setRuleFields] = useState([]); const [checkedRuleKeys, setCheckedRuleKeys] = useState([]); const [isSavingRules, setIsSavingRules] = useState(false); // 树展开状态 const [expandedKeys, setExpandedKeys] = useState([]); // 树引用 const treeRef = useRef(null); // Tab 配置 const tabList: CardProps['tabList'] = [ { key: "users", icon: , label: t('system.role.tab.users'), }, { key: "rules", icon: , label: t('system.role.tab.rules') } ]; // 角色表格列配置 const roleColumns: XinTableColumn[] = [ { title: 'id', dataIndex: "id", valueType: "text", align: 'center', width: 80, hideInForm: true }, { title: t('system.role.table.roleName'), dataIndex: "name", valueType: "text", align: "center", required: true, colProps: {span: 8}, rules: [{ required: true, message: t('system.role.table.roleName.required') }], render: (value: any, record: ISysRole) => ( <> {value} ), }, { title: t('system.role.table.sort'), dataIndex: "sort", valueType: "digit", hideInSearch: true, align: "center", required: true, colProps: {span: 8}, rules: [{ required: true, message: t('system.role.table.sort.required') }], render: (value: number) => {value}, }, { title: t('system.role.table.userCount'), dataIndex: "countUser", valueType: "text", hideInForm: true, align: "center", render: (value: number) => {value}{t('system.role.userTable.person')}, }, { title: t('system.role.table.status'), dataIndex: "status", valueType: "switch", align: "center", colProps: {span: 8}, filters: [ { text: t('system.role.table.status.disable'), value: 0 }, { text: t('system.role.table.status.enable'), value: 1 }, ], hideInSearch: true, required: true, rules: [{ required: true, message: t('system.role.table.status.required') }], render: (_, record: ISysRole) => ( { event.stopPropagation(); try { await statusRole(record.id!); message.success(t('system.role.message.statusUpdateSuccess')); } catch (error) { message.error(t('system.role.message.statusUpdateFailed')); console.error('Failed to update role status:', error); } }} /> ), }, { valueType: 'textarea', title: t('system.role.table.description'), dataIndex: 'description', colProps: {span: 24}, hideInTable: true, }, { title: t('system.role.table.createdAt'), hideInForm: true, hideInSearch: true, dataIndex: 'created_at', align: 'center', render: (value: string) => value ? dayjs(value).fromNow() : '-', }, { title: t('system.role.table.updatedAt'), hideInForm: true, hideInSearch: true, dataIndex: 'updated_at', align: 'center', render: (value: string) => value ? dayjs(value).fromNow() : '-', }, ]; // 用户列表表格列配置 const userColumns: TableProps['columns'] = [ { title: t('system.role.userTable.userId'), dataIndex: 'id', key: 'id', align: 'center', width: 80, }, { title: t('system.role.userTable.username'), dataIndex: 'username', key: 'username', align: 'center', }, { title: t('system.role.userTable.nickname'), dataIndex: 'nickname', key: 'nickname', align: 'center', }, { title: t('system.role.userTable.email'), dataIndex: 'email', key: 'email', align: 'center', ellipsis: true, }, { title: t('system.role.userTable.mobile'), dataIndex: 'mobile', key: 'mobile', align: 'center', }, { title: t('system.role.userTable.status'), dataIndex: 'status', key: 'status', align: 'center', render: (value: number) => { const status = value === 0 ? { color: 'error', text: t('system.role.userTable.status.banned') } : { color: 'success', text: t('system.role.userTable.status.normal') }; return {status.text}; }, width: 80, }, ]; // 获取角色用户列表 const fetchRoleUsers = async (page: number = 1, pageSize: number = 10) => { if (!selectedRoleId) return; setIsLoadingUsers(true); try { const response = await usersApi(selectedRoleId, { page, pageSize }); const { data, total } = response.data.data!; setRoleUsers(data); setRoleUsersTotal(total); } finally { setIsLoadingUsers(false); } } useEffect(() => { rulesList().then(response => { setRuleFields(response.data.data!); }); }, []); // 当selectedRoleId变化时,重新获取用户列表 useEffect(() => { fetchRoleUsers(1, 10) }, [selectedRoleId]); // 事件处理函数 const handleRoleSelect = (record: ISysRole) => { setSelectedRoleId(record.id!); // 回显当前角色的权限 if (record.ruleIds && record.ruleIds.length > 0) { setCheckedRuleKeys(record.ruleIds); } else { setCheckedRuleKeys([]); } }; // 渲染权限树节点 const renderRuleTreeNode = (node: RuleFieldsList) => { if (node.local) { return ( <> {t(node.local)} - {node.title} ); } return <>{node.title}; }; // 处理权限树勾选变化 const handleRuleCheck: TreeProps['onCheck'] = (checkedKeys) => { if(isArray(checkedKeys)) { setCheckedRuleKeys(checkedKeys); }else { setCheckedRuleKeys(checkedKeys.checked); } }; // 处理树展开状态变化 const handleTreeExpand = (keys: React.Key[]) => { setExpandedKeys(keys); }; // 获取所有节点的key const getAllNodeKeys = (nodes: RuleFieldsList[]): React.Key[] => { let keys: React.Key[] = []; nodes.forEach(node => { keys.push(node.key); if (node.children && node.children.length > 0) { keys = [...keys, ...getAllNodeKeys(node.children)]; } }); return keys; }; // 展开全部节点 const expandAll = () => { const allKeys = getAllNodeKeys(ruleFields); setExpandedKeys(allKeys); }; // 折叠全部节点 const collapseAll = () => { setExpandedKeys([]); }; // 选择全部节点 const selectAll = () => { const allKeys = getAllNodeKeys(ruleFields); setCheckedRuleKeys(allKeys); }; // 取消选择全部节点 const deselectAll = () => { setCheckedRuleKeys([]); }; // 反选节点 const invertSelection = () => { const allKeys = getAllNodeKeys(ruleFields); const currentCheckedSet = new Set(checkedRuleKeys); const invertedKeys = allKeys.filter(key => !currentCheckedSet.has(key)); setCheckedRuleKeys(invertedKeys); }; // 保存权限设置 const handleSaveRules = async () => { if (!selectedRoleId) { message.warning(t('system.role.message.selectRoleFirst')); return; } setIsSavingRules(true); try { const ruleIds = checkedRuleKeys.map(key => Number(key)); await setRule(selectedRoleId, ruleIds); message.success(t('system.role.message.rulesSaveSuccess')); } finally { setIsSavingRules(false); } } return ( <>
{t('system.role.page.title')} {t('system.role.page.description')}
{/* 角色列表 */} api="/system/role" accessName="system.role" columns={roleColumns} rowKey="id" searchShow={false} searchProps={false} scroll={{x: 800}} editShow={(row) => row.id !== 1} deleteShow={(row) => row.id !== 1} formProps={{ grid: true, rowProps: { gutter: [20, 0] }, layout: 'vertical' }} modalProps={{ width: 800 }} operateProps={{ fixed: 'right' }} rowSelection={{ type: 'radio', selectedRowKeys: selectedRoleId ? [selectedRoleId] : [], onChange: (_, rows) => handleRoleSelect(rows[0]) }} onRow ={(record) => ({ onClick: () => handleRoleSelect(record) })} /> {/* 用户列表和权限管理 */} { selectedRoleId ? ( activeTab === 'users' ? ( loading={isLoadingUsers} dataSource={roleUsers} bordered={true} columns={userColumns} size="small" pagination={{ total: roleUsersTotal, showSizeChanger: true, onChange: fetchRoleUsers }} scroll={{x: 600}} /> ) : ( <>
{t('system.role.permission.selectedCount', { count: checkedRuleKeys.length })}
) ) : (

{t('system.role.placeholder.selectRole')}

)}
); } export default Role;