pages_organization.tsx 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. import React, { useState, useEffect } from 'react';
  2. import {
  3. Layout, Menu, Button, Table, Space,
  4. Form, Input, Select, TreeSelect, message, Modal,
  5. Card, Spin, Row, Col, Breadcrumb, Avatar,
  6. Dropdown, ConfigProvider, theme, Typography,
  7. Switch, Badge, Image, Upload, Divider, Descriptions,
  8. Popconfirm, Tag, Statistic, DatePicker, Radio, Progress, Tabs, List, Alert, Collapse, Empty, Drawer, Tree
  9. } from 'antd';
  10. import 'dayjs/locale/zh-cn';
  11. import {
  12. organizationAPI,
  13. } from './api.ts';
  14. import type { Organization } from '../share/types.ts';
  15. const { Title } = Typography;
  16. // 系统管理页面 - 机构管理
  17. export const OrganizationPage = () => {
  18. const [organizations, setOrganizations] = useState<Organization[]>([]);
  19. const [treeData, setTreeData] = useState<Organization[]>([]);
  20. const [loading, setLoading] = useState(false);
  21. const [modalVisible, setModalVisible] = useState(false);
  22. const [currentOrg, setCurrentOrg] = useState<Organization | null>(null);
  23. const [form] = Form.useForm();
  24. // 加载机构数据
  25. useEffect(() => {
  26. loadOrganizations();
  27. }, []);
  28. const loadOrganizations = async () => {
  29. setLoading(true);
  30. try {
  31. const res = await organizationAPI.getOrganizations();
  32. setTreeData(res.data);
  33. // 将树形数据转换为扁平列表
  34. const flattenOrganizations = flattenTree(res.data);
  35. setOrganizations(flattenOrganizations);
  36. } catch (err) {
  37. message.error('加载机构数据失败');
  38. console.error(err);
  39. } finally {
  40. setLoading(false);
  41. }
  42. };
  43. // 树形数据扁平化
  44. // 转换Organization为TreeSelect需要的格式
  45. const convertToTreeData = (orgs: Organization[]): Array<{value: string; title: string; children?: any}> => {
  46. return orgs.map(org => ({
  47. value: String(org.id),
  48. title: org.name,
  49. children: org.children ? convertToTreeData(org.children) : undefined,
  50. ...org
  51. }));
  52. };
  53. const flattenTree = (tree: Organization[]): Organization[] => {
  54. return tree.reduce((acc, node) => {
  55. acc.push({
  56. id: node.id,
  57. name: node.name,
  58. code: node.code,
  59. parentId: node.parentId,
  60. description: node.description
  61. });
  62. if (node.children) {
  63. acc.push(...flattenTree(node.children));
  64. }
  65. return acc;
  66. }, [] as Organization[]);
  67. };
  68. // 打开创建机构弹窗
  69. const handleCreate = () => {
  70. setCurrentOrg(null);
  71. setModalVisible(true);
  72. form.resetFields();
  73. };
  74. // 打开编辑机构弹窗
  75. const handleEdit = (record: Organization) => {
  76. setCurrentOrg(record);
  77. setModalVisible(true);
  78. form.setFieldsValue(record);
  79. };
  80. // 删除机构
  81. const handleDelete = async (id: number) => {
  82. Modal.confirm({
  83. title: '确认删除',
  84. content: '确定要删除该机构吗?',
  85. onOk: async () => {
  86. try {
  87. await organizationAPI.deleteOrganization(id);
  88. message.success('删除成功');
  89. loadOrganizations();
  90. } catch (err) {
  91. message.error('删除失败');
  92. }
  93. }
  94. });
  95. };
  96. // 提交表单
  97. const handleSubmit = async () => {
  98. try {
  99. const values = await form.validateFields();
  100. if (currentOrg?.id) {
  101. // 更新机构
  102. await organizationAPI.updateOrganization(currentOrg.id, values);
  103. message.success('更新成功');
  104. } else {
  105. // 创建机构
  106. await organizationAPI.createOrganization(values);
  107. message.success('创建成功');
  108. }
  109. setModalVisible(false);
  110. loadOrganizations();
  111. } catch (err) {
  112. console.error(err);
  113. }
  114. };
  115. const columns = [
  116. {
  117. title: '机构名称',
  118. dataIndex: 'name',
  119. key: 'name',
  120. },
  121. {
  122. title: '机构编码',
  123. dataIndex: 'code',
  124. key: 'code',
  125. },
  126. {
  127. title: '描述',
  128. dataIndex: 'description',
  129. key: 'description',
  130. },
  131. {
  132. title: '操作',
  133. key: 'action',
  134. render: (_: any, record: any) => (
  135. <Space size="middle">
  136. <Button size="small" onClick={() => handleEdit(record)}>
  137. 编辑
  138. </Button>
  139. <Button
  140. size="small"
  141. danger
  142. onClick={() => handleDelete(record.id)}
  143. >
  144. 删除
  145. </Button>
  146. </Space>
  147. ),
  148. },
  149. ];
  150. return (
  151. <div>
  152. <Title level={2}>机构管理</Title>
  153. <div className="bg-white rounded-lg shadow p-6">
  154. <div className="flex justify-between mb-4">
  155. <Button type="primary" onClick={handleCreate}>
  156. 新增机构
  157. </Button>
  158. </div>
  159. <Tabs defaultActiveKey="1">
  160. <Tabs.TabPane tab="树形展示" key="1">
  161. <TreeSelect
  162. treeData={treeData}
  163. placeholder="请选择机构"
  164. treeDefaultExpandAll
  165. className="w-full"
  166. dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
  167. fieldNames={{ label: 'name', value: 'id', children: 'children' }}
  168. onSelect={(value, node) => {
  169. const org = organizations.find(o => o.id === value);
  170. if (org) handleEdit(org);
  171. }}
  172. />
  173. </Tabs.TabPane>
  174. <Tabs.TabPane tab="表格展示" key="2">
  175. <Table
  176. dataSource={organizations}
  177. rowKey="id"
  178. loading={loading}
  179. pagination={false}
  180. columns={columns}
  181. />
  182. </Tabs.TabPane>
  183. </Tabs>
  184. </div>
  185. {/* 新增/编辑弹窗 */}
  186. <Modal
  187. title={currentOrg?.id ? '编辑机构' : '新增机构'}
  188. open={modalVisible}
  189. onOk={handleSubmit}
  190. onCancel={() => setModalVisible(false)}
  191. confirmLoading={loading}
  192. >
  193. <Form form={form} layout="vertical">
  194. <Form.Item
  195. name="name"
  196. label="机构名称"
  197. rules={[{ required: true, message: '请输入机构名称' }]}
  198. >
  199. <Input placeholder="请输入机构名称" />
  200. </Form.Item>
  201. <Form.Item
  202. name="code"
  203. label="机构编码"
  204. rules={[{ required: true, message: '请输入机构编码' }]}
  205. >
  206. <Input placeholder="请输入机构编码" />
  207. </Form.Item>
  208. <Form.Item
  209. name="parentId"
  210. label="上级机构"
  211. >
  212. <TreeSelect
  213. treeData={treeData}
  214. placeholder="请选择上级机构"
  215. allowClear
  216. treeDefaultExpandAll
  217. dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
  218. />
  219. </Form.Item>
  220. <Form.Item
  221. name="description"
  222. label="机构描述"
  223. >
  224. <Input.TextArea placeholder="请输入机构描述" rows={3} />
  225. </Form.Item>
  226. </Form>
  227. </Modal>
  228. </div>
  229. );
  230. };