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.

props.jsx 15 kB

2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467
  1. import KFIcon from '@/components/KFIcon';
  2. import ParameterInput from '@/components/ParameterInput';
  3. import SubAreaTitle from '@/components/SubAreaTitle';
  4. import { getComputingResourceReq } from '@/services/pipeline';
  5. import { openAntdModal } from '@/utils/modal';
  6. import { to } from '@/utils/promise';
  7. import { Button, Drawer, Form, Input, Select } from 'antd';
  8. import { pick } from 'lodash';
  9. import { forwardRef, useEffect, useImperativeHandle, useState } from 'react';
  10. import PropsLabel from '../components/PropsLabel';
  11. import ResourceSelectorModal, { ResourceSelectorType } from '../components/ResourceSelectorModal';
  12. import styles from './props.less';
  13. import { canInput, createMenuItems } from './utils';
  14. const { TextArea } = Input;
  15. const Props = forwardRef(({ onParentChange }, ref) => {
  16. const [form] = Form.useForm();
  17. const [stagingItem, setStagingItem] = useState({});
  18. const [open, setOpen] = useState(false);
  19. const [selectedModel, setSelectedModel] = useState(undefined); // 选择的模型,为了再次打开时恢复原来的选择
  20. const [selectedDataset, setSelectedDataset] = useState(undefined); // 选择的数据集,为了再次打开时恢复原来的选择
  21. const [resourceStandardList, setResourceStandardList] = useState([]); // 资源规模列表
  22. const [menuItems, setMenuItems] = useState([]);
  23. useEffect(() => {
  24. getComputingResource();
  25. }, []);
  26. // 获取资源规格列表数据
  27. const getComputingResource = async () => {
  28. const params = {
  29. page: 0,
  30. size: 1000,
  31. resource_type: '',
  32. };
  33. const [res] = await to(getComputingResourceReq(params));
  34. if (res && res.data && res.data.content) {
  35. setResourceStandardList(res.data.content);
  36. }
  37. };
  38. const afterOpenChange = () => {
  39. if (!open) {
  40. console.log('zzzzz', form.getFieldsValue());
  41. const control_strategy = form.getFieldValue('control_strategy');
  42. const in_parameters = form.getFieldValue('in_parameters');
  43. const out_parameters = form.getFieldValue('out_parameters');
  44. onParentChange({
  45. ...stagingItem,
  46. ...form.getFieldsValue(),
  47. control_strategy: JSON.stringify(control_strategy),
  48. in_parameters: JSON.stringify(in_parameters),
  49. out_parameters: JSON.stringify(out_parameters),
  50. });
  51. }
  52. };
  53. const onClose = () => {
  54. setOpen(false);
  55. };
  56. useImperativeHandle(ref, () => ({
  57. getFieldsValue: async () => {
  58. const [propsRes, propsError] = await to(form.validateFields());
  59. if (propsRes && !propsError) {
  60. const values = form.getFieldsValue();
  61. return values;
  62. } else {
  63. return Promise.reject(propsError);
  64. }
  65. },
  66. showDrawer(e, params, parentNodes) {
  67. if (e.item && e.item.getModel()) {
  68. form.resetFields();
  69. const model = e.item.getModel();
  70. try {
  71. const nodeData = {
  72. ...model,
  73. in_parameters: JSON.parse(model.in_parameters),
  74. out_parameters: JSON.parse(model.out_parameters),
  75. control_strategy: JSON.parse(model.control_strategy),
  76. };
  77. console.log('model', nodeData);
  78. setStagingItem({
  79. ...nodeData,
  80. });
  81. form.setFieldsValue({
  82. ...nodeData,
  83. });
  84. } catch (error) {
  85. console.log(error);
  86. }
  87. setSelectedModel(undefined);
  88. setSelectedDataset(undefined);
  89. setOpen(true);
  90. // 参数下拉菜单
  91. setMenuItems(createMenuItems(params, parentNodes));
  92. }
  93. },
  94. propClose: () => {
  95. onClose();
  96. },
  97. }));
  98. // 选择数据集、模型、镜像
  99. const selectResource = (name, item) => {
  100. let type;
  101. let resource;
  102. switch (item.item_type) {
  103. case 'dataset':
  104. type = ResourceSelectorType.Dataset;
  105. resource = selectedDataset;
  106. break;
  107. case 'model':
  108. type = ResourceSelectorType.Model;
  109. resource = selectedModel;
  110. break;
  111. default:
  112. type = ResourceSelectorType.Mirror;
  113. break;
  114. }
  115. const { close } = openAntdModal(ResourceSelectorModal, {
  116. type,
  117. defaultExpandedKeys: resource ? [resource.id] : [],
  118. defaultCheckedKeys: resource ? [`${resource.id}-${resource.version}`] : [],
  119. defaultActiveTab: resource?.activeTab,
  120. onOk: (res) => {
  121. if (res) {
  122. if (type === ResourceSelectorType.Mirror) {
  123. form.setFieldValue(name, res);
  124. } else {
  125. const jsonObj = pick(res, ['id', 'version', 'path']);
  126. const value = JSON.stringify(jsonObj);
  127. const showValue = `${res.name}:${res.version}`;
  128. form.setFieldValue(name, { ...item, value, showValue, fromSelect: true });
  129. if (type === ResourceSelectorType.Dataset) {
  130. setSelectedDataset(res);
  131. } else if (type === ResourceSelectorType.Model) {
  132. setSelectedModel(res);
  133. }
  134. }
  135. } else {
  136. if (type === ResourceSelectorType.Dataset) {
  137. setSelectedDataset(undefined);
  138. } else if (type === ResourceSelectorType.Model) {
  139. setSelectedModel(undefined);
  140. }
  141. form.setFieldValue(name, '');
  142. }
  143. close();
  144. },
  145. });
  146. };
  147. // 获取选择数据集、模型后面按钮 icon
  148. const getSelectBtnIcon = (item) => {
  149. const type = item.item_type;
  150. if (type === 'dataset') {
  151. return <KFIcon type="icon-xuanzeshujuji" />;
  152. } else if (type === 'model') {
  153. return <KFIcon type="icon-xuanzemoxing" />;
  154. } else {
  155. return <KFIcon type="icon-xuanzejingxiang" />;
  156. }
  157. };
  158. // 筛选资源规格
  159. const filterResourceStandard = (input, { computing_resource = '' }) => {
  160. return computing_resource.toLocaleLowerCase().includes(input.toLocaleLowerCase());
  161. };
  162. // 参数回填
  163. const handleParameterClick = (name, value) => {
  164. form.setFieldValue(name, value);
  165. };
  166. // 控制策略
  167. const controlStrategyList = Object.entries(stagingItem.control_strategy ?? {}).map(
  168. ([key, value]) => ({ key, value }),
  169. );
  170. // 输入参数
  171. const inParametersList = Object.entries(stagingItem.in_parameters ?? {}).map(([key, value]) => ({
  172. key,
  173. value,
  174. }));
  175. // 输出参数
  176. const outParametersList = Object.entries(stagingItem.out_parameters ?? {}).map(
  177. ([key, value]) => ({ key, value }),
  178. );
  179. return (
  180. <Drawer
  181. title="编辑任务"
  182. placement="right"
  183. rootStyle={{ marginTop: '45px' }}
  184. getContainer={false}
  185. closeIcon={false}
  186. onClose={onClose}
  187. afterOpenChange={afterOpenChange}
  188. open={open}
  189. width={520}
  190. className={styles['pipeline-drawer']}
  191. >
  192. <Form
  193. name="form"
  194. form={form}
  195. layout="vertical"
  196. labelCol={{
  197. span: 24,
  198. }}
  199. wrapperCol={{
  200. span: 24,
  201. }}
  202. style={{
  203. maxWidth: 600,
  204. }}
  205. autoComplete="off"
  206. >
  207. <div className={styles['pipeline-drawer__title']}>
  208. <SubAreaTitle image="/assets/images/static-message.png" title="基本信息"></SubAreaTitle>
  209. </div>
  210. <Form.Item
  211. label="任务名称"
  212. name="label"
  213. rules={[
  214. {
  215. required: true,
  216. message: '请输入任务名称',
  217. },
  218. ]}
  219. >
  220. <Input placeholder="请输入任务名称" allowClear />
  221. </Form.Item>
  222. <Form.Item
  223. label="任务ID"
  224. name="id"
  225. rules={[
  226. {
  227. required: true,
  228. message: '请输入任务id',
  229. },
  230. ]}
  231. >
  232. <Input disabled />
  233. </Form.Item>
  234. <div className={styles['pipeline-drawer__title']}>
  235. <SubAreaTitle image="/assets/images/duty-message.png" title="任务信息"></SubAreaTitle>
  236. </div>
  237. <Form.Item label="镜像" required>
  238. <div className={styles['pipeline-drawer__ref-row']}>
  239. <Form.Item name="image" noStyle rules={[{ required: true, message: '请输入镜像' }]}>
  240. <Input placeholder="请输入或选择镜像" allowClear />
  241. </Form.Item>
  242. <Form.Item noStyle>
  243. <Button
  244. type="link"
  245. size="small"
  246. icon={getSelectBtnIcon({ item_type: 'image' })}
  247. onClick={() => selectResource('image', { item_type: 'image' })}
  248. className={styles['pipeline-drawer__ref-row__select-button']}
  249. >
  250. 选择镜像
  251. </Button>
  252. </Form.Item>
  253. </div>
  254. </Form.Item>
  255. <Form.Item
  256. name="working_directory"
  257. label={
  258. <PropsLabel
  259. menuItems={menuItems}
  260. title="工作目录"
  261. onClick={(value) => {
  262. handleParameterClick('working_directory', value);
  263. }}
  264. />
  265. }
  266. >
  267. <Input placeholder="请输入工作目录" allowClear />
  268. </Form.Item>
  269. <Form.Item
  270. name="command"
  271. label={
  272. <PropsLabel
  273. menuItems={menuItems}
  274. title="启动命令"
  275. onClick={(value) => {
  276. handleParameterClick('command', value);
  277. }}
  278. />
  279. }
  280. >
  281. <TextArea placeholder="请输入启动命令" allowClear />
  282. </Form.Item>
  283. <Form.Item
  284. label="资源规格"
  285. name="resources_standard"
  286. rules={[
  287. {
  288. required: true,
  289. message: '请选择资源规格',
  290. },
  291. ]}
  292. >
  293. <Select
  294. showSearch
  295. placeholder="请选择资源规格"
  296. filterOption={filterResourceStandard}
  297. options={resourceStandardList}
  298. fieldNames={{
  299. label: 'description',
  300. value: 'standard',
  301. }}
  302. />
  303. </Form.Item>
  304. <Form.Item
  305. name="mount_path"
  306. label={
  307. <PropsLabel
  308. menuItems={menuItems}
  309. title="挂载路径"
  310. onClick={(value) => {
  311. handleParameterClick('mount_path', value);
  312. }}
  313. />
  314. }
  315. >
  316. <Input placeholder="请输入挂载路径" allowClear />
  317. </Form.Item>
  318. <Form.Item
  319. name="env_variables"
  320. label={
  321. <PropsLabel
  322. menuItems={menuItems}
  323. title="环境变量"
  324. onClick={(value) => {
  325. handleParameterClick('env_variables', value);
  326. }}
  327. />
  328. }
  329. >
  330. <TextArea placeholder="请输入环境变量" allowClear />
  331. </Form.Item>
  332. {controlStrategyList.map((item) => (
  333. <Form.Item
  334. key={item.key}
  335. name={['control_strategy', item.key]}
  336. label={
  337. <PropsLabel
  338. menuItems={menuItems}
  339. title={item.value.label}
  340. onClick={(value) => {
  341. handleParameterClick(['control_strategy', item.key], {
  342. ...item.value,
  343. value,
  344. fromSelect: true,
  345. showValue: value,
  346. });
  347. }}
  348. />
  349. }
  350. // getValueProps={(e) => {
  351. // return { value: e.value };
  352. // }}
  353. // getValueFromEvent={(e) => {
  354. // return {
  355. // ...item.value,
  356. // value: e.target.value,
  357. // };
  358. // }}
  359. >
  360. <ParameterInput placeholder={item.value.placeholder} allowClear></ParameterInput>
  361. </Form.Item>
  362. ))}
  363. <div className={styles['pipeline-drawer__title']}>
  364. <SubAreaTitle image="/assets/images/duty-message.png" title="输入参数"></SubAreaTitle>
  365. </div>
  366. {inParametersList.map((item) => (
  367. <Form.Item
  368. key={item.key}
  369. label={
  370. <PropsLabel
  371. menuItems={menuItems}
  372. title={item.value.label + '(' + item.key + ')'}
  373. onClick={(value) => {
  374. handleParameterClick(['in_parameters', item.key], {
  375. ...item.value,
  376. value,
  377. fromSelect: true,
  378. showValue: value,
  379. });
  380. }}
  381. />
  382. }
  383. required={item.value.require ? true : false}
  384. >
  385. <div className={styles['pipeline-drawer__ref-row']}>
  386. <Form.Item
  387. name={['in_parameters', item.key]}
  388. noStyle
  389. rules={[{ required: item.value.require ? true : false }]}
  390. >
  391. <ParameterInput
  392. placeholder={item.value.placeholder}
  393. canInput={canInput(item.value)}
  394. allowClear
  395. ></ParameterInput>
  396. </Form.Item>
  397. {item.value.type === 'ref' && (
  398. <Form.Item noStyle>
  399. <Button
  400. size="small"
  401. type="link"
  402. icon={getSelectBtnIcon(item.value)}
  403. onClick={() => selectResource(['in_parameters', item.key], item.value)}
  404. className={styles['pipeline-drawer__ref-row__select-button']}
  405. >
  406. {item.value.label}
  407. </Button>
  408. </Form.Item>
  409. )}
  410. </div>
  411. </Form.Item>
  412. ))}
  413. <div className={styles['pipeline-drawer__title']}>
  414. <SubAreaTitle image="/assets/images/duty-message.png" title="输出参数"></SubAreaTitle>
  415. </div>
  416. {outParametersList.map((item) => (
  417. <Form.Item
  418. key={item.key}
  419. name={['out_parameters', item.key]}
  420. label={
  421. <PropsLabel
  422. menuItems={menuItems}
  423. title={item.value.label + '(' + item.key + ')'}
  424. onClick={(value) => {
  425. handleParameterClick(['out_parameters', item.key], {
  426. ...item.value,
  427. value,
  428. fromSelect: true,
  429. showValue: value,
  430. });
  431. }}
  432. />
  433. }
  434. rules={[{ required: item.value.require ? true : false }]}
  435. // getValueProps={(e) => {
  436. // return { value: e.value };
  437. // }}
  438. // getValueFromEvent={(e) => {
  439. // return {
  440. // ...item.value,
  441. // value: e.target.value,
  442. // };
  443. // }}
  444. >
  445. <ParameterInput placeholder={item.value.placeholder} allowClear></ParameterInput>
  446. </Form.Item>
  447. ))}
  448. </Form>
  449. </Drawer>
  450. );
  451. });
  452. export default Props;