|
|
@@ -0,0 +1,251 @@
|
|
|
+
|
|
|
+import React, { useState, useEffect } from 'react';
|
|
|
+import {
|
|
|
+ Layout, Menu, Button, Table, Space,
|
|
|
+ Form, Input, Select, TreeSelect, message, Modal,
|
|
|
+ Card, Spin, Row, Col, Breadcrumb, Avatar,
|
|
|
+ Dropdown, ConfigProvider, theme, Typography,
|
|
|
+ Switch, Badge, Image, Upload, Divider, Descriptions,
|
|
|
+ Popconfirm, Tag, Statistic, DatePicker, Radio, Progress, Tabs, List, Alert, Collapse, Empty, Drawer, Tree
|
|
|
+} from 'antd';
|
|
|
+
|
|
|
+import 'dayjs/locale/zh-cn';
|
|
|
+import {
|
|
|
+ organizationAPI,
|
|
|
+} from './api.ts';
|
|
|
+
|
|
|
+import type { Organization } from '../share/types.ts';
|
|
|
+
|
|
|
+const { Title } = Typography;
|
|
|
+
|
|
|
+// 系统管理页面 - 机构管理
|
|
|
+export const OrganizationPage = () => {
|
|
|
+const [organizations, setOrganizations] = useState<Organization[]>([]);
|
|
|
+const [treeData, setTreeData] = useState<Organization[]>([]);
|
|
|
+const [loading, setLoading] = useState(false);
|
|
|
+const [modalVisible, setModalVisible] = useState(false);
|
|
|
+const [currentOrg, setCurrentOrg] = useState<Organization | null>(null);
|
|
|
+const [form] = Form.useForm();
|
|
|
+
|
|
|
+// 加载机构数据
|
|
|
+useEffect(() => {
|
|
|
+ loadOrganizations();
|
|
|
+}, []);
|
|
|
+
|
|
|
+const loadOrganizations = async () => {
|
|
|
+ setLoading(true);
|
|
|
+ try {
|
|
|
+ const res = await organizationAPI.getOrganizations();
|
|
|
+ setTreeData(res.data);
|
|
|
+ // 将树形数据转换为扁平列表
|
|
|
+ const flattenOrganizations = flattenTree(res.data);
|
|
|
+ setOrganizations(flattenOrganizations);
|
|
|
+ } catch (err) {
|
|
|
+ message.error('加载机构数据失败');
|
|
|
+ console.error(err);
|
|
|
+ } finally {
|
|
|
+ setLoading(false);
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// 树形数据扁平化
|
|
|
+// 转换Organization为TreeSelect需要的格式
|
|
|
+const convertToTreeData = (orgs: Organization[]): Array<{value: string; title: string; children?: any}> => {
|
|
|
+ return orgs.map(org => ({
|
|
|
+ value: String(org.id),
|
|
|
+ title: org.name,
|
|
|
+ children: org.children ? convertToTreeData(org.children) : undefined,
|
|
|
+ ...org
|
|
|
+ }));
|
|
|
+};
|
|
|
+
|
|
|
+const flattenTree = (tree: Organization[]): Organization[] => {
|
|
|
+ return tree.reduce((acc, node) => {
|
|
|
+ acc.push({
|
|
|
+ id: node.id,
|
|
|
+ name: node.name,
|
|
|
+ code: node.code,
|
|
|
+ parentId: node.parentId,
|
|
|
+ description: node.description
|
|
|
+ });
|
|
|
+ if (node.children) {
|
|
|
+ acc.push(...flattenTree(node.children));
|
|
|
+ }
|
|
|
+ return acc;
|
|
|
+ }, [] as Organization[]);
|
|
|
+};
|
|
|
+
|
|
|
+// 打开创建机构弹窗
|
|
|
+const handleCreate = () => {
|
|
|
+ setCurrentOrg(null);
|
|
|
+ setModalVisible(true);
|
|
|
+ form.resetFields();
|
|
|
+};
|
|
|
+
|
|
|
+// 打开编辑机构弹窗
|
|
|
+const handleEdit = (record: Organization) => {
|
|
|
+ setCurrentOrg(record);
|
|
|
+ setModalVisible(true);
|
|
|
+ form.setFieldsValue(record);
|
|
|
+};
|
|
|
+
|
|
|
+// 删除机构
|
|
|
+const handleDelete = async (id: number) => {
|
|
|
+ Modal.confirm({
|
|
|
+ title: '确认删除',
|
|
|
+ content: '确定要删除该机构吗?',
|
|
|
+ onOk: async () => {
|
|
|
+ try {
|
|
|
+ await organizationAPI.deleteOrganization(id);
|
|
|
+ message.success('删除成功');
|
|
|
+ loadOrganizations();
|
|
|
+ } catch (err) {
|
|
|
+ message.error('删除失败');
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+};
|
|
|
+
|
|
|
+// 提交表单
|
|
|
+const handleSubmit = async () => {
|
|
|
+ try {
|
|
|
+ const values = await form.validateFields();
|
|
|
+ if (currentOrg?.id) {
|
|
|
+ // 更新机构
|
|
|
+ await organizationAPI.updateOrganization(currentOrg.id, values);
|
|
|
+ message.success('更新成功');
|
|
|
+ } else {
|
|
|
+ // 创建机构
|
|
|
+ await organizationAPI.createOrganization(values);
|
|
|
+ message.success('创建成功');
|
|
|
+ }
|
|
|
+ setModalVisible(false);
|
|
|
+ loadOrganizations();
|
|
|
+ } catch (err) {
|
|
|
+ console.error(err);
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+const columns = [
|
|
|
+ {
|
|
|
+ title: '机构名称',
|
|
|
+ dataIndex: 'name',
|
|
|
+ key: 'name',
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: '机构编码',
|
|
|
+ dataIndex: 'code',
|
|
|
+ key: 'code',
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: '描述',
|
|
|
+ dataIndex: 'description',
|
|
|
+ key: 'description',
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: '操作',
|
|
|
+ key: 'action',
|
|
|
+ render: (_: any, record: any) => (
|
|
|
+ <Space size="middle">
|
|
|
+ <Button size="small" onClick={() => handleEdit(record)}>
|
|
|
+ 编辑
|
|
|
+ </Button>
|
|
|
+ <Button
|
|
|
+ size="small"
|
|
|
+ danger
|
|
|
+ onClick={() => handleDelete(record.id)}
|
|
|
+ >
|
|
|
+ 删除
|
|
|
+ </Button>
|
|
|
+ </Space>
|
|
|
+ ),
|
|
|
+ },
|
|
|
+];
|
|
|
+
|
|
|
+return (
|
|
|
+ <div>
|
|
|
+ <Title level={2}>机构管理</Title>
|
|
|
+
|
|
|
+ <div className="bg-white rounded-lg shadow p-6">
|
|
|
+ <div className="flex justify-between mb-4">
|
|
|
+ <Button type="primary" onClick={handleCreate}>
|
|
|
+ 新增机构
|
|
|
+ </Button>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <Tabs defaultActiveKey="1">
|
|
|
+ <Tabs.TabPane tab="树形展示" key="1">
|
|
|
+ <TreeSelect
|
|
|
+ treeData={treeData}
|
|
|
+ placeholder="请选择机构"
|
|
|
+ treeDefaultExpandAll
|
|
|
+ className="w-full"
|
|
|
+ dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
|
|
|
+ fieldNames={{ label: 'name', value: 'id', children: 'children' }}
|
|
|
+ onSelect={(value, node) => {
|
|
|
+ const org = organizations.find(o => o.id === value);
|
|
|
+ if (org) handleEdit(org);
|
|
|
+ }}
|
|
|
+ />
|
|
|
+ </Tabs.TabPane>
|
|
|
+ <Tabs.TabPane tab="表格展示" key="2">
|
|
|
+ <Table
|
|
|
+ dataSource={organizations}
|
|
|
+ rowKey="id"
|
|
|
+ loading={loading}
|
|
|
+ pagination={false}
|
|
|
+ columns={columns}
|
|
|
+ />
|
|
|
+ </Tabs.TabPane>
|
|
|
+ </Tabs>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ {/* 新增/编辑弹窗 */}
|
|
|
+ <Modal
|
|
|
+ title={currentOrg?.id ? '编辑机构' : '新增机构'}
|
|
|
+ open={modalVisible}
|
|
|
+ onOk={handleSubmit}
|
|
|
+ onCancel={() => setModalVisible(false)}
|
|
|
+ confirmLoading={loading}
|
|
|
+ >
|
|
|
+ <Form form={form} layout="vertical">
|
|
|
+ <Form.Item
|
|
|
+ name="name"
|
|
|
+ label="机构名称"
|
|
|
+ rules={[{ required: true, message: '请输入机构名称' }]}
|
|
|
+ >
|
|
|
+ <Input placeholder="请输入机构名称" />
|
|
|
+ </Form.Item>
|
|
|
+
|
|
|
+ <Form.Item
|
|
|
+ name="code"
|
|
|
+ label="机构编码"
|
|
|
+ rules={[{ required: true, message: '请输入机构编码' }]}
|
|
|
+ >
|
|
|
+ <Input placeholder="请输入机构编码" />
|
|
|
+ </Form.Item>
|
|
|
+
|
|
|
+ <Form.Item
|
|
|
+ name="parentId"
|
|
|
+ label="上级机构"
|
|
|
+ >
|
|
|
+ <TreeSelect
|
|
|
+ treeData={treeData}
|
|
|
+ placeholder="请选择上级机构"
|
|
|
+ allowClear
|
|
|
+ treeDefaultExpandAll
|
|
|
+ dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
|
|
|
+ />
|
|
|
+ </Form.Item>
|
|
|
+
|
|
|
+ <Form.Item
|
|
|
+ name="description"
|
|
|
+ label="机构描述"
|
|
|
+ >
|
|
|
+ <Input.TextArea placeholder="请输入机构描述" rows={3} />
|
|
|
+ </Form.Item>
|
|
|
+ </Form>
|
|
|
+ </Modal>
|
|
|
+ </div>
|
|
|
+);
|
|
|
+};
|