JobCreate.jsx 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986
  1. import {
  2. Button,
  3. Form,
  4. Input,
  5. Select,
  6. Radio,
  7. Space,
  8. Upload,
  9. Steps,
  10. message,
  11. Divider,
  12. } from 'antd'
  13. import { PlusOutlined, DeleteOutlined } from '@ant-design/icons'
  14. import React, { useState, useEffect, useRef } from 'react'
  15. import styled from 'styled-components'
  16. import JobStepOne from './JobStepOne'
  17. import JobStepTwo from './JobStepTwo'
  18. import JobStepThree from './JobStepThree'
  19. import { useNavigate, useLocation } from 'react-router-dom'
  20. import {
  21. getConstantsTypes,
  22. createJob,
  23. getFileInfomation,
  24. getJobInfo,
  25. deleteTag,
  26. updateJobInfo,
  27. uploadFile,
  28. getFileUri,
  29. } from '../services'
  30. const JobCre = styled.div`
  31. padding: 20px;
  32. `
  33. const CreContent = styled.div`
  34. background-color: #ffffff;
  35. padding: 20px 10px;
  36. .creTitle {
  37. width: 120px;
  38. height: 60px;
  39. margin-left: 15px;
  40. display: flex;
  41. }
  42. .bluetTitle {
  43. background-color: rgba(24, 129, 218, 1);
  44. width: 3px;
  45. height: 16px;
  46. margin-top: 25px;
  47. line-height: 67px;
  48. }
  49. .creText {
  50. width: 105px;
  51. height: 20px;
  52. margin-left: 10px;
  53. overflow-wrap: break-word;
  54. color: rgba(74, 74, 74, 1);
  55. font-size: 14px;
  56. letter-spacing: 1px;
  57. text-align: center;
  58. white-space: nowrap;
  59. line-height: 67px;
  60. font-weight: 800;
  61. }
  62. .ant-form-item-control-input {
  63. width: 295px;
  64. }
  65. .JaPyInfo {
  66. .JaPySubmit {
  67. display: flex;
  68. .Submit {
  69. margin-left: 20px;
  70. .ant-btn {
  71. color: #1890ff;
  72. border-color: #1890ff;
  73. }
  74. }
  75. .Cancel {
  76. .ant-btn {
  77. color: #1890ff;
  78. border-color: #1890ff;
  79. }
  80. }
  81. }
  82. }
  83. .DagInfo {
  84. .ant-steps-item-icon {
  85. width: 42px;
  86. height: 42px;
  87. line-height: 42px;
  88. }
  89. .ant-steps-item-tail {
  90. font-size: 1px;
  91. margin-left: 67px;
  92. }
  93. .ant-steps-label-vertical .ant-steps-item-content {
  94. width: 220px;
  95. }
  96. }
  97. `
  98. const { Option } = Select
  99. const FormItem = Form.Item
  100. const RadioGroup = Radio.Group
  101. const layout = {
  102. labelCol: {
  103. span: 4,
  104. },
  105. wrapperCol: {
  106. span: 8,
  107. },
  108. }
  109. const tailLayout = {
  110. wrapperCol: {
  111. offset: 4,
  112. span: 8,
  113. },
  114. }
  115. export default function JobCreate() {
  116. const [WorkTypeForm] = Form.useForm()
  117. const { Step } = Steps
  118. // 路由导航
  119. const navigate = useNavigate()
  120. const { state } = useLocation()
  121. // 显示/隐藏配置Ja作业信息模块
  122. const [JaIsShow, setJaIsShow] = useState('none')
  123. // 显示/隐藏配置Python作业信息模块
  124. const [PyIsShow, setPyIsShow] = useState('none')
  125. // 显示/隐藏配置Dag作业信息模块
  126. const [DagIsShow, setDagIsShow] = useState('none')
  127. // 单选框动态传值
  128. const [radioValue, setRadioValue] = useState(null)
  129. // 脚本路径
  130. const [scriptFile, setSriptFile] = useState(null)
  131. // 步骤数
  132. const [currentStep, setCurrentStep] = useState(0)
  133. //作业类型
  134. const [jobTypeOptions, setJobTypeOptions] = useState([])
  135. //作业标签
  136. const [jobTags, setJobTags] = useState([])
  137. //dag第一步表单数据
  138. const [ConfigDataForm] = Form.useForm()
  139. //dag第二步表单数据
  140. const [InputDataForm] = Form.useForm()
  141. //dag第三步表单数据
  142. const [OutputDataForm] = Form.useForm()
  143. //当前表单数据
  144. const [currentForm, setCurrentForm] = useState(ConfigDataForm)
  145. //java表单数据
  146. const [JavaDataForm] = Form.useForm()
  147. //python表单数据
  148. const [PythonDataForm] = Form.useForm()
  149. //当前选择的作业类型
  150. const [currentJobType, setCurrentJobType] = useState('')
  151. const JobTagInputRef = useRef(null)
  152. const [tagName, setTagName] = useState('')
  153. const [dagData, setDagData] = useState(null)
  154. const [syncState, setSyncState] = useState(false)
  155. const [fileList, setFileList] = useState([])
  156. const [javas, setJavas] = useState([])
  157. const [pythons, setPythons] = useState([])
  158. const [pythonImages, setPythonImages] = useState([])
  159. const [javaImages, setJavaImages] = useState([])
  160. useEffect(() => {
  161. if (state) {
  162. fetchJobInfo(state.id)
  163. }
  164. }, [state])
  165. useEffect(() => {
  166. fetchFileUri('java')
  167. fetchFileUri('python')
  168. }, [])
  169. const fetchFileUri = async type => {
  170. const params = {
  171. project_id: 'test',
  172. user_id: 'test',
  173. file_type: type,
  174. }
  175. const { data } = await getFileUri(params)
  176. if (data.code === 200) {
  177. console.log(type, data)
  178. type === 'java' ? setJavas(data.data) : setPythons(data.data)
  179. } else {
  180. message.error(data.msg)
  181. }
  182. }
  183. const fetchJobInfo = async id => {
  184. const { data } = await getJobInfo(id)
  185. if (data.code === 200) {
  186. WorkTypeForm.setFieldValue('type', data.data.type)
  187. onGenderChange(data.data.type)
  188. switch (data.data.type) {
  189. case 'Python':
  190. PythonDataForm.setFieldsValue({
  191. name: data.data.name,
  192. tag: data.data.tag,
  193. image_url: data.data.image_url,
  194. script_file: 'fromList',
  195. execute_command: data.data.execute_command,
  196. })
  197. setRadioValue('fromList')
  198. setSriptFile(data.data.script_file)
  199. break
  200. case 'Java':
  201. JavaDataForm.setFieldsValue({
  202. name: data.data.name,
  203. tag: data.data.tag,
  204. image_url: data.data.image_url,
  205. script_file: 'fromList',
  206. execute_command: data.data.execute_command,
  207. })
  208. setRadioValue('fromList')
  209. setSriptFile(data.data.script_file)
  210. break
  211. case 'Dag':
  212. ConfigDataForm.setFieldsValue({
  213. name: data.data.name,
  214. tag: data.data.tag,
  215. dag_url: data.data.dag_url,
  216. })
  217. setDagData(data.data.dag_url)
  218. const inputs = []
  219. const outputs = []
  220. data.data.hd_relation.forEach(item => {
  221. if (item.type === 'input') {
  222. inputs.push(item)
  223. } else {
  224. outputs.push(item)
  225. }
  226. })
  227. inputs.forEach((item, index) => {
  228. InputDataForm.setFieldValue(
  229. `datasource${index}`,
  230. item.datasource_id
  231. )
  232. InputDataForm.setFieldValue(`sourceTable${index}`, item.table)
  233. })
  234. outputs.forEach((item, index) => {
  235. OutputDataForm.setFieldValue(
  236. `datasource${index}`,
  237. item.datasource_id
  238. )
  239. OutputDataForm.setFieldValue(`sourceTable${index}`, item.table)
  240. })
  241. break
  242. default:
  243. break
  244. }
  245. } else {
  246. message.error(data.msg)
  247. }
  248. }
  249. //动态改变作业标签的值
  250. const onNameChange = event => {
  251. setTagName(event.target.value)
  252. }
  253. //添加自定义作业标签
  254. const addTagItem = e => {
  255. if (tagName) {
  256. e.preventDefault()
  257. if (!jobTags.find(item => item === tagName)) {
  258. setJobTags([...jobTags, tagName])
  259. currentForm.setFieldValue('tag', tagName)
  260. setTagName('')
  261. setTimeout(() => {
  262. JobTagInputRef.current?.focus()
  263. }, 0)
  264. } else {
  265. message.error('存在同名标签')
  266. }
  267. } else {
  268. message.error('请输入自定义作业标签名称')
  269. }
  270. }
  271. // 设置单选框value值
  272. const onChange = e => {
  273. setRadioValue(e.target.value)
  274. }
  275. //上传文件配置
  276. const UpProps = {
  277. onRemove: file => {
  278. const index = fileList.indexOf(file)
  279. const newFileList = fileList.slice()
  280. newFileList.splice(index, 1)
  281. setFileList(newFileList)
  282. },
  283. beforeUpload: file => {
  284. setFileList([...fileList, file])
  285. return false
  286. },
  287. fileList,
  288. }
  289. //根据作业类型做出判断
  290. const onGenderChange = value => {
  291. if (value === 'Java') {
  292. setJaIsShow('block')
  293. setDagIsShow('none')
  294. setPyIsShow('none')
  295. setCurrentJobType('Java')
  296. setCurrentForm(JavaDataForm)
  297. } else if (value === 'Python') {
  298. setJaIsShow('none')
  299. setDagIsShow('none')
  300. setPyIsShow('block')
  301. setCurrentJobType('Python')
  302. setCurrentForm(PythonDataForm)
  303. } else if (value === 'Dag') {
  304. setJaIsShow('none')
  305. setDagIsShow('block')
  306. setPyIsShow('none')
  307. setCurrentJobType('Dag')
  308. setCurrentForm(ConfigDataForm)
  309. }
  310. }
  311. //上一步/下一步
  312. const changeStep = num => {
  313. setCurrentStep(currentStep + num)
  314. }
  315. //提交DAG
  316. const DagSubmit = async () => {
  317. const { data } = await getFileInfomation(dagData)
  318. if (data.code === 200) {
  319. const innodes = data.data.nodes.filter(item => item.op === 'datasource')
  320. const outnodes = data.data.nodes.filter(
  321. item => item.op === 'outputsource'
  322. )
  323. const relation_list = []
  324. innodes.forEach((item, index) => {
  325. relation_list.push({
  326. type: 'input',
  327. datasource_id: InputDataForm.getFieldValue(`datasource${index}`),
  328. node_uuid: item.id,
  329. table: InputDataForm.getFieldValue(`sourceTable${index}`),
  330. })
  331. })
  332. outnodes.forEach((item, index) => {
  333. relation_list.push({
  334. type: 'output',
  335. datasource_id: OutputDataForm.getFieldValue(`datasource${index}`),
  336. node_uuid: item.id,
  337. table: OutputDataForm.getFieldValue(`sourceTable${index}`),
  338. })
  339. })
  340. const params = {
  341. ...ConfigDataForm.getFieldValue(),
  342. script_file: '',
  343. type: currentJobType,
  344. dag_uuid: 'test',
  345. user_id: 'test',
  346. project_id: 'test',
  347. // 镜像
  348. image_url: '',
  349. relation_list,
  350. execute_command: '',
  351. }
  352. let resp = {}
  353. if (state?.id) {
  354. resp = await updateJobInfo(state.id, params)
  355. } else {
  356. resp = await createJob(params)
  357. }
  358. if (resp.data.code === 200) {
  359. navigate(-1)
  360. } else {
  361. message.error(resp.data.msg)
  362. }
  363. }
  364. }
  365. //下一步
  366. const nextStep = () => {
  367. currentForm
  368. .validateFields()
  369. .then(() => {
  370. if (currentStep === 0 || syncState) {
  371. if (currentStep === 2) {
  372. DagSubmit()
  373. } else {
  374. changeStep(1)
  375. }
  376. } else {
  377. message.error('请检查字段对应关系')
  378. }
  379. })
  380. .catch(err => {
  381. message.error('请检查表单数据是否完整')
  382. })
  383. }
  384. useEffect(() => {
  385. switch (currentStep) {
  386. case 0:
  387. setCurrentForm(ConfigDataForm)
  388. break
  389. case 1:
  390. setCurrentForm(InputDataForm)
  391. break
  392. case 2:
  393. setCurrentForm(OutputDataForm)
  394. break
  395. default:
  396. break
  397. }
  398. getJobType()
  399. getJobTag()
  400. getPythonImages()
  401. getJavaImages()
  402. }, [currentStep])
  403. const getPythonImages = async () => {
  404. const JobTypes = await getConstantsTypes('pythonImage')
  405. if (JobTypes.data.length !== 0) {
  406. setPythonImages(
  407. JobTypes.data.data.map(item => {
  408. const strs = item.split('/')
  409. return {
  410. label: strs.pop(),
  411. value: item,
  412. }
  413. })
  414. )
  415. }
  416. }
  417. const getJavaImages = async () => {
  418. const JobTypes = await getConstantsTypes('javaImage')
  419. if (JobTypes.data.length !== 0) {
  420. setJavaImages(
  421. JobTypes.data.data.map(item => {
  422. const strs = item.split('/')
  423. return {
  424. label: strs.pop(),
  425. value: item,
  426. }
  427. })
  428. )
  429. }
  430. }
  431. //获取作业类型和分类
  432. const getJobType = async () => {
  433. const JobTypes = await getConstantsTypes('作业类型')
  434. if (JobTypes.data.length !== 0) {
  435. setJobTypeOptions(
  436. JobTypes.data.data.map(item => {
  437. return {
  438. label: item,
  439. value: item,
  440. }
  441. })
  442. )
  443. }
  444. }
  445. const getJobTag = async () => {
  446. const { data } = await getConstantsTypes('作业标签')
  447. if (data.code === 200) {
  448. setJobTags(data.data)
  449. } else {
  450. message.error(data.msg)
  451. }
  452. }
  453. const finishSubmit = async () => {
  454. const params = {
  455. ...currentForm.getFieldValue(),
  456. type: currentJobType,
  457. dag_uuid: '',
  458. user_id: 'test',
  459. project_id: 'test',
  460. dag_url: '',
  461. relation_list: [],
  462. }
  463. if (radioValue === 'fromList') {
  464. params['script_file'] = scriptFile
  465. } else {
  466. const file = new FormData()
  467. fileList.forEach(item => {
  468. file.append('file', item)
  469. })
  470. file.append('project_id', 'test')
  471. file.append('file_type', currentJobType === 'Java' ? 'java' : 'python')
  472. const { data } = await uploadFile(file)
  473. if (data.code === 200) {
  474. params['script_file'] = data.data
  475. } else {
  476. message.error(data.msg)
  477. }
  478. }
  479. let res = {}
  480. if (state?.id) {
  481. res = await updateJobInfo(state.id, params)
  482. } else {
  483. res = await createJob(params)
  484. }
  485. if (res.data.code === 200) {
  486. navigate(-1)
  487. } else {
  488. message.error(res.data.msg)
  489. }
  490. }
  491. //java or python提交
  492. const JaPySubmit = () => {
  493. currentForm
  494. .validateFields()
  495. .then(() => {
  496. if (scriptFile !== null || fileList.length > 0) {
  497. finishSubmit()
  498. } else {
  499. message.error('请选择脚本')
  500. }
  501. })
  502. .catch(err => {
  503. message.error('请检查表单数据是否完整')
  504. })
  505. }
  506. const cancel = () => {
  507. navigate(-1)
  508. }
  509. const onScriptFileChange = val => {
  510. setSriptFile(val)
  511. }
  512. const onDeleteTag = async val => {
  513. const { data } = await deleteTag({ type: '作业标签', val })
  514. if (data.code === 200) {
  515. currentForm.setFieldValue('tag', null)
  516. message.success('删除成功')
  517. getJobTag()
  518. } else {
  519. message.error(data.msg)
  520. }
  521. }
  522. return (
  523. <JobCre>
  524. <CreContent>
  525. <div>
  526. <div className="creTitle">
  527. <div className="bluetTitle" />
  528. <span className="creText">配置作业类型</span>
  529. </div>
  530. {/* 作业类型选择 */}
  531. <Form {...layout} form={WorkTypeForm} name="control-hooks">
  532. <FormItem
  533. name="type"
  534. label="选择作业类型"
  535. rules={[
  536. {
  537. required: true,
  538. message: '请选择作业类型',
  539. },
  540. ]}>
  541. <Select
  542. placeholder="请选择作业类型..."
  543. onSelect={onGenderChange}
  544. options={jobTypeOptions}
  545. />
  546. </FormItem>
  547. </Form>
  548. </div>
  549. <div className="DagInfo" style={{ display: DagIsShow }}>
  550. <Steps labelPlacement="vertical" size="small" current={currentStep}>
  551. <Step title="步骤1: 配置作业信息" />
  552. <Step title="步骤2: 定义输入数据源" />
  553. <Step title="步骤3: 定义输出数据源" />
  554. </Steps>
  555. <div className="creTitle">
  556. <div className="bluetTitle" />
  557. <span className="creText">配置作业信息</span>
  558. </div>
  559. {/* 配置Dag作业信息 */}
  560. {/* 第一步 */}
  561. {currentStep === 0 && (
  562. <JobStepOne
  563. ConfigDataForm={ConfigDataForm}
  564. SelectDag={val => setDagData(val)}
  565. />
  566. )}
  567. {/* 第二步 */}
  568. {currentStep === 1 && (
  569. <JobStepTwo
  570. InputDataForm={InputDataForm}
  571. dagData={dagData}
  572. checkSync={val => setSyncState(val)}
  573. />
  574. )}
  575. {/* 第三步 */}
  576. {currentStep === 2 && (
  577. <JobStepThree
  578. OutputDataForm={OutputDataForm}
  579. dagData={dagData}
  580. checkSync={val => setSyncState(val)}
  581. />
  582. )}
  583. <Space style={{ margin: '20px' }}>
  584. <Button
  585. onClick={() => {
  586. if (currentStep !== 0) {
  587. changeStep(-1)
  588. } else {
  589. navigate(-1)
  590. }
  591. }}>
  592. {currentStep === 0 ? '取消' : '上一步'}
  593. </Button>
  594. <Button type="primary" onClick={nextStep}>
  595. {currentStep === 2 ? '提交' : '下一步'}
  596. </Button>
  597. </Space>
  598. </div>
  599. {/* 配置Java作业信息 */}
  600. <div className="JaPyInfo" style={{ display: JaIsShow }}>
  601. <div className="creTitle">
  602. <div className="bluetTitle" />
  603. <span className="creText">配置作业信息</span>
  604. </div>
  605. {/* 输入作业名称 */}
  606. <Form {...layout} form={JavaDataForm} name="control-hooks">
  607. <FormItem
  608. name="name"
  609. label="作业名称"
  610. rules={[
  611. {
  612. required: true,
  613. message: '请输入作业名称',
  614. },
  615. ]}>
  616. <Input placeholder="请输入作业名称..." allowClear></Input>
  617. </FormItem>
  618. {/* 选择作业标签 */}
  619. <FormItem
  620. shouldUpdate
  621. name="tag"
  622. label="作业标签"
  623. rules={[
  624. {
  625. required: true,
  626. message: '请选择作业标签',
  627. },
  628. ]}>
  629. <Select
  630. showSearch
  631. optionFilterProp="label"
  632. placeholder="请选择作业标签..."
  633. allowClear
  634. optionLabelProp="label"
  635. dropdownRender={menu => (
  636. <>
  637. {menu}
  638. <Divider
  639. style={{
  640. margin: '8px 0',
  641. }}
  642. />
  643. <Space
  644. style={{
  645. padding: '0 8px 4px',
  646. }}>
  647. <Input
  648. placeholder="添加作业标签"
  649. ref={JobTagInputRef}
  650. value={tagName}
  651. onChange={onNameChange}
  652. />
  653. <Button
  654. type="text"
  655. icon={<PlusOutlined />}
  656. onClick={addTagItem}>
  657. 添加分类
  658. </Button>
  659. </Space>
  660. </>
  661. )}>
  662. {jobTags.map(item => (
  663. <Option key={item} value={item} label={item}>
  664. <Space>
  665. <DeleteOutlined onClick={() => onDeleteTag(item)} />
  666. <span>{item}</span>
  667. </Space>
  668. </Option>
  669. ))}
  670. </Select>
  671. </FormItem>
  672. {/* 选择执行镜像 */}
  673. <FormItem
  674. name="image_url"
  675. label="配置执行的镜像"
  676. rules={[
  677. {
  678. required: true,
  679. message: '请选择配置执行的镜像',
  680. },
  681. ]}>
  682. <Select
  683. placeholder="请选择镜像..."
  684. allowClear
  685. showSearch
  686. optionFilterProp="label">
  687. {javaImages.map(item => (
  688. <Option
  689. key={item.value}
  690. value={item.value}
  691. label={item.label}>
  692. {item.label}
  693. </Option>
  694. ))}
  695. </Select>
  696. </FormItem>
  697. {/* 选择执行的Java脚本 */}
  698. <FormItem
  699. label="配置执行的Java脚本"
  700. name="script_file"
  701. rules={[
  702. {
  703. required: true,
  704. message: '请选择配置执行的Java脚本',
  705. },
  706. ]}>
  707. <RadioGroup onChange={onChange}>
  708. <Space direction="vertical" className="jsSel">
  709. <Radio value={'fromList'}>
  710. <span
  711. onClick={e => {
  712. e.preventDefault()
  713. e.stopPropagation()
  714. }}>
  715. <Select
  716. showSearch
  717. optionFilterProp="label"
  718. placeholder="在工作目录中选择"
  719. allowClear
  720. className="javaSelect"
  721. value={scriptFile}
  722. onChange={onScriptFileChange}
  723. disabled={radioValue === 'fromList' ? false : true}>
  724. {javas.map(item => (
  725. <Option
  726. key={item.uri}
  727. value={item.name}
  728. label={item.name}>
  729. {item.name}
  730. </Option>
  731. ))}
  732. </Select>
  733. </span>
  734. </Radio>
  735. <Radio value={'fromLocal'}>
  736. <Upload {...UpProps} accept=".jar">
  737. <Button
  738. disabled={radioValue === 'fromLocal' ? false : true}
  739. type="primary"
  740. className="upButton">
  741. 本地上传
  742. </Button>
  743. </Upload>
  744. </Radio>
  745. </Space>
  746. </RadioGroup>
  747. </FormItem>
  748. <FormItem label="执行命令" name="execute_command">
  749. <Input placeholder="请输入执行命令..." allowClear></Input>
  750. </FormItem>
  751. <FormItem {...tailLayout}>
  752. <div className="JaPySubmit">
  753. <div className="Cancel">
  754. <Button onClick={cancel}>取消</Button>
  755. </div>
  756. <div className="Submit">
  757. <Button onClick={JaPySubmit}>提交</Button>
  758. </div>
  759. </div>
  760. </FormItem>
  761. </Form>
  762. </div>
  763. {/* 配置Python作业信息 */}
  764. <div className="JaPyInfo" style={{ display: PyIsShow }}>
  765. <div className="creTitle">
  766. <div className="bluetTitle" />
  767. <span className="creText">配置作业信息</span>
  768. </div>
  769. {/* 输入作业名称 */}
  770. <Form {...layout} form={PythonDataForm} name="control-hooks">
  771. <FormItem
  772. name="name"
  773. label="作业名称"
  774. rules={[
  775. {
  776. required: true,
  777. message: '请输入作业名称',
  778. },
  779. ]}>
  780. <Input placeholder="请输入作业名称..." allowClear></Input>
  781. </FormItem>
  782. {/* 选择作业标签 */}
  783. <FormItem
  784. name="tag"
  785. label="作业标签"
  786. rules={[
  787. {
  788. required: true,
  789. message: '请选则作业标签',
  790. },
  791. ]}>
  792. <Select
  793. showSearch
  794. optionFilterProp="label"
  795. placeholder="请选择作业标签..."
  796. allowClear
  797. optionLabelProp="label"
  798. dropdownRender={menu => (
  799. <>
  800. {menu}
  801. <Divider
  802. style={{
  803. margin: '8px 0',
  804. }}
  805. />
  806. <Space
  807. style={{
  808. padding: '0 8px 4px',
  809. }}>
  810. <Input
  811. placeholder="添加作业标签"
  812. ref={JobTagInputRef}
  813. value={tagName}
  814. onChange={onNameChange}
  815. />
  816. <Button
  817. type="text"
  818. icon={<PlusOutlined />}
  819. onClick={addTagItem}>
  820. 添加分类
  821. </Button>
  822. </Space>
  823. </>
  824. )}>
  825. {jobTags.map(item => (
  826. <Option key={item} value={item} label={item}>
  827. <Space>
  828. <DeleteOutlined onClick={() => onDeleteTag(item)} />
  829. <span>{item}</span>
  830. </Space>
  831. </Option>
  832. ))}
  833. </Select>
  834. </FormItem>
  835. {/* 选择执行镜像 */}
  836. <FormItem
  837. name="image_url"
  838. label="配置执行的镜像"
  839. rules={[
  840. {
  841. required: true,
  842. message: '请配置执行的镜像',
  843. },
  844. ]}>
  845. <Select
  846. showSearch
  847. optionFilterProp="label"
  848. placeholder="请选择镜像..."
  849. allowClear>
  850. {pythonImages.map(item => (
  851. <Option
  852. key={item.value}
  853. value={item.value}
  854. label={item.label}>
  855. {item.label}
  856. </Option>
  857. ))}
  858. </Select>
  859. </FormItem>
  860. {/* 选择执行的Python脚本 */}
  861. <FormItem
  862. label="配置执行的Python脚本"
  863. name="script_file"
  864. rules={[
  865. {
  866. required: true,
  867. message: '请配置执行的Python脚本',
  868. },
  869. ]}>
  870. <RadioGroup onChange={onChange} className="PyScript_file">
  871. <Space direction="vertical">
  872. <Radio value={'fromList'}>
  873. <span
  874. onClick={e => {
  875. e.preventDefault()
  876. e.stopPropagation()
  877. }}>
  878. <Select
  879. showSearch
  880. optionFilterProp="label"
  881. placeholder="在工作目录中选择"
  882. allowClear
  883. className="javaSelect"
  884. value={scriptFile}
  885. onChange={onScriptFileChange}
  886. disabled={radioValue === 'fromList' ? false : true}>
  887. {pythons.map(item => (
  888. <Option
  889. key={item.uri}
  890. value={item.name}
  891. label={item.name}>
  892. {item.name}
  893. </Option>
  894. ))}
  895. </Select>
  896. </span>
  897. </Radio>
  898. <Radio value={'fromLocal'}>
  899. <Upload {...UpProps} accept=".py">
  900. <Button
  901. className="upButton"
  902. type="primary"
  903. disabled={radioValue === 'fromLocal' ? false : true}>
  904. 本地上传
  905. </Button>
  906. </Upload>
  907. </Radio>
  908. </Space>
  909. </RadioGroup>
  910. </FormItem>
  911. <FormItem label="执行命令" name="execute_command">
  912. <Input placeholder="请输入执行命令..." allowClear></Input>
  913. </FormItem>
  914. <FormItem {...tailLayout}>
  915. <div className="JaPySubmit">
  916. <div className="Cancel">
  917. <Button onClick={cancel}>取消</Button>
  918. </div>
  919. <div className="Submit">
  920. <Button onClick={JaPySubmit}>提交</Button>
  921. </div>
  922. </div>
  923. </FormItem>
  924. </Form>
  925. </div>
  926. </CreContent>
  927. </JobCre>
  928. )
  929. }