index.tsx 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936
  1. import {
  2. apiInterceptors,
  3. createEvaluations,
  4. delDataSet,
  5. delEvaluation,
  6. downloadDataSet,
  7. downloadEvaluation,
  8. getAppList,
  9. getDataSets,
  10. getEvaluations,
  11. getMetrics,
  12. getSpaceList,
  13. showEvaluation,
  14. updateEvaluations,
  15. uploadDataSetsContent,
  16. uploadDataSetsFile,
  17. } from '@/client/api';
  18. import { InfoCircleOutlined, UploadOutlined } from '@ant-design/icons';
  19. import { useRequest } from 'ahooks';
  20. import type { TableProps } from 'antd';
  21. import {
  22. Badge,
  23. Button,
  24. ConfigProvider,
  25. Form,
  26. Input,
  27. Modal,
  28. Popconfirm,
  29. Segmented,
  30. Select,
  31. Space,
  32. Statistic,
  33. Table,
  34. Tag,
  35. Tooltip,
  36. Upload,
  37. message,
  38. } from 'antd';
  39. import { valueType } from 'antd/es/statistic/utils';
  40. import { useMemo, useState } from 'react';
  41. const { TextArea } = Input;
  42. const { useWatch } = Form;
  43. interface DataSetItemType {
  44. code: string;
  45. name: string;
  46. file_type: string;
  47. storage_type: string;
  48. storage_position: string;
  49. datasets_count: string;
  50. have_answer: boolean;
  51. members: string;
  52. user_name: string;
  53. user_id: string;
  54. sys_code: string;
  55. gmt_create: string;
  56. gmt_modified: string;
  57. }
  58. interface EvaluationItemType {
  59. evaluate_code: string;
  60. scene_key: string;
  61. scene_value: string;
  62. datasets: string;
  63. evaluate_metrics: string;
  64. context: object;
  65. user_name: string;
  66. user_id: string;
  67. sys_code: string;
  68. parallel_num: string;
  69. state: string;
  70. result: string;
  71. average_score: string;
  72. log_info: string;
  73. gmt_create: string;
  74. gmt_modified: string;
  75. }
  76. const Evaluation = () => {
  77. const [isModalOpen, setIsModalOpen] = useState(false);
  78. const [isDataSetModalOpen, setIsDataSetModalOpen] = useState(false);
  79. const [evaluationList, setEvaluationList] = useState<EvaluationItemType[]>([]);
  80. const [evaluationTotal, setEvaluationTotal] = useState<number>(0);
  81. const [dataSetsTotal, setDataSetsTotal] = useState<number>(0);
  82. const [sceneValueOptions, setSceneValueOptions] = useState<{ label: string; value: string }[]>();
  83. const [metricOptions, setMetricOptions] = useState<{ label: string; value: string }[]>();
  84. const [sceneValueOptionLoading, setSceneValueOptionLoading] = useState(false);
  85. const [currentTable, setCurrentTable] = useState('evaluations');
  86. const [isModalVisible, setIsModalVisible] = useState(false);
  87. const [isAddDataSet, setIsAddDataSet] = useState(true);
  88. const [evaluationShowData, setEvaluationShowData] = useState<Record<string, string>[]>([{}]);
  89. const [storageTypeOptions, _] = useState<{ label: string; value: string }[]>();
  90. const [dataSetsList, setDataSetsList] = useState<DataSetItemType[]>([]);
  91. const [currentEvaluationCode, setCurrentEvaluationCode] = useState<string>('');
  92. const [dataSetModalLoading, setDataSetModalLoading] = useState(false);
  93. const [evaluationModalLoading, setEvaluationModalLoading] = useState(false);
  94. const [commonLoading, setCommonLoading] = useState(false);
  95. const dataSetsOptions = useMemo(() => {
  96. return dataSetsList?.map(item => {
  97. return {
  98. label: item?.name,
  99. value: item?.code,
  100. };
  101. });
  102. }, [dataSetsList]);
  103. const [form] = Form.useForm();
  104. const [dataSetForm] = Form.useForm();
  105. //getMetrics
  106. const { run: runGetMetrics, loading: getMetricsLoading } = useRequest(
  107. async params => {
  108. const [_, data] = await apiInterceptors(getMetrics(params));
  109. return data;
  110. },
  111. {
  112. manual: true,
  113. onSuccess: data => {
  114. setMetricOptions(
  115. data?.map((i: Record<string, string>) => {
  116. return { label: i.describe, value: i.name };
  117. }),
  118. );
  119. },
  120. },
  121. );
  122. //showEvaluation
  123. const { run: runShowEvaluation, loading: showEvaluationLoading } = useRequest(
  124. async params => {
  125. const [_, data] = await apiInterceptors(showEvaluation(params));
  126. return data;
  127. },
  128. {
  129. manual: true,
  130. onSuccess: data => {
  131. if (data && data.length) {
  132. setEvaluationShowData(data);
  133. setIsModalVisible(true);
  134. }
  135. },
  136. },
  137. );
  138. // TODO: unuesed function
  139. // // getStorageTypes
  140. // const { run: runGetStorageTypes } = useRequest(
  141. // async () => {
  142. // const [_, data] = await apiInterceptors(getStorageTypes());
  143. // return data;
  144. // },
  145. // {
  146. // onSuccess: data => {
  147. // data &&
  148. // setStorageTypeOptions(
  149. // data.map((i: Record<string, string>[]) => {
  150. // const [k, v] = Object.entries(i)[0];
  151. // return { label: v, value: k };
  152. // }),
  153. // );
  154. // },
  155. // },
  156. // );
  157. const {
  158. run: runGetEvaluations,
  159. loading: getEvaluationsLoading,
  160. refresh: getEvaluationsRefresh,
  161. } = useRequest(
  162. async (page = 1, page_size = 10) => {
  163. const [_, data] = await apiInterceptors(
  164. getEvaluations({
  165. page,
  166. page_size,
  167. }),
  168. );
  169. return data;
  170. },
  171. {
  172. // manual: true,
  173. onSuccess: data => {
  174. setEvaluationList(data?.items);
  175. setEvaluationTotal(data?.total_count);
  176. },
  177. },
  178. );
  179. const {
  180. run: runGetDataSets,
  181. loading: getDataSetsLoading,
  182. refresh: getDataSetsRefresh,
  183. } = useRequest(
  184. async (page = 1, page_size = 10) => {
  185. const [_, data] = await apiInterceptors(
  186. getDataSets({
  187. page,
  188. page_size,
  189. }),
  190. );
  191. return data;
  192. },
  193. {
  194. // manual: true,
  195. onSuccess: data => {
  196. setDataSetsList(data?.items);
  197. setDataSetsTotal(data?.total_count);
  198. },
  199. },
  200. );
  201. // TODO: unuesed function
  202. // // uploadDataSets
  203. // const {
  204. // run: runUploadDataSets,
  205. // loading: uploadDataSetsLoading,
  206. // refresh: uploadDataSetsRefresh,
  207. // } = useRequest(
  208. // async data => {
  209. // const [_, res] = await apiInterceptors(
  210. // uploadDataSets({
  211. // ...data,
  212. // }),
  213. // );
  214. // return res;
  215. // },
  216. // {
  217. // manual: true,
  218. // onSuccess: res => {
  219. // setEvaluationList(res?.items);
  220. // },
  221. // },
  222. // );
  223. const columns: TableProps<DataSetItemType>['columns'] = [
  224. {
  225. title: '名称',
  226. dataIndex: 'name',
  227. key: 'name',
  228. width: '10%',
  229. fixed: 'left',
  230. },
  231. {
  232. title: '编码',
  233. dataIndex: 'code',
  234. key: 'code',
  235. width: '20%',
  236. // render: (text) => <a>{text}</a>,
  237. },
  238. {
  239. title: '储存方式',
  240. dataIndex: 'storage_type',
  241. key: 'storage_type',
  242. },
  243. {
  244. title: '数据集数量',
  245. dataIndex: 'datasets_count',
  246. key: 'datasets_count',
  247. },
  248. {
  249. title: '创建时间',
  250. dataIndex: 'gmt_create',
  251. key: 'gmt_create',
  252. },
  253. {
  254. title: '成员',
  255. dataIndex: 'members',
  256. key: 'members',
  257. width: '10%',
  258. render: text => {
  259. return text?.split(',').map((item: string) => {
  260. return <Tag key={item}>{item}</Tag>;
  261. });
  262. },
  263. },
  264. {
  265. title: '更新时间',
  266. dataIndex: 'gmt_modified',
  267. key: 'gmt_modified',
  268. },
  269. {
  270. title: 'Action',
  271. key: 'action',
  272. render: (_, record) => (
  273. <Space size='middle'>
  274. <Popconfirm
  275. title='确认删除吗'
  276. onConfirm={async () => {
  277. const [, , res] = await apiInterceptors(
  278. delDataSet({
  279. code: record?.code,
  280. }),
  281. );
  282. if (res?.success == true) {
  283. message.success('删除成功');
  284. getDataSetsRefresh();
  285. }
  286. }}
  287. >
  288. <Button type='link'>删除</Button>
  289. </Popconfirm>
  290. <Button
  291. type='link'
  292. onClick={() => {
  293. setIsAddDataSet(false);
  294. setIsDataSetModalOpen(true);
  295. setCurrentEvaluationCode(record?.code);
  296. dataSetForm.setFieldsValue({
  297. dataset_name: record?.name,
  298. members: record?.members?.split(','),
  299. });
  300. }}
  301. >
  302. 编辑
  303. </Button>
  304. <Button
  305. type='link'
  306. loading={commonLoading}
  307. onClick={async () => {
  308. setCommonLoading(true);
  309. const response = await downloadDataSet({
  310. code: record?.code,
  311. });
  312. const contentType = response.headers['content-type'];
  313. if (contentType.includes('application/json')) {
  314. // 如果是 JSON,解析错误信息
  315. const reader = new FileReader();
  316. reader.onload = () => {
  317. try {
  318. const error = JSON.parse(reader.result as string);
  319. message.error(error.err_msg);
  320. // 在页面或通知系统中展示错误信息
  321. } catch (parseError) {
  322. console.error('Failed to parse error response:', parseError);
  323. }
  324. };
  325. reader.readAsText(response.data as any);
  326. } else {
  327. // 从响应头中获取文件名
  328. const contentDisposition = response.headers['content-disposition'];
  329. let filename = 'downloaded_file.xlsx';
  330. if (contentDisposition) {
  331. const match = contentDisposition.match(/filename\*?="?(.+)"/);
  332. if (match[1]) {
  333. filename = decodeURIComponent(match[1]);
  334. }
  335. }
  336. // 创建 URL 并触发下载
  337. const url = window.URL.createObjectURL(
  338. new Blob([response.data as any], {
  339. type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
  340. }),
  341. );
  342. const a = document.createElement('a');
  343. a.href = url;
  344. a.download = filename;
  345. document.body.appendChild(a);
  346. a.click();
  347. a.remove();
  348. window.URL.revokeObjectURL(url); // 释放内存
  349. }
  350. setCommonLoading(false);
  351. }}
  352. >
  353. 下载
  354. </Button>
  355. </Space>
  356. ),
  357. },
  358. ];
  359. /* evaluations Columns
  360. */
  361. const evaluationsColumns: TableProps<EvaluationItemType>['columns'] = [
  362. {
  363. title: '数据集名称',
  364. dataIndex: 'datasets_name',
  365. key: 'datasets_name',
  366. fixed: 'left',
  367. width: '15%',
  368. render: text => (
  369. <span
  370. style={{
  371. textWrap: 'nowrap',
  372. maxWidth: '300px',
  373. }}
  374. >
  375. {text}
  376. </span>
  377. ),
  378. },
  379. {
  380. title: '测评状态',
  381. dataIndex: 'state',
  382. key: 'state',
  383. render: text => {
  384. return <Badge style={{ textWrap: 'nowrap' }} status={text == 'failed' ? 'error' : 'success'} text={text} />;
  385. },
  386. },
  387. {
  388. title: '测评编码',
  389. dataIndex: 'evaluate_code',
  390. key: 'evaluate_code',
  391. },
  392. {
  393. title: '场景',
  394. dataIndex: 'scene_key',
  395. key: 'scene_key',
  396. },
  397. {
  398. title: '测评指标',
  399. dataIndex: 'evaluate_metrics',
  400. key: 'evaluate_metrics',
  401. },
  402. {
  403. title: '创建时间',
  404. dataIndex: 'gmt_create',
  405. key: 'gmt_create',
  406. },
  407. {
  408. title: '更新时间',
  409. dataIndex: 'gmt_modified',
  410. key: 'gmt_modified',
  411. },
  412. Table.EXPAND_COLUMN,
  413. {
  414. title: (
  415. <span className='w-[50px]'>
  416. <span className='text-nowrap'>详情</span>
  417. <Tooltip placement='topLeft' title='查看日志与评分'>
  418. <InfoCircleOutlined />
  419. </Tooltip>
  420. </span>
  421. ),
  422. render: () => (
  423. <div
  424. style={{
  425. minWidth: '50px',
  426. }}
  427. ></div>
  428. ),
  429. },
  430. {
  431. title: '测评结果',
  432. key: 'result',
  433. render: (_, record) => (
  434. <>
  435. <Button
  436. type='link'
  437. loading={showEvaluationLoading}
  438. onClick={async () => {
  439. runShowEvaluation({
  440. evaluate_code: record?.evaluate_code,
  441. });
  442. }}
  443. >
  444. 评分明细
  445. </Button>
  446. <Button
  447. type='link'
  448. loading={commonLoading}
  449. onClick={async () => {
  450. setCommonLoading(true);
  451. const response = await downloadEvaluation({
  452. evaluate_code: record?.evaluate_code,
  453. });
  454. const contentType = response.headers['content-type'];
  455. if (contentType.includes('application/json')) {
  456. // 如果是 JSON,解析错误信息
  457. const reader = new FileReader();
  458. reader.onload = () => {
  459. try {
  460. const error = JSON.parse(reader.result as string);
  461. message.error(error.err_msg);
  462. // 在页面或通知系统中展示错误信息
  463. } catch (parseError) {
  464. console.error('Failed to parse error response:', parseError);
  465. }
  466. };
  467. reader.readAsText(response.data as any);
  468. } else {
  469. // 从响应头中获取文件名
  470. const contentDisposition = response.headers['content-disposition'];
  471. let filename = 'downloaded_file.xlsx';
  472. if (contentDisposition) {
  473. const match = contentDisposition.match(/filename\*?="?(.+)"/);
  474. if (match[1]) {
  475. filename = decodeURIComponent(match[1]);
  476. }
  477. }
  478. // 创建 URL 并触发下载
  479. const url = window.URL.createObjectURL(
  480. new Blob([response.data as any], {
  481. type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
  482. }),
  483. );
  484. const a = document.createElement('a');
  485. a.href = url;
  486. a.download = filename;
  487. document.body.appendChild(a);
  488. a.click();
  489. a.remove();
  490. window.URL.revokeObjectURL(url); // 释放内存
  491. }
  492. setCommonLoading(false);
  493. }}
  494. >
  495. 下载
  496. </Button>
  497. </>
  498. ),
  499. },
  500. {
  501. title: '操作',
  502. key: 'action',
  503. width: '25%',
  504. render: (_, record) => (
  505. <>
  506. <Popconfirm
  507. title='确认删除吗'
  508. onConfirm={async () => {
  509. const [, , res] = await apiInterceptors(
  510. delEvaluation({
  511. evaluation_code: record?.evaluate_code,
  512. }),
  513. );
  514. if (res?.success == true) {
  515. message.success('删除成功');
  516. getEvaluationsRefresh();
  517. }
  518. }}
  519. >
  520. <Button type='link'>删除</Button>
  521. </Popconfirm>
  522. </>
  523. ),
  524. },
  525. ];
  526. const handleModalClose = () => {
  527. setIsModalVisible(false);
  528. };
  529. return (
  530. <ConfigProvider
  531. theme={{
  532. components: {
  533. Segmented: {
  534. itemSelectedBg: '#2867f5',
  535. itemSelectedColor: 'white',
  536. },
  537. },
  538. }}
  539. >
  540. <div className='flex flex-col h-full w-full dark:bg-gradient-dark bg-gradient-light bg-cover bg-center'>
  541. <div className='px-6 py-2 overflow-y-auto'>
  542. <Segmented
  543. className='backdrop-filter backdrop-blur-lg bg-white bg-opacity-30 border-2 border-white rounded-lg shadow p-1 dark:border-[#6f7f95] dark:bg-[#6f7f95] dark:bg-opacity-60'
  544. options={[
  545. {
  546. label: '评测数据',
  547. value: 'evaluations',
  548. },
  549. {
  550. label: '数据集',
  551. value: 'dataSet',
  552. },
  553. ]}
  554. onChange={type => {
  555. setCurrentTable(type as string);
  556. }}
  557. value={currentTable}
  558. />
  559. {currentTable === 'dataSet' && (
  560. <>
  561. <div className='flex flex-row-reverse mb-4'>
  562. <Button
  563. className='border-none text-white bg-button-gradient h-full'
  564. onClick={() => {
  565. setIsDataSetModalOpen(true);
  566. setIsAddDataSet(true);
  567. }}
  568. >
  569. 添加数据集
  570. </Button>
  571. </div>
  572. <Table
  573. pagination={{
  574. total: dataSetsTotal,
  575. onChange(page) {
  576. runGetDataSets(page);
  577. },
  578. }}
  579. scroll={{ x: 1300 }}
  580. loading={getDataSetsLoading}
  581. columns={columns}
  582. dataSource={dataSetsList}
  583. />
  584. </>
  585. )}
  586. {currentTable === 'evaluations' && (
  587. <>
  588. <div className='flex flex-row-reverse mb-4'>
  589. <Button
  590. className='border-none text-white bg-button-gradient h-full'
  591. onClick={() => {
  592. setIsModalOpen(true);
  593. }}
  594. >
  595. 发起评测
  596. </Button>
  597. </div>
  598. <Table
  599. pagination={{
  600. total: evaluationTotal,
  601. onChange(page) {
  602. runGetEvaluations(page);
  603. },
  604. }}
  605. rowKey={record => record.evaluate_code}
  606. expandable={{
  607. expandedRowRender: ({ average_score, log_info }) => {
  608. return (
  609. <div className='flex flex-col gap-2'>
  610. {(() => {
  611. if (!average_score) return <></>;
  612. try {
  613. const jsonData = JSON.parse(average_score);
  614. return (
  615. <div className='flex flex-row gap-1'>
  616. {Object.entries(jsonData)?.map(item => {
  617. const [k, v] = item;
  618. return <Statistic title={k} key={k} value={v as valueType} />;
  619. })}
  620. </div>
  621. );
  622. } catch {
  623. return <></>;
  624. }
  625. })()}
  626. {log_info && (
  627. <div>
  628. <span className='text-gray-500 text-sm'>log:</span>
  629. <span>{log_info}</span>
  630. </div>
  631. )}
  632. </div>
  633. );
  634. },
  635. }}
  636. scroll={{ x: '100%' }}
  637. loading={getEvaluationsLoading}
  638. columns={evaluationsColumns}
  639. dataSource={evaluationList}
  640. />
  641. </>
  642. )}
  643. <Modal
  644. title='发起测评'
  645. open={isModalOpen}
  646. onOk={async () => {
  647. const values = await form.validateFields();
  648. setEvaluationModalLoading(true);
  649. if (values) {
  650. const [, , res] = await apiInterceptors(
  651. createEvaluations({
  652. ...values,
  653. }),
  654. );
  655. if (res?.success) {
  656. message.success('发起成功');
  657. getEvaluationsRefresh();
  658. form.resetFields();
  659. }
  660. }
  661. setIsModalOpen(false);
  662. setEvaluationModalLoading(false);
  663. }}
  664. confirmLoading={evaluationModalLoading}
  665. onCancel={() => {
  666. setIsModalOpen(false);
  667. }}
  668. >
  669. <Form
  670. name='basic'
  671. form={form}
  672. initialValues={{ remember: true }}
  673. autoComplete='off'
  674. labelCol={{ span: 4 }}
  675. wrapperCol={{ span: 20 }}
  676. >
  677. <Form.Item name='scene_key' label='场景类型' rules={[{ required: true }]}>
  678. <Select
  679. options={[
  680. {
  681. value: 'recall',
  682. label: 'recall',
  683. },
  684. {
  685. value: 'app',
  686. label: 'app',
  687. },
  688. ]}
  689. onChange={async value => {
  690. //getSceneValueOptions
  691. setSceneValueOptionLoading(true);
  692. form.setFieldValue('scene_value', '');
  693. if (value === 'recall') {
  694. const res = await getSpaceList();
  695. if (res.data.success) {
  696. setSceneValueOptions(
  697. res.data.data.map(i => ({
  698. label: i.name,
  699. value: i.id.toString(),
  700. })),
  701. );
  702. }
  703. } else {
  704. const res = await getAppList({});
  705. if (res.data.success) {
  706. setSceneValueOptions(
  707. res.data.data.app_list.map(i => ({
  708. label: i.app_name,
  709. value: i.app_code,
  710. })),
  711. );
  712. }
  713. }
  714. setSceneValueOptionLoading(false);
  715. }}
  716. ></Select>
  717. </Form.Item>
  718. <Form.Item name='scene_value' label='场景参数' rules={[{ required: true }]}>
  719. <Select
  720. loading={sceneValueOptionLoading}
  721. disabled={sceneValueOptionLoading}
  722. options={sceneValueOptions}
  723. onChange={value => {
  724. if (form.getFieldValue('scene_key')) {
  725. runGetMetrics({
  726. scene_key: form.getFieldValue('scene_key'),
  727. scene_value: value,
  728. });
  729. }
  730. }}
  731. ></Select>
  732. </Form.Item>
  733. <Form.Item name='parallel_num' label='并行参数' rules={[{ required: true }]} initialValue={1}>
  734. <Input></Input>
  735. </Form.Item>
  736. <Form.Item name='datasets' label='数据集' rules={[{ required: true }]}>
  737. <Select options={dataSetsOptions}></Select>
  738. </Form.Item>
  739. <Form.Item
  740. name='evaluate_metrics'
  741. label='评测指标'
  742. rules={[{ required: useWatch('scene_key', form) === 'app' }]}
  743. >
  744. <Select loading={getMetricsLoading} disabled={getMetricsLoading} options={metricOptions}></Select>
  745. </Form.Item>
  746. </Form>
  747. </Modal>
  748. <Modal
  749. title={isAddDataSet ? '添加数据集' : '编辑数据集'}
  750. open={isDataSetModalOpen}
  751. confirmLoading={dataSetModalLoading}
  752. onOk={() => {
  753. dataSetForm.validateFields().then(values => {
  754. setDataSetModalLoading(true);
  755. if (isAddDataSet) {
  756. const storageType = values.storage_type;
  757. if (storageType === 'oss') {
  758. // 创建FormData对象
  759. const formData = new FormData();
  760. formData.append('dataset_name', values.dataset_name);
  761. values.members && formData.append('members', values.members.join(','));
  762. const file = values.doc_file.file; // 获取文件对象
  763. formData.append('doc_file', file, file.name);
  764. uploadDataSetsFile(formData)
  765. .then(response => {
  766. if (response.data.success) {
  767. message.success('上传成功');
  768. runGetDataSets();
  769. } else {
  770. message.error(response.data.err_msg);
  771. }
  772. })
  773. .catch(error => {
  774. console.error('上传失败', error);
  775. message.error(error?.response?.data?.err_msg || '上传失败');
  776. })
  777. .finally(() => {
  778. setIsDataSetModalOpen(false);
  779. setDataSetModalLoading(false);
  780. });
  781. } else if (storageType === 'db') {
  782. uploadDataSetsContent({
  783. dataset_name: values.dataset_name,
  784. members: values.members.join(','),
  785. content: values.content,
  786. })
  787. .then(res => {
  788. if (res.data.success) {
  789. message.success('上传成功');
  790. runGetDataSets();
  791. } else {
  792. message.error(res.data.err_msg);
  793. }
  794. })
  795. .catch(err => {
  796. console.log(err);
  797. message.error(err?.response?.data?.err_msg || '上传失败');
  798. })
  799. .finally(() => {
  800. setIsDataSetModalOpen(false);
  801. setDataSetModalLoading(false);
  802. dataSetForm.resetFields();
  803. });
  804. }
  805. } else {
  806. updateEvaluations({
  807. code: currentEvaluationCode,
  808. members: values.members.join(','),
  809. })
  810. .then(res => {
  811. if (res.data.success) {
  812. message.success('更新成功');
  813. runGetDataSets();
  814. } else {
  815. message.error(res.data.err_msg);
  816. }
  817. })
  818. .catch(err => {
  819. console.log(err);
  820. message.error('更新失败');
  821. })
  822. .finally(() => {
  823. setIsDataSetModalOpen(false);
  824. setDataSetModalLoading(false);
  825. });
  826. }
  827. });
  828. }}
  829. onCancel={() => {
  830. setIsDataSetModalOpen(false);
  831. }}
  832. >
  833. <Form
  834. name='basic'
  835. form={dataSetForm}
  836. initialValues={{ remember: true }}
  837. autoComplete='off'
  838. labelCol={{ span: 4 }}
  839. wrapperCol={{ span: 20 }}
  840. >
  841. <Form.Item name='dataset_name' label='名称' rules={[{ required: true }]}>
  842. <Input disabled={!isAddDataSet} />
  843. </Form.Item>
  844. <Form.Item name='members' label='成员'>
  845. <Select mode='tags' />
  846. </Form.Item>
  847. {isAddDataSet && (
  848. <Form.Item name='storage_type' label='储存类型' rules={[{ required: true }]}>
  849. <Select options={storageTypeOptions} />
  850. </Form.Item>
  851. )}
  852. {useWatch('storage_type', dataSetForm) === 'oss' && isAddDataSet && (
  853. <Form.Item name='doc_file' label='doc_file' rules={[{ required: true }]}>
  854. <Upload
  855. name='dataSet'
  856. maxCount={1}
  857. beforeUpload={file => {
  858. dataSetForm.setFieldsValue({
  859. doc_file: file,
  860. });
  861. return false;
  862. }}
  863. onRemove={() => {
  864. dataSetForm.setFieldsValue({
  865. doc_file: undefined,
  866. });
  867. }}
  868. >
  869. <Button icon={<UploadOutlined />}>Click to Upload</Button>
  870. </Upload>
  871. </Form.Item>
  872. )}
  873. {useWatch('storage_type', dataSetForm) === 'db' && isAddDataSet && (
  874. <Form.Item name='content' label='content' rules={[{ required: true }]}>
  875. <TextArea rows={8} />
  876. </Form.Item>
  877. )}
  878. </Form>
  879. </Modal>
  880. <Modal
  881. title='评分明细'
  882. open={isModalVisible}
  883. onOk={handleModalClose}
  884. onCancel={handleModalClose}
  885. styles={{
  886. body: {
  887. maxHeight: '500px',
  888. overflowY: 'auto',
  889. minWidth: '700px',
  890. },
  891. }}
  892. style={{
  893. minWidth: '750px',
  894. }}
  895. footer={[
  896. <Button key='back' onClick={handleModalClose}>
  897. 返回
  898. </Button>,
  899. ]}
  900. >
  901. <Table
  902. columns={Object.keys(evaluationShowData?.[0]).map(key => ({
  903. title: key,
  904. dataIndex: key,
  905. key,
  906. }))}
  907. style={{
  908. minWidth: '700px',
  909. }}
  910. dataSource={evaluationShowData}
  911. rowKey='code'
  912. pagination={false} // 禁用分页
  913. />
  914. </Modal>
  915. </div>
  916. </div>
  917. </ConfigProvider>
  918. );
  919. };
  920. export default Evaluation;