From be8c526a454aa9081000d15f26bebdef8c183aea Mon Sep 17 00:00:00 2001
From: wsj <593216709@qq.com>
Date: Mon, 26 Oct 2026 23:34:23 +0800
Subject: [PATCH] feat: 提交

---
 src/pages/system/roles/index.tsx |  125 +++++++++++++++++++++++++++++++++++++++++
 1 files changed, 125 insertions(+), 0 deletions(-)

diff --git a/src/pages/system/roles/index.tsx b/src/pages/system/roles/index.tsx
new file mode 100644
index 0000000..1d6f6ac
--- /dev/null
+++ b/src/pages/system/roles/index.tsx
@@ -0,0 +1,125 @@
+import { useCallback, useMemo, useRef, useState } from 'react';
+import { ProTable } from '@ant-design/pro-components';
+import type { ActionType } from '@ant-design/pro-components';
+import { App as AntdApp, Descriptions, Drawer, Tag } from 'antd';
+import { createRoleColumns, getRoleStatusMeta, getRoleTypeMeta } from './columns';
+import { getRoleDetail, getRoleList, toggleRoleStatus } from './server';
+import type { RoleListQuery, RoleVO } from './types';
+
+const Roles = () => {
+  const actionRef = useRef<ActionType>();
+  const { message, modal } = AntdApp.useApp();
+  const [detail, setDetail] = useState<RoleVO | null>(null);
+  const [detailOpen, setDetailOpen] = useState(false);
+  const [detailLoading, setDetailLoading] = useState(false);
+
+  const handleDetail = useCallback(async (record: RoleVO) => {
+    setDetailOpen(true);
+    setDetailLoading(true);
+
+    try {
+      const data = await getRoleDetail({ id: record.id });
+      setDetail(data);
+    } catch {
+      // 错误已由 request 统一提示
+      setDetailOpen(false);
+    } finally {
+      setDetailLoading(false);
+    }
+  }, []);
+
+  const handleToggle = useCallback(
+    (record: RoleVO) => {
+      const nextAction = record.status === 'ENABLED' ? '禁用' : '启用';
+      modal.confirm({
+        title: `${nextAction}角色`,
+        content: `确定要${nextAction}角色「${record.roleName}」吗?`,
+        okText: nextAction,
+        cancelText: '取消',
+        okButtonProps: { danger: record.status === 'ENABLED' },
+        onOk: async () => {
+          await toggleRoleStatus({ id: record.id });
+          message.success(`角色已${nextAction}`);
+          actionRef.current?.reload();
+        },
+      });
+    },
+    [message, modal],
+  );
+
+  const columns = useMemo(
+    () => createRoleColumns({ onDetail: handleDetail, onToggle: handleToggle }),
+    [handleDetail, handleToggle],
+  );
+
+  return (
+    <div className="role-page">
+      <ProTable<RoleVO, RoleListQuery>
+        rowKey="id"
+        actionRef={actionRef}
+        columns={columns}
+        cardProps={{ className: 'table-card' }}
+        search={{ labelWidth: 'auto', defaultCollapsed: false }}
+        scroll={{ x: 960 }}
+        options={false}
+        pagination={{
+          defaultPageSize: 10,
+          showSizeChanger: true,
+          hideOnSinglePage: false,
+          showTotal: (total) => `共 ${total} 条`,
+        }}
+        request={async (params) => {
+          const { current = 1, pageSize = 10, roleName, status } = params;
+          const keyword = roleName?.trim().toLowerCase();
+          const list = await getRoleList();
+          const filtered = list.filter((role) => {
+            const matchesKeyword =
+              !keyword ||
+              role.roleName.toLowerCase().includes(keyword) ||
+              role.roleCode.toLowerCase().includes(keyword);
+            const matchesStatus = !status || role.status === status;
+            return matchesKeyword && matchesStatus;
+          });
+          const start = (current - 1) * pageSize;
+
+          return {
+            data: filtered.slice(start, start + pageSize),
+            total: filtered.length,
+            success: true,
+          };
+        }}
+      />
+
+      <Drawer
+        title="角色详情"
+        width={460}
+        open={detailOpen}
+        loading={detailLoading}
+        onClose={() => {
+          setDetailOpen(false);
+          setDetail(null);
+        }}
+      >
+        {detail && (
+          <Descriptions column={1} bordered size="small">
+            <Descriptions.Item label="角色名称">{detail.roleName}</Descriptions.Item>
+            <Descriptions.Item label="角色编码">{detail.roleCode}</Descriptions.Item>
+            <Descriptions.Item label="角色类型">
+              <Tag color={getRoleTypeMeta(detail.roleType).color} bordered={false}>
+                {getRoleTypeMeta(detail.roleType).text}
+              </Tag>
+            </Descriptions.Item>
+            <Descriptions.Item label="状态">
+              <Tag color={getRoleStatusMeta(detail.status).color} bordered={false}>
+                {getRoleStatusMeta(detail.status).text}
+              </Tag>
+            </Descriptions.Item>
+            <Descriptions.Item label="描述">{detail.description || '-'}</Descriptions.Item>
+          </Descriptions>
+        )}
+      </Drawer>
+    </div>
+  );
+};
+
+export default Roles;

--
Gitblit v1.9.3