SolutionDesigns.tsx 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. import React, { useState, useEffect } from 'react';
  2. import { Card, Button, Table, Tag, Space, Modal, message, Progress } from 'antd';
  3. import { PlusOutlined, EditOutlined, DeleteOutlined, EyeOutlined, DownloadOutlined } from '@ant-design/icons';
  4. import { useNavigate } from 'react-router';
  5. import { useAuth } from '../hooks/AuthProvider';
  6. import { solutionDesignsClient } from '@/client/api';
  7. interface SolutionDesign {
  8. id: number;
  9. title: string;
  10. description: string | null;
  11. status: string;
  12. progress: number;
  13. totalChapters: number;
  14. completedChapters: number;
  15. outputFormat: string;
  16. createdAt: string;
  17. updatedAt: string;
  18. }
  19. const SolutionDesignsPage: React.FC = () => {
  20. const navigate = useNavigate();
  21. const { user } = useAuth(); // eslint-disable-line @typescript-eslint/no-unused-vars
  22. const [loading, setLoading] = useState(false);
  23. const [data, setData] = useState<SolutionDesign[]>([]);
  24. const [pagination, setPagination] = useState({
  25. current: 1,
  26. pageSize: 10,
  27. total: 0,
  28. });
  29. const fetchSolutionDesigns = async (page: number = 1, pageSize: number = 10) => {
  30. try {
  31. setLoading(true);
  32. const response = await solutionDesignsClient.$get({
  33. query: {
  34. page,
  35. pageSize,
  36. },
  37. });
  38. if (response.status === 200) {
  39. const result = await response.json();
  40. setData(result.data);
  41. setPagination({
  42. current: result.pagination.current,
  43. pageSize: result.pagination.pageSize,
  44. total: result.pagination.total,
  45. });
  46. }
  47. } catch (error) {
  48. console.error('获取方案设计列表失败:', error);
  49. message.error('获取方案设计列表失败');
  50. } finally {
  51. setLoading(false);
  52. }
  53. };
  54. useEffect(() => {
  55. fetchSolutionDesigns();
  56. }, []);
  57. const handleTableChange = (pagination: any) => {
  58. fetchSolutionDesigns(pagination.current, pagination.pageSize);
  59. };
  60. const handleCreate = () => {
  61. navigate('/admin/solution-designs/create');
  62. };
  63. const handleView = (id: number) => {
  64. navigate(`/admin/solution-designs/${id}`);
  65. };
  66. const handleEdit = (id: number) => {
  67. navigate(`/admin/solution-designs/${id}/edit`);
  68. };
  69. const handleDelete = async (id: number) => {
  70. Modal.confirm({
  71. title: '确认删除',
  72. content: '确定要删除这个方案设计吗?此操作不可恢复。',
  73. onOk: async () => {
  74. try {
  75. const response = await solutionDesignsClient[':id'].$delete({
  76. param: { id: id.toString() },
  77. });
  78. if (response.status === 200) {
  79. message.success('删除成功');
  80. fetchSolutionDesigns(pagination.current, pagination.pageSize);
  81. }
  82. } catch (error) {
  83. console.error('删除方案设计失败:', error);
  84. message.error('删除方案设计失败');
  85. }
  86. },
  87. });
  88. };
  89. const handleGenerate = async (id: number) => {
  90. try {
  91. const response = await solutionDesignsClient[':id'].generate.$post({
  92. param: { id: id.toString() },
  93. });
  94. if (response.status === 200) {
  95. const result = await response.json();
  96. message.success('文档生成成功');
  97. // 处理下载链接
  98. if (result.downloadUrl.startsWith('data:')) {
  99. // Base64数据,直接下载
  100. const link = document.createElement('a');
  101. link.href = result.downloadUrl;
  102. link.download = result.fileName;
  103. link.click();
  104. } else {
  105. // MinIO链接,新窗口打开
  106. window.open(result.downloadUrl, '_blank');
  107. }
  108. }
  109. } catch (error) {
  110. console.error('生成文档失败:', error);
  111. message.error('生成文档失败');
  112. }
  113. };
  114. const columns = [
  115. {
  116. title: '方案标题',
  117. dataIndex: 'title',
  118. key: 'title',
  119. render: (text: string, record: SolutionDesign) => (
  120. <Button type="link" onClick={() => handleView(record.id)}>
  121. {text}
  122. </Button>
  123. ),
  124. },
  125. {
  126. title: '描述',
  127. dataIndex: 'description',
  128. key: 'description',
  129. render: (text: string | null) => text || '-',
  130. },
  131. {
  132. title: '状态',
  133. dataIndex: 'status',
  134. key: 'status',
  135. render: (status: string) => {
  136. const statusConfig = {
  137. draft: { color: 'default', text: '草稿' },
  138. reviewing: { color: 'processing', text: '审核中' },
  139. completed: { color: 'success', text: '已完成' },
  140. };
  141. const config = statusConfig[status as keyof typeof statusConfig] || { color: 'default', text: status };
  142. return <Tag color={config.color}>{config.text}</Tag>;
  143. },
  144. },
  145. {
  146. title: '进度',
  147. dataIndex: 'progress',
  148. key: 'progress',
  149. render: (progress: number, record: SolutionDesign) => (
  150. <Progress
  151. percent={progress}
  152. size="small"
  153. format={() => `${record.completedChapters}/${record.totalChapters}`}
  154. />
  155. ),
  156. },
  157. {
  158. title: '输出格式',
  159. dataIndex: 'outputFormat',
  160. key: 'outputFormat',
  161. render: (format: string) => <Tag>{format.toUpperCase()}</Tag>,
  162. },
  163. {
  164. title: '创建时间',
  165. dataIndex: 'createdAt',
  166. key: 'createdAt',
  167. render: (date: string) => new Date(date).toLocaleString(),
  168. },
  169. {
  170. title: '操作',
  171. key: 'actions',
  172. render: (_: any, record: SolutionDesign) => (
  173. <Space size="middle">
  174. <Button
  175. type="link"
  176. icon={<EyeOutlined />}
  177. onClick={() => handleView(record.id)}
  178. >
  179. 查看
  180. </Button>
  181. <Button
  182. type="link"
  183. icon={<EditOutlined />}
  184. onClick={() => handleEdit(record.id)}
  185. >
  186. 编辑
  187. </Button>
  188. {record.status === 'completed' && (
  189. <Button
  190. type="link"
  191. icon={<DownloadOutlined />}
  192. onClick={() => handleGenerate(record.id)}
  193. >
  194. 下载
  195. </Button>
  196. )}
  197. <Button
  198. type="link"
  199. danger
  200. icon={<DeleteOutlined />}
  201. onClick={() => handleDelete(record.id)}
  202. >
  203. 删除
  204. </Button>
  205. </Space>
  206. ),
  207. },
  208. ];
  209. return (
  210. <div>
  211. <Card
  212. title="方案设计管理"
  213. extra={
  214. <Button type="primary" icon={<PlusOutlined />} onClick={handleCreate}>
  215. 新建方案
  216. </Button>
  217. }
  218. >
  219. <Table
  220. columns={columns}
  221. dataSource={data}
  222. rowKey="id"
  223. loading={loading}
  224. pagination={pagination}
  225. onChange={handleTableChange}
  226. />
  227. </Card>
  228. </div>
  229. );
  230. };
  231. export default SolutionDesignsPage;