You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

index.tsx 7.2 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. import KFIcon from '@/components/KFIcon';
  2. import { ExperimentStatus } from '@/enums';
  3. import { useCheck } from '@/hooks';
  4. import { experimentStatusInfo } from '@/pages/Experiment/status';
  5. import themes from '@/styles/theme.less';
  6. import { type ExperimentInstance } from '@/types';
  7. import { elapsedTime, formatDate } from '@/utils/date';
  8. import { to } from '@/utils/promise';
  9. import { modalConfirm } from '@/utils/ui';
  10. import { DoubleRightOutlined } from '@ant-design/icons';
  11. import { App, Button, Checkbox, ConfigProvider, Typography } from 'antd';
  12. import classNames from 'classnames';
  13. import { useEffect, useMemo } from 'react';
  14. import { ExperimentListType, experimentListConfig } from '../ExperimentList/config';
  15. import styles from './index.less';
  16. type ExperimentInstanceProps = {
  17. type: ExperimentListType;
  18. experimentInsList?: ExperimentInstance[];
  19. experimentInsTotal: number;
  20. onClickInstance?: (instance: ExperimentInstance) => void;
  21. onRemove?: () => void;
  22. onTerminate?: (instance: ExperimentInstance) => void;
  23. onLoadMore?: () => void;
  24. };
  25. function ExperimentInstanceComponent({
  26. type,
  27. experimentInsList,
  28. experimentInsTotal,
  29. onClickInstance,
  30. onRemove,
  31. onTerminate,
  32. onLoadMore,
  33. }: ExperimentInstanceProps) {
  34. const { message } = App.useApp();
  35. const allIntanceIds = useMemo(() => {
  36. return experimentInsList?.map((item) => item.id) || [];
  37. }, [experimentInsList]);
  38. const [
  39. selectedIns,
  40. setSelectedIns,
  41. checked,
  42. indeterminate,
  43. checkAll,
  44. isSingleChecked,
  45. checkSingle,
  46. ] = useCheck(allIntanceIds);
  47. const config = experimentListConfig[type];
  48. useEffect(() => {
  49. // 关闭时清空
  50. if (allIntanceIds.length === 0) {
  51. setSelectedIns([]);
  52. }
  53. }, [allIntanceIds, setSelectedIns]);
  54. // 删除实验实例确认
  55. const handleRemove = (instance: ExperimentInstance) => {
  56. modalConfirm({
  57. title: '确定删除该条实例吗?',
  58. onOk: () => {
  59. deleteExperimentInstance(instance.id);
  60. },
  61. });
  62. };
  63. // 删除实验实例
  64. const deleteExperimentInstance = async (id: number) => {
  65. const request = config.deleteInsReq;
  66. const [res] = await to(request(id));
  67. if (res) {
  68. message.success('删除成功');
  69. onRemove?.();
  70. }
  71. };
  72. // 批量删除实验实例确认
  73. const handleDeleteAll = () => {
  74. modalConfirm({
  75. title: '确定批量删除选中的实例吗?',
  76. onOk: () => {
  77. batchDeleteExperimentInstances();
  78. },
  79. });
  80. };
  81. // 批量删除实验实例
  82. const batchDeleteExperimentInstances = async () => {
  83. const request = config.batchDeleteInsReq;
  84. const [res] = await to(request(selectedIns));
  85. if (res) {
  86. message.success('删除成功');
  87. setSelectedIns([]);
  88. onRemove?.();
  89. }
  90. };
  91. // 终止实验实例
  92. const terminateExperimentInstance = async (instance: ExperimentInstance) => {
  93. const request = config.stopInsReq;
  94. const [res] = await to(request(instance.id));
  95. if (res) {
  96. message.success('终止成功');
  97. onTerminate?.(instance);
  98. }
  99. };
  100. if (!experimentInsList || experimentInsList.length === 0) {
  101. return <div style={{ textAlign: 'center' }}>暂无实验实例</div>;
  102. }
  103. return (
  104. <div>
  105. <div className={styles.tableExpandBox} style={{ paddingBottom: '16px' }}>
  106. <div className={styles.check}>
  107. <Checkbox checked={checked} indeterminate={indeterminate} onChange={checkAll}></Checkbox>
  108. </div>
  109. <div className={styles.index}>序号</div>
  110. <div className={styles.description}>运行时长</div>
  111. <div className={styles.startTime}>开始时间</div>
  112. <div className={styles.status}>状态</div>
  113. <div className={styles.operation}>
  114. <span>操作</span>
  115. {selectedIns.length > 0 && (
  116. <Button
  117. style={{ position: 'absolute', right: '0' }}
  118. color="primary"
  119. variant="filled"
  120. size="small"
  121. onClick={handleDeleteAll}
  122. icon={<KFIcon type="icon-shanchu" />}
  123. >
  124. 删除
  125. </Button>
  126. )}
  127. </div>
  128. </div>
  129. {experimentInsList.map((item, index) => (
  130. <div
  131. key={item.id}
  132. className={classNames(styles.tableExpandBox, styles.tableExpandBoxContent)}
  133. >
  134. <div className={styles.check}>
  135. <Checkbox
  136. checked={isSingleChecked(item.id)}
  137. onChange={() => checkSingle(item.id)}
  138. ></Checkbox>
  139. </div>
  140. <a
  141. className={styles.index}
  142. style={{ padding: '0 16px' }}
  143. onClick={() => onClickInstance?.(item)}
  144. >
  145. {index + 1}
  146. </a>
  147. <div className={styles.description}>
  148. {elapsedTime(item.create_time, item.finish_time)}
  149. </div>
  150. <div className={styles.startTime}>
  151. <Typography.Text ellipsis={{ tooltip: formatDate(item.create_time) }}>
  152. {formatDate(item.create_time)}
  153. </Typography.Text>
  154. </div>
  155. <div className={styles.statusBox}>
  156. <img
  157. style={{ width: '17px', marginRight: '7px' }}
  158. src={experimentStatusInfo[item.status as ExperimentStatus]?.icon}
  159. draggable={false}
  160. alt=""
  161. />
  162. <span
  163. style={{ color: experimentStatusInfo[item.status as ExperimentStatus]?.color }}
  164. className={styles.statusIcon}
  165. >
  166. {experimentStatusInfo[item.status as ExperimentStatus]?.label}
  167. </span>
  168. </div>
  169. <div className={styles.operation}>
  170. <Button
  171. type="link"
  172. size="small"
  173. key="stop"
  174. disabled={
  175. item.status === ExperimentStatus.Succeeded ||
  176. item.status === ExperimentStatus.Failed ||
  177. item.status === ExperimentStatus.Terminated
  178. }
  179. icon={<KFIcon type="icon-zhongzhi" />}
  180. onClick={() => terminateExperimentInstance(item)}
  181. >
  182. 终止
  183. </Button>
  184. <ConfigProvider
  185. theme={{
  186. token: {
  187. colorLink: themes['warningColor'],
  188. },
  189. }}
  190. >
  191. <Button
  192. type="link"
  193. size="small"
  194. key="batchRemove"
  195. disabled={
  196. item.status === ExperimentStatus.Running ||
  197. item.status === ExperimentStatus.Pending
  198. }
  199. icon={<KFIcon type="icon-shanchu" />}
  200. onClick={() => handleRemove(item)}
  201. >
  202. 删除
  203. </Button>
  204. </ConfigProvider>
  205. </div>
  206. </div>
  207. ))}
  208. {experimentInsTotal > experimentInsList.length ? (
  209. <div className={styles.loadMoreBox}>
  210. <Button type="link" onClick={onLoadMore}>
  211. 更多
  212. <DoubleRightOutlined rotate={90} />
  213. </Button>
  214. </div>
  215. ) : null}
  216. </div>
  217. );
  218. }
  219. export default ExperimentInstanceComponent;