瀏覽代碼

新接口

Leo 2 年之前
父節點
當前提交
e7676a701a

+ 59 - 26
src/component/CronSelect.jsx

@@ -1,4 +1,4 @@
-import { Input, Radio, Select, Space, Tooltip } from 'antd'
+import { Input, Radio, Select, Space } from 'antd'
 import React, {
   useState,
   useImperativeHandle,
@@ -6,12 +6,22 @@ import React, {
   useEffect,
 } from 'react'
 import styled from 'styled-components'
-import { QuestionCircleOutlined } from '@ant-design/icons'
 
 const CronWrapper = styled.div`
+  .time_wrapper {
+    margin-top: 10px;
+  }
   .time_select {
     width: 58px;
   }
+  .cron_input {
+    width: 60%;
+    margin-right: 10px;
+  }
+  .cron_format {
+    display: block;
+    margin: 10px 0;
+  }
 `
 
 const RadioGroup = Radio.Group
@@ -28,6 +38,7 @@ const CronSelect = ({ cron_data }, ref) => {
   const [weekVal, setWeekVal] = useState('')
   const [monthVal, setMonthVal] = useState('')
   const [cronVal, setCronVal] = useState('')
+  const [showExample, setShowExample] = useState(false)
   useImperativeHandle(ref, () => {
     return {
       getCronData: formatCronData(),
@@ -146,11 +157,14 @@ const CronSelect = ({ cron_data }, ref) => {
   const onChangeCronVal = e => {
     setCronVal(e.target.value)
   }
+  const onCronExampleChange = val => {
+    setCronVal(val)
+  }
 
   // 各时间段选项
   const timeOptions = val => {
     const options = []
-    for (let i = 1; i < val; i++) {
+    for (let i = 0; i < val; i++) {
       options.push(
         <Option value={i} key={i}>
           {i}
@@ -256,31 +270,43 @@ const CronSelect = ({ cron_data }, ref) => {
   )
 
   const cronExample = (
-    <Space direction="vertical">
-      <p>格式:分 时 天 月 周</p>
-      <p>0 * * * * (每小时)</p>
-      <p>0 0 * * * (每天)</p>
-      <p>0 0 * * 1(每周)</p>
-      <p>0 0 1 * *(每月)</p>
-      <p>*/15 * * * *(每隔15分钟执行一次)</p>
-      <p>* *12 * * *(每隔12小时执行一次)</p>
-      <p>15 10 16 * *(每月16日10:15分运行)</p>
-    </Space>
+    <Select allowClear onChange={onCronExampleChange} placeholder="查看示例">
+      <Option value="0 * * * *">
+        <span>0 * * * * (每小时)</span>
+      </Option>
+      <Option value="0 0 * * 1">
+        <span>0 0 * * 1(每周)</span>
+      </Option>
+      <Option value="0 0 1 * *">
+        <span>0 0 1 * *(每月)</span>
+      </Option>
+      <Option value="*/15 * * * *">
+        <span>*/15 * * * *(每隔15分钟执行一次)</span>
+      </Option>
+      <Option value="* */12 * * *">
+        <span>* */12 * * *(每隔12小时执行一次)</span>
+      </Option>
+      <Option value="15 10 16 * *">
+        <span>15 10 16 * *(每月16日10:15分运行)</span>
+      </Option>
+    </Select>
   )
 
   const cronSet = (
-    <Space>
+    <div>
       <Input
-        placeholder="cron表达式"
+        className="cron_input"
+        placeholder="* * * * *"
         value={cronVal}
         onChange={onChangeCronVal}
       />
-      <Tooltip placement="right" title={cronExample}>
-        <QuestionCircleOutlined
-          style={{ color: '#d9d9d9', fontSize: '16px' }}
-        />
-      </Tooltip>
-    </Space>
+      {!showExample && <span onClick={() => setShowExample(true)}>示例</span>}
+      {showExample && <span onClick={() => setShowExample(false)}>隐藏</span>}
+      <div>
+        <span className="cron_format">格式:分 时 天 月 周</span>
+        {showExample && cronExample}
+      </div>
+    </div>
   )
 
   return (
@@ -292,11 +318,18 @@ const CronSelect = ({ cron_data }, ref) => {
         <Radio value="3">按月</Radio>
         <Radio value="4">Cron表达式</Radio>
       </RadioGroup>
-      {cronType === '0' && hourSet}
-      {cronType === '1' && daySet}
-      {cronType === '2' && weekSet}
-      {cronType === '3' && monthSet}
-      {cronType === '4' && cronSet}
+      <div
+        className="time_wrapper"
+        onClick={e => {
+          e.preventDefault()
+          e.stopPropagation()
+        }}>
+        {cronType === '0' && hourSet}
+        {cronType === '1' && daySet}
+        {cronType === '2' && weekSet}
+        {cronType === '3' && monthSet}
+        {cronType === '4' && cronSet}
+      </div>
     </CronWrapper>
   )
 }

+ 1 - 0
src/module/datasource/component/DataTableStruct.jsx

@@ -4,6 +4,7 @@ import styled from 'styled-components'
 import JsonEditor from './utils/JsonEditor'
 
 const DataTable = styled.div`
+  height: 100%;
   .table-style {
     .ant-table-thead {
       > tr {

+ 104 - 16
src/module/datasource/component/DatasourceLog.jsx

@@ -1,20 +1,34 @@
-import React, { useState, useEffect } from 'react'
-import { Table, Space } from 'antd'
-import { getJoblog } from '../services'
+import React, { useState, useEffect, useImperativeHandle } from 'react'
+import { Table, Space, message } from 'antd'
+import { getJoblog, refreshLogsStatus } from '../services'
 import moment from 'moment'
 import { useNavigate } from 'react-router-dom'
 
-const DatasourceLog = () => {
+const DatasourceLog = ({ onRef, logId }) => {
   // 初始化日志列表
   const [logList, setLogList] = useState([])
   // 表格Loading状态
   const [dataLoading, setDataLoading] = useState(false)
 
+  const [currentPage, setCurrentPage] = useState(1)
+  const [currentPageSize, setCurrentPageSize] = useState(10)
+  const [dataTotal, setDataTotal] = useState(0)
+
   const navigate = useNavigate()
 
   const changeTask = () => {}
+
+  // 暴露更新列表方法
+  useImperativeHandle(onRef, () => {
+    return {
+      updateLogList: fetchJoblog,
+    }
+  })
+
   const checkLog = id => {
-    navigate('/datasource/log-watcher', { state: { id } })
+    navigate('/datasource/log-watcher', {
+      state: { id },
+    })
   }
   const columns = [
     {
@@ -22,6 +36,11 @@ const DatasourceLog = () => {
       dataIndex: 'jobId',
       key: 'jobId',
     },
+    {
+      title: '任务描述',
+      dataIndex: 'jobDesc',
+      key: 'jobDesc',
+    },
     {
       title: '调度时间',
       dataIndex: 'triggerTime',
@@ -36,7 +55,7 @@ const DatasourceLog = () => {
           style={{
             color: code === 0 ? '#FF4D4F' : code === 1 ? '#52C41A' : '#4A4A4A',
           }}>
-          {code === 0 ? '失败' : code === 1 ? '成功' : '无'}
+          {code === 0 ? '失败' : '成功'}
         </span>
       ),
     },
@@ -52,9 +71,17 @@ const DatasourceLog = () => {
       render: code => (
         <span
           style={{
-            color: code === 0 ? '#FF4D4F' : code === 1 ? '#52C41A' : '#4A4A4A',
+            color: code === 3 ? '#FF4D4F' : code === 2 ? '#52C41A' : '#4A4A4A',
           }}>
-          {code === 0 ? '失败' : code === 1 ? '成功' : '无'}
+          {code === 3
+            ? '失败'
+            : code === 2
+            ? '成功'
+            : code === 1
+            ? '运行中'
+            : code === 0
+            ? '队列中'
+            : '暂无'}
         </span>
       ),
     },
@@ -85,28 +112,83 @@ const DatasourceLog = () => {
     },
   ]
 
-  const fetchJoblog = async () => {
+  const fetchJoblog = async (page = 1, pageSize = 10) => {
     setDataLoading(true)
-    const { data } = await getJoblog()
+    const { data } = await getJoblog({
+      page: page,
+      size: pageSize,
+      id: logId,
+    })
     if (data.code === 200) {
-      const list = data.data.items.map(item => {
+      setDataTotal(data.data.extra_data.total)
+      setCurrentPage(data.data.extra_data.page)
+      setCurrentPageSize(data.data.extra_data.size)
+      const refreshLogs = []
+      const list = data.data.item.map(item => {
+        if ([0, 1].includes(item.execute_result)) {
+          refreshLogs.push(item.id)
+        }
         return {
           key: item.id,
           jobId: item.job_id,
-          triggerTime: moment(item.trigger_time).format('YYYY.MM.DD HH:MM'),
-          triggerResult: item.trigger_code,
-          handleTime: moment(item.handle_time).format('YYYY.MM.DD HH:MM'),
-          handleResult: item.handle_code,
+          jobDesc: item.job_desc,
+          afJobId: item.af_job_id,
+          runId: item.run_id,
+          triggerTime: item.trigger_time
+            ? moment(item.trigger_time * 1000).format('YYYY.MM.DD HH:MM')
+            : '无',
+          triggerResult: item.trigger_result,
+          handleTime: item.execute_time
+            ? moment(item.execute_time * 1000).format('YYYY.MM.DD HH:MM')
+            : '无',
+          handleResult: item.execute_result,
         }
       })
       setLogList(list)
+      refreshStatus(refreshLogs)
     }
     setDataLoading(false)
   }
 
+  const refreshStatus = async refreshLogs => {
+    if (refreshLogs.length !== 0) {
+      const { data } = await refreshLogsStatus(refreshLogs.toString())
+      if (data.code === 200) {
+        const list = []
+        let logs = []
+        console.log(data.data)
+        const keys = Object.keys(data.data)
+        keys.forEach(item => {
+          if ([2, 3].includes(data.data[item])) {
+            logs = logList.map(logData => {
+              if (logData.id === item) {
+                logData.execute_result = data.data[item]
+              }
+              return logData
+            })
+          } else {
+            list.push(item)
+          }
+        })
+        if (logs.length !== 0) {
+          setLogList(logs)
+        }
+        setTimeout(() => {
+          refreshStatus(list)
+        }, 10000)
+      } else {
+        message.error(data.msg)
+      }
+    }
+  }
+
+  const pageChange = (page, pageSize) => {
+    fetchJoblog(page, pageSize)
+  }
+
   useEffect(() => {
     fetchJoblog()
-  }, [])
+  }, [logId])
 
   return (
     <Table
@@ -114,6 +196,12 @@ const DatasourceLog = () => {
       dataSource={logList}
       bordered
       loading={dataLoading}
+      pagination={{
+        current: currentPage,
+        total: dataTotal,
+        pageSize: currentPageSize,
+        onChange: pageChange,
+      }}
     />
   )
 }

+ 16 - 31
src/module/datasource/component/DatasourceManage.jsx

@@ -1,14 +1,11 @@
 import React, { useState, useEffect, useImperativeHandle } from 'react'
-import { Space, Table, Modal, message } from 'antd'
+import { Space, Table, Modal, message, Popconfirm } from 'antd'
 import DatasourceData from '../component/DatasourceData'
 import {
   getDataSourceList,
   delDataSource,
   getTableNamesList,
 } from '../services'
-import { ExclamationCircleOutlined } from '@ant-design/icons'
-
-const { confirm } = Modal
 
 export default function DatasourceManage({ onRef }) {
   // 查看数据源弹窗是否可可视
@@ -55,26 +52,14 @@ export default function DatasourceManage({ onRef }) {
   })
 
   // 删除确认
-  const DeleteConfirm = key => {
-    confirm({
-      title: '是否要删除该数据源?',
-      icon: <ExclamationCircleOutlined />,
-      okText: '确认删除',
-      okType: 'danger',
-      cancelText: '取消',
-      onOk: async () => {
-        const { data } = await delDataSource(key)
-        if (data.code === 200) {
-          message.success('删除成功')
-          fetchDataSourceList()
-        } else {
-          message.success('删除失败')
-        }
-      },
-      onCancel() {
-        message.info('取消删除')
-      },
-    })
+  const DeleteConfirm = async key => {
+    const { data } = await delDataSource(key)
+    if (data.code === 200) {
+      message.success('删除成功')
+      fetchDataSourceList()
+    } else {
+      message.success('删除失败')
+    }
   }
 
   //获取数据源表名
@@ -162,13 +147,13 @@ export default function DatasourceManage({ onRef }) {
           {/* <a href="/#" onClick={editData} style={{ color: '#1881DA' }}>
             编辑
           </a> */}
-          <span
-            onClick={() => {
-              DeleteConfirm(record.key)
-            }}
-            style={{ color: '#1881DA', cursor: 'pointer' }}>
-            删除
-          </span>
+          <Popconfirm
+            title="确认删除?"
+            okText="确认"
+            cancelText="取消"
+            onConfirm={() => DeleteConfirm(record.key)}>
+            <span style={{ color: '#1881DA', cursor: 'pointer' }}>删除</span>
+          </Popconfirm>
         </Space>
       ),
     },

+ 92 - 21
src/module/datasource/component/DatasourceSyncView.jsx

@@ -1,8 +1,11 @@
-import { Table, Space, Switch, message } from 'antd'
+import { Table, Space, Switch, message, Popconfirm } from 'antd'
 import React, { useEffect, useState } from 'react'
-import { getJobList, updateJobList } from '../services'
+import { useNavigate } from 'react-router-dom'
+import { getJobList, updateJobStatus, delJob, executeOnce } from '../services'
+
+export default function DatasourceSyncView({ selectLog }) {
+  const navigate = useNavigate()
 
-export default function DatasourceSyncView() {
   // 初始化同步配置任务列表
   const [jobList, setJobList] = useState([])
   // 任务列表完整数据
@@ -14,12 +17,22 @@ export default function DatasourceSyncView() {
   // 表格Loading状态
   const [dataLoading, setDataLoading] = useState(false)
 
+  const [currentPage, setCurrentPage] = useState(1)
+  const [currentPageSize, setCurrentPageSize] = useState(10)
+  const [dataTotal, setDataTotal] = useState(0)
+
   // 请求任务列表数据
-  const fetchJobList = async () => {
+  const fetchJobList = async (page = 1, pageSize = 10) => {
     setDataLoading(true)
-    const { data } = await getJobList()
+    const { data } = await getJobList({
+      page: page,
+      size: pageSize,
+    })
     if (data.code === 200) {
-      const list = data.data.items.map(item => {
+      setDataTotal(data.data.extra_data.total)
+      setCurrentPage(data.data.extra_data.page)
+      setCurrentPageSize(data.data.extra_data.size)
+      const list = data.data.item.map(item => {
         return {
           key: item.id,
           id: item.id,
@@ -34,6 +47,31 @@ export default function DatasourceSyncView() {
     setDataLoading(false)
   }
 
+  const pageChange = (page, pageSize) => {
+    fetchJobList(page, pageSize)
+  }
+
+  const handleDelete = async id => {
+    const { data } = await delJob(id)
+    if (data.code === 200) {
+      message.success('删除成功')
+      fetchJobList()
+    } else {
+      message.error('删除失败')
+    }
+  }
+  const editSyncItem = id => {
+    navigate('/datasource/sync-edit', { state: { id } })
+  }
+  const runTimeOnce = async id => {
+    const { data } = await executeOnce(id)
+    if (data.code === 200) {
+      message.success('开始执行')
+    } else {
+      message.error(data.msg)
+    }
+  }
+
   useEffect(() => {
     fetchJobList()
   }, [])
@@ -67,21 +105,12 @@ export default function DatasourceSyncView() {
             onClick={async () => {
               setSwitchLoading(true)
               const switch_data = jobData.find(item => item.id === record.key)
-              const request_body = {
-                job_cron: switch_data.job_cron,
-                job_desc: switch_data.job_desc,
-                executor_route_strategy: switch_data.executor_route_strategy,
-                executor_handler: switch_data.executor_handler,
-                executor_param: switch_data.executor_param,
-                executor_block_strategy: switch_data.executor_block_strategy,
-                executor_timeout: switch_data.executor_timeout,
-                executor_fail_retry_count:
-                  switch_data.executor_fail_retry_count,
-                inc_start_time: switch_data.inc_start_time,
-                job_json: switch_data.job_json,
-                trigger_status: switch_data.trigger_status === 1 ? 0 : 1,
-              }
-              const { data } = await updateJobList(record.key, request_body)
+              const trigger_status = switch_data.trigger_status === 1 ? 0 : 1
+
+              const { data } = await updateJobStatus({
+                id: record.key,
+                trigger_status,
+              })
               if (data.code === 200) {
                 message.success(
                   record.trigger_status === 1 ? '关闭成功' : '开启成功'
@@ -98,6 +127,42 @@ export default function DatasourceSyncView() {
         </Space>
       ),
     },
+    {
+      title: '操作',
+      key: 'operation',
+      render: (_, record) => (
+        <Space size="middle">
+          <span
+            onClick={() => {
+              runTimeOnce(record.id)
+            }}
+            style={{ color: '#1881DA', cursor: 'pointer' }}>
+            执行一次
+          </span>
+          <span
+            onClick={() => {
+              selectLog(record.id)
+            }}
+            style={{ color: '#1881DA', cursor: 'pointer' }}>
+            查看日志
+          </span>
+          <span
+            onClick={() => {
+              editSyncItem(record.id)
+            }}
+            style={{ color: '#1881DA', cursor: 'pointer' }}>
+            编辑
+          </span>
+          <Popconfirm
+            title="确认删除?"
+            okText="确认"
+            cancelText="取消"
+            onConfirm={() => handleDelete(record.id)}>
+            <span style={{ color: '#1881DA', cursor: 'pointer' }}>删除</span>
+          </Popconfirm>
+        </Space>
+      ),
+    },
   ]
   return (
     <Table
@@ -105,6 +170,12 @@ export default function DatasourceSyncView() {
       dataSource={jobList}
       bordered
       loading={dataLoading}
+      pagination={{
+        current: currentPage,
+        total: dataTotal,
+        pageSize: currentPageSize,
+        onChange: pageChange,
+      }}
     />
   )
 }

+ 181 - 0
src/module/datasource/component/EditSyncView.jsx

@@ -0,0 +1,181 @@
+import React, { useEffect, useRef, useState } from 'react'
+import StepFour from './StepFour'
+import DataTableStruct from './DataTableStruct'
+import { useNavigate, useLocation } from 'react-router-dom'
+import styled from 'styled-components'
+import { Button, Form, Space, Drawer, message } from 'antd'
+import { MenuFoldOutlined, MenuUnfoldOutlined } from '@ant-design/icons'
+import { getJobInfo, updateJobList } from '../services'
+import moment from 'moment'
+const EditView = styled.div`
+  padding: 20px;
+  .check__btn {
+    position: absolute;
+    right: 0;
+    top: 20%;
+    border-radius: 0;
+  }
+`
+
+const EditSyncView = () => {
+  // 路由导航
+  const navigate = useNavigate()
+  const { state } = useLocation()
+
+  const cronRef = useRef()
+  // jsonValue
+  const jsonValue = React.createRef()
+
+  // 同步参数表单
+  const [syncDataForm] = Form.useForm()
+  const [partitionNum, setPartitionNum] = useState(0)
+  // 右侧表结构可视
+  const [visible, setVisible] = useState(false)
+  const [dataxJson, setDataxJson] = useState(null)
+  const [cron_data, setCronData] = useState(null)
+  const [trigger_status, setTriggerStatus] = useState(false)
+
+  const fetchSyncInfo = async id => {
+    const { data } = await getJobInfo(id)
+    if (data.code === 200) {
+      syncDataForm.setFieldsValue({
+        job_desc: data.data.job_desc,
+        executor_route_strategy: data.data.executor_route_strategy,
+        executor_block_strategy: data.data.executor_block_strategy,
+        cron_expression: data.data.cron_expression_dict,
+        executor_timeout: data.data.executor_timeout,
+        executor_fail_retry_count: data.data.executor_fail_retry_count,
+        inc_start_time:
+          data.data.inc_start_time && moment(data.data.inc_start_time * 1000),
+        last_time: data.data.last_time,
+        current_time: data.data.current_time,
+        partition_info: data.data.partition_info,
+        partition_time: data.data.partition_time,
+        jvm_param: data.data.jvm_param,
+      })
+      setTriggerStatus(data.data.trigger_status)
+      setCronData(data.data.cron_expression_dict)
+      setPartitionNum(data.data.partition_num)
+      setDataxJson(JSON.parse(data.data.job_json))
+    } else {
+      message.error(data.msg)
+    }
+  }
+  const submit = () => {
+    if (jsonValue.current) {
+      setDataxJson(JSON.parse(jsonValue.current.jsonValue.editor.getValue()))
+    }
+    try {
+      let job_json = JSON.stringify(dataxJson)
+      if (jsonValue.current) {
+        job_json = JSON.stringify(
+          JSON.parse(jsonValue.current.jsonValue.editor.getValue())
+        )
+      }
+      if (Object.keys(cronRef.current.getCronData).length !== 0) {
+        syncDataForm.setFieldValue(
+          'cron_expression',
+          cronRef.current.getCronData
+        )
+      }
+      syncDataForm
+        .validateFields()
+        .then(() => {
+          handleEdit(job_json)
+        })
+        .catch(err => {
+          message.error('请检查表单数据是否完整')
+        })
+    } catch (error) {
+      message.error('转换JSON字符串失败,请检查json数据', error)
+    }
+  }
+
+  const handleEdit = async job_json => {
+    const fields = syncDataForm.getFieldValue()
+    const params = {
+      ...fields,
+      trigger_status,
+      job_json,
+      inc_start_time: moment(fields.inc_start_time).unix(),
+      user_id: 'test',
+    }
+    params['partition_num'] = partitionNum
+    const { data } = await updateJobList(state.id, params)
+    if (data.code === 200) {
+      message.success('提交成功')
+      navigate(-1)
+    } else {
+      message.error('提交失败')
+    }
+  }
+
+  // 展示
+  const showDrawer = () => {
+    setVisible(true)
+  }
+
+  // 关闭
+  const onClose = () => {
+    if (jsonValue.current) {
+      setDataxJson(JSON.parse(jsonValue.current.jsonValue.editor.getValue()))
+    }
+
+    setVisible(false)
+  }
+
+  useEffect(() => {
+    if (state) {
+      fetchSyncInfo(state.id)
+    }
+  }, [state])
+  return (
+    <EditView>
+      <StepFour
+        onRef={cronRef}
+        syncDataForm={syncDataForm}
+        cron_data={cron_data}
+        partitionNumHandle={{
+          num: partitionNum,
+          func: val => setPartitionNum(val),
+        }}
+      />
+      <Space style={{ margin: '20px' }}>
+        <Button
+          onClick={() => {
+            navigate(-1)
+          }}>
+          取消
+        </Button>
+        <Button type="primary" onClick={submit}>
+          提交
+        </Button>
+      </Space>
+      <Button
+        type="primary"
+        onClick={showDrawer}
+        className="check__btn"
+        size="large"
+        icon={<MenuFoldOutlined />}>
+        构建
+      </Button>
+      <Drawer
+        title="构建"
+        placement="right"
+        onClose={onClose}
+        visible={visible}
+        width={600}
+        mask={false}
+        destroyOnClose={true}
+        closeIcon={<MenuUnfoldOutlined style={{ color: '#1881DA' }} />}>
+        <DataTableStruct
+          currentStep={3}
+          tableData={{}}
+          datax={dataxJson}
+          onRef={jsonValue}
+        />
+      </Drawer>
+    </EditView>
+  )
+}
+export default EditSyncView

+ 30 - 5
src/module/datasource/component/LogWatcher.jsx

@@ -1,37 +1,62 @@
+import { Button } from 'antd'
 import React, { useEffect, useState } from 'react'
-import { useLocation } from 'react-router-dom'
+import { useLocation, useNavigate } from 'react-router-dom'
 import styled from 'styled-components'
 import { getOnceJoblog } from '../services/index'
 
 const LogWrapper = styled.div`
+  margin: 20px;
   padding: 30px;
-  width: 100%;
-  height: 800px;
   background-color: #fff;
   .log {
     background-color: #012b36;
-    height: 100%;
+    height: calc(100vh - 200px);
     overflow-y: scroll;
     color: #638691;
     padding: 8px 10px;
+    ::-webkit-scrollbar {
+      display: none;
+    }
+  }
+  .return {
+    display: flex;
+    justify-content: flex-end;
+    padding-bottom: 10px;
   }
 `
 
 const LogWatcher = () => {
+  const navigate = useNavigate()
   const { state } = useLocation()
   const [logData, setLogData] = useState(null)
 
   const fetchOnceJoblog = async () => {
     const { data } = await getOnceJoblog(state.id)
     if (data.code === 200) {
-      setLogData(data.data.handle_msg)
+      setLogData(data.data.log)
     }
   }
   useEffect(() => {
     fetchOnceJoblog()
   }, [state])
+
+  const returnLogTable = () => {
+    console.log('111')
+    navigate('/datasource', {
+      state: { key: '3' },
+    })
+  }
   return (
     <LogWrapper>
+      {/* <div className="return">
+        <Button
+          type="primary"
+          onClick={() => {
+            returnLogTable()
+          }}>
+          返回上一级
+        </Button>
+      </div> */}
       <pre className="log">{logData}</pre>
     </LogWrapper>
   )

+ 123 - 116
src/module/datasource/component/StepFour.jsx

@@ -1,29 +1,42 @@
-import React, { useState } from 'react'
-import { Form, Select, Input, DatePicker, Row, Col, InputNumber, Checkbox } from 'antd'
-const { Option } = Select
-export default function StepFour({ syncDataForm }) {
+import React, { useMemo, useState } from 'react'
+import { Form, Select, Input, DatePicker, Row, Col, InputNumber } from 'antd'
+import CronSelect from '../../../component/CronSelect'
+
+const FormItem = Form.Item
+
+export default function StepFour({
+  onRef,
+  syncDataForm,
+  partitionNumHandle,
+  cron_data,
+}) {
   // 是否展示分区时间
   const [showPartitionTime, setShowPartitionTime] = useState(false)
-  // 是否展示定时规则高级配置
-  const [showRuleConfig, setShowRuleConfig] = useState(false)
-  // 定时规则单位选择
-  const suffixSelector = (
-    <Select
-      style={{
-        width: 70,
-      }}
-      defaultValue="秒"
-    >
-      <Option value="秒">秒</Option>
-      <Option value="分钟">分钟</Option>
-      <Option value="小时">小时</Option>
-      <Option value="日">日</Option>
-      <Option value="周">周</Option>
-      <Option value="月">月</Option>
-    </Select>
-  );
+  const [cronData, setCronData] = useState(null)
+  const [partitionNum, setPartitionNum] = useState(null)
+  useState(() => {
+    setCronData(syncDataForm.getFieldValue('cron_expression'))
+    if (syncDataForm.getFieldValue('partition_info')) {
+      setShowPartitionTime(true)
+    }
+  }, [])
+  useMemo(() => {
+    setCronData(cron_data)
+    if (syncDataForm.getFieldValue('partition_info')) {
+      setShowPartitionTime(true)
+    }
+  }, [cron_data])
+  useState(() => {
+    if (partitionNumHandle.num !== null) {
+      setPartitionNum(partitionNumHandle.num)
+    }
+  }, [partitionNumHandle.num])
+  const onPartitionNumChange = val => {
+    setPartitionNum(val)
+    partitionNumHandle.func(val)
+  }
   return (
-    <>
+    <div>
       <p
         style={{
           fontWeight: 600,
@@ -39,17 +52,26 @@ export default function StepFour({ syncDataForm }) {
           span: 2,
         }}
         wrapperCol={{
-          span: 6,
+          span: 8,
         }}
         initialValues={{
           partition_info: '',
-          replace_param: '',
           executor_timeout: 0,
           executor_fail_retry_count: 0,
-          inc_start_time: ''
-        }}
-      >
-        <Form.Item
+          inc_start_time: '',
+        }}>
+        <FormItem
+          label="同步任务名称"
+          name="job_desc"
+          rules={[
+            {
+              required: true,
+              message: '请输入同步任务名',
+            },
+          ]}>
+          <Input placeholder="请输入同步任务名称" />
+        </FormItem>
+        <FormItem
           label="路由策略"
           name="executor_route_strategy"
           rules={[
@@ -60,14 +82,14 @@ export default function StepFour({ syncDataForm }) {
           ]}>
           <Select
             options={[
-              {label: '第一个', value: 'FIRST'},
-              {label: '最后一个', value: 'LAST'}
+              { label: '第一个', value: 'FIRST' },
+              { label: '最后一个', value: 'LAST' },
             ]}
             allowClear
-            placeholder='请选择路由策略'
+            placeholder="请选择路由策略"
           />
-        </Form.Item>
-        <Form.Item
+        </FormItem>
+        <FormItem
           label="阻塞处理方式"
           name="executor_block_strategy"
           rules={[
@@ -78,44 +100,60 @@ export default function StepFour({ syncDataForm }) {
           ]}>
           <Select
             options={[
-              {label: '单机串行', value: 'SERIAL_EXECUTION'},
-              {label: '丢弃后续调度', value: 'SERIAL_EXECUTION1'},
-              {label: '覆盖之前调度', value: 'SERIAL_EXECUTION2'},
+              { label: '单机串行', value: 'SERIAL_EXECUTION' },
+              { label: '丢弃后续调度', value: 'SERIAL_EXECUTION1' },
+              { label: '覆盖之前调度', value: 'SERIAL_EXECUTION2' },
             ]}
             allowClear
-            placeholder='请选择阻塞处理方式'
+            placeholder="请选择阻塞处理方式"
           />
-        </Form.Item>
-        <Form.Item
+        </FormItem>
+        <FormItem
+          label="定时规则"
+          name="cron_expression"
+          rules={[
+            {
+              required: true,
+              message: '请选择定时规则',
+            },
+          ]}>
+          <CronSelect cron_data={cronData} ref={onRef} />
+        </FormItem>
+        <FormItem
           label="超时时间"
           name="executor_timeout"
-          required
-        >
-          <InputNumber min={0} addonAfter="分钟"/>
-        </Form.Item>
-        <Form.Item
+          rules={[
+            {
+              required: true,
+              message: '请输入超时时间',
+            },
+          ]}>
+          <InputNumber min={0} addonAfter="分钟" />
+        </FormItem>
+        <FormItem
           label="失败重试次数"
           name="executor_fail_retry_count"
-          required
-        >
-          <InputNumber  min={0} addonAfter="次" />
-        </Form.Item>
-        <Form.Item
-          label="增量开始时间"
-          name="inc_start_time"
-        >
-          <DatePicker showTime placeholder='请选择增量开始时间'/>
-        </Form.Item>
-        <Form.Item
-          label="增量时间字段"
-          name="replace_param"
-        >
-          <Input placeholder="-DlastTime='%s' -DcurrentTime='%s'"/>
-        </Form.Item>
+          rules={[
+            {
+              required: true,
+              message: '请输入失败重试次数',
+            },
+          ]}>
+          <InputNumber min={0} addonAfter="次" />
+        </FormItem>
+        <FormItem label="增量开始时间" name="inc_start_time">
+          <DatePicker showTime placeholder="请选择增量开始时间" />
+        </FormItem>
+        <FormItem label="上次执行时间" name="last_time">
+          <Input placeholder="lastTime" />
+        </FormItem>
+        <FormItem label="本次执行时间" name="current_time">
+          <Input placeholder="currentTime" />
+        </FormItem>
         <Row span={24}>
           <Col span={8}>
-            <Form.Item
-              label="分字段"
+            <FormItem
+              label="分字段"
               name="partition_info"
               labelCol={{
                 span: 6,
@@ -131,14 +169,14 @@ export default function StepFour({ syncDataForm }) {
                     setShowPartitionTime(false)
                   }
                 }}
-                placeholder='请输入区分字段'
+                placeholder="请输入分区字段"
               />
-            </Form.Item>
+            </FormItem>
           </Col>
           {showPartitionTime ? (
             <>
               <Col span={8}>
-                <Form.Item
+                <FormItem
                   label="分区时间"
                   name="partition_time"
                   labelCol={{
@@ -149,67 +187,36 @@ export default function StepFour({ syncDataForm }) {
                   }}>
                   <Select
                     options={[
-                      {label: 'yyyy-MM-dd', value: 'yyyy-MM-dd'},
-                      {label: 'yyyyMMdd', value: 'yyyyMMdd'},
-                      {label: 'yyyy/MM/dd', value: 'yyyy/MM/dd'},
+                      { label: '%Y-%m-%d', value: '%Y-%m-%d' },
+                      { label: '%Y%m%d', value: '%Y%m%d' },
+                      { label: '%Y/%m/%d', value: '%Y/%m/%d' },
                     ]}
                     allowClear
-                    defaultValue={'yyyy-MM-dd'}
+                    defaultValue={'%Y-%m-%d'}
+                    onSelect={val =>
+                      syncDataForm.setFieldValue('partition_time', val)
+                    }
                   />
-                </Form.Item>
+                </FormItem>
               </Col>
               <Col>
-                <InputNumber defaultValue={0} style={{ marginLeft: '10px' }} max={0}/>
+                <InputNumber
+                  value={partitionNum}
+                  onChange={onPartitionNumChange}
+                  style={{ marginLeft: '10px' }}
+                  max={0}
+                  min={-20}
+                />
               </Col>
             </>
           ) : (
             ''
           )}
         </Row>
-        <Row span={24}>
-          <Col span={8}>
-            <Form.Item
-              label="定时规则"
-              name="job_cron"
-              labelCol={{
-                span: 6,
-              }}
-              wrapperCol={{
-                span: 18,
-              }}
-              rules={[
-                {
-                  required: true,
-                  message: '请输入定时规则',
-                },
-              ]}>
-              {showRuleConfig ? <Input placeholder='请输入定时规则'/> : <InputNumber
-                min={0}
-                addonAfter={suffixSelector}
-                placeholder='请输入定时规则'
-                style={{ width: '100%',}}
-              />}
-            </Form.Item>
-          </Col>
-          <Col span={8} style={{marginLeft: '20px'}}>
-            <Form.Item labelCol={{span: 3}}>
-              <Checkbox checked={showRuleConfig} onChange={(e) => {setShowRuleConfig(e.target.checked)}}>高级配置</Checkbox>
-            </Form.Item>
-          </Col>
-        </Row>
-        
-        <Form.Item
-          label="同步任务名称"
-          name="job_desc"
-          rules={[
-            {
-              required: true,
-              message: '请输入定时规则',
-            },
-          ]}>
-          <Input placeholder='请输入同步任务名称' />
-        </Form.Item>
+        <FormItem label="JVM启动参数" name="jvm_param">
+          <Input placeholder="-Xms1024m -Xmx1024m -XX:+HeapDumpOnOutOfMemoryError" />
+        </FormItem>
       </Form>
-    </>
+    </div>
   )
 }

+ 40 - 19
src/module/datasource/component/StepOne.jsx

@@ -2,7 +2,11 @@ import React, { useState, useEffect } from 'react'
 import { Form, Select, Input, message } from 'antd'
 import { getTableNamesList } from '../services'
 
-export default function StepOne({ drawDataForm, dataSourceList, updateTableStruct }) {
+export default function StepOne({
+  drawDataForm,
+  dataSourceList,
+  updateTableStruct,
+}) {
   const [datasourceType, setDatasourceType] = useState(null)
 
   const [datasources, setDatasources] = useState()
@@ -12,12 +16,14 @@ export default function StepOne({ drawDataForm, dataSourceList, updateTableStruc
   // 获取数据源列表
   useEffect(() => {
     if (dataSourceList.length !== 0) {
-      setDatasources(dataSourceList.map(item => {
-        return {
-          label: item.datasource_name,
-          value: item.key
-        }
-      }))
+      setDatasources(
+        dataSourceList.map(item => {
+          return {
+            label: item.datasource_name,
+            value: item.key,
+          }
+        })
+      )
     }
   }, [dataSourceList])
 
@@ -34,15 +40,15 @@ export default function StepOne({ drawDataForm, dataSourceList, updateTableStruc
   }, [])
 
   // 选择数据源
-  const selectDatasource = async (val) => {
+  const selectDatasource = async val => {
     drawDataForm.setFieldValue('datasource_table', null)
     const type = dataSourceList.find(item => item.key === val).datasource
     setDatasourceType(type)
     const { data } = await getTableNamesList(val)
-    if(data.code === 200) {
+    if (data.code === 200) {
       const list = data.data.map(item => ({
         label: item,
-        value: item
+        value: item,
       }))
       setTableList(list)
     } else {
@@ -51,7 +57,7 @@ export default function StepOne({ drawDataForm, dataSourceList, updateTableStruc
   }
 
   // 选择表
-  const selectTable = (val) => {
+  const selectTable = val => {
     const ds_id = drawDataForm.getFieldValue('datasource_name')
     updateTableStruct(ds_id, val)
   }
@@ -79,9 +85,8 @@ export default function StepOne({ drawDataForm, dataSourceList, updateTableStruc
           query_sql: '',
           where_param: '',
           reader_split_pk: '',
-          delimiter: ''
-        }}
-      >
+          delimiter: '',
+        }}>
         <Form.Item
           label="选择数据源"
           name="datasource_name"
@@ -106,7 +111,12 @@ export default function StepOne({ drawDataForm, dataSourceList, updateTableStruc
               message: '请选择表',
             },
           ]}>
-          <Select options={tableList} allowClear onSelect={selectTable} placeholder="请选择数据表"/>
+          <Select
+            options={tableList}
+            allowClear
+            onSelect={selectTable}
+            placeholder="请选择数据表"
+          />
         </Form.Item>
         {datasourceType === 'mysql' ? (
           <>
@@ -114,16 +124,19 @@ export default function StepOne({ drawDataForm, dataSourceList, updateTableStruc
               label="查询字段"
               name="query_sql"
               wrapperCol={{ span: 10 }}>
-              <Input.TextArea rows={5} placeholder="sql查询,一般用于多表关联查询时才用"/>
+              <Input.TextArea
+                rows={5}
+                placeholder="sql查询,一般用于多表关联查询时才用"
+              />
             </Form.Item>
             <Form.Item label="切分主键" name="reader_split_pk">
-              <Input placeholder='切分主键' />
+              <Input placeholder="切分主键" />
             </Form.Item>
             <Form.Item
               label="条件语句"
               name="where_param"
               wrapperCol={{ span: 10 }}>
-              <Input.TextArea rows={5} placeholder="where条件"/>
+              <Input.TextArea rows={5} placeholder="where条件" />
             </Form.Item>
           </>
         ) : (
@@ -170,7 +183,15 @@ export default function StepOne({ drawDataForm, dataSourceList, updateTableStruc
                 placeholder="文件的类型"
               />
             </Form.Item>
-            <Form.Item label="分隔符" name="reader_field_delimiter">
+            <Form.Item
+              label="分隔符"
+              name="reader_field_delimiter"
+              rules={[
+                {
+                  required: true,
+                  message: '请输入分隔符!',
+                },
+              ]}>
               <Input placeholder="读取字段的分隔符" />
             </Form.Item>
           </>

+ 16 - 3
src/module/datasource/component/StepThree.jsx

@@ -57,7 +57,7 @@ const StepThreeDiv = styled.div`
     text-overflow: ellipsis;
     overflow: hidden;
     white-space: nowrap;
-}
+  }
   .field__sync {
     background: #eef5fe;
   }
@@ -86,7 +86,12 @@ const StepThreeDiv = styled.div`
   }
 `
 
-export default function StepThree({ onRef, drawDataForm, loadDataForm }) {
+export default function StepThree({
+  onRef,
+  drawDataForm,
+  loadDataForm,
+  syncMapSaving,
+}) {
   const [datasourceFields, setDatasourceFields] = useState([])
   const [syncFields, setSyncFields] = useState([])
   // 映射管理
@@ -143,6 +148,10 @@ export default function StepThree({ onRef, drawDataForm, loadDataForm }) {
     }
   }, [loadDataForm])
 
+  useEffect(() => {
+    setSyncMappings(syncMapSaving)
+  }, [syncMapSaving])
+
   const syncFieldData = index => {
     const datasourceField = `${datasourceFields[index].value}: ${datasourceFields[index].type}`
     const syncField = `${syncFields[index].value}: ${syncFields[index].type}`
@@ -197,10 +206,14 @@ export default function StepThree({ onRef, drawDataForm, loadDataForm }) {
             </ReactSortable>
             <div>
               {datasourceFields.map((item, index) => {
+                const sourceType = datasourceFields[index]?.type.split('(')[0]
+                const syncType = syncFields[index]?.type.split('(')[0]
                 const sameType =
                   index > syncFields.length - 1
                     ? false
-                    : datasourceFields[index]?.type === syncFields[index]?.type
+                    : sourceType === syncType ||
+                      (['varchar', 'string'].includes(sourceType) &&
+                        ['varchar', 'string'].includes(syncType))
                 return (
                   <div className="sync__img" key={item?.key}>
                     <img

+ 44 - 13
src/module/datasource/component/StepTwo.jsx

@@ -2,7 +2,11 @@ import React, { useState, useEffect } from 'react'
 import { Form, Select, Input, message } from 'antd'
 import { getTableNamesList } from '../services'
 
-export default function LoadFormConfig({ loadDataForm, dataSourceList, updateTableStruct }) {
+export default function LoadFormConfig({
+  loadDataForm,
+  dataSourceList,
+  updateTableStruct,
+}) {
   const [datasourceType, setDatasourceType] = useState(null)
 
   const [datasources, setDatasources] = useState()
@@ -53,7 +57,7 @@ export default function LoadFormConfig({ loadDataForm, dataSourceList, updateTab
   }
 
   // 选择表
-  const selectTable = (val) => {
+  const selectTable = val => {
     const ds_id = loadDataForm.getFieldValue('datasource_name')
     updateTableStruct(ds_id, val)
   }
@@ -80,9 +84,8 @@ export default function LoadFormConfig({ loadDataForm, dataSourceList, updateTab
         initialValues={{
           pre_sql: '',
           post_sql: '',
-          writer_field_delimiter: ''
-        }}
-      >
+          writer_field_delimiter: '',
+        }}>
         <Form.Item
           label="选择数据源"
           name="datasource_name"
@@ -95,7 +98,7 @@ export default function LoadFormConfig({ loadDataForm, dataSourceList, updateTab
           <Select
             options={datasources}
             onSelect={selectDatasource}
-            placeholder='请选择数据源'
+            placeholder="请选择数据源"
             allowClear
           />
         </Form.Item>
@@ -108,15 +111,23 @@ export default function LoadFormConfig({ loadDataForm, dataSourceList, updateTab
               message: '请选择表',
             },
           ]}>
-          <Select options={tableList} allowClear onSelect={selectTable} placeholder='请选择数据表' />
+          <Select
+            options={tableList}
+            allowClear
+            onSelect={selectTable}
+            placeholder="请选择数据表"
+          />
         </Form.Item>
         {datasourceType === 'mysql' ? (
           <>
             <Form.Item label="pre_sql" name="pre_sql" wrapperCol={{ span: 10 }}>
-              <Input.TextArea rows={5} placeholder='请输入pre_sql语句'/>
+              <Input.TextArea rows={5} placeholder="请输入pre_sql语句" />
             </Form.Item>
-            <Form.Item label="post_sql" name="post_sql" wrapperCol={{ span: 10 }}>
-              <Input.TextArea rows={5} placeholder='请输入post_sql语句'/>
+            <Form.Item
+              label="post_sql"
+              name="post_sql"
+              wrapperCol={{ span: 10 }}>
+              <Input.TextArea rows={5} placeholder="请输入post_sql语句" />
             </Form.Item>
           </>
         ) : (
@@ -144,6 +155,17 @@ export default function LoadFormConfig({ loadDataForm, dataSourceList, updateTab
               ]}>
               <Input placeholder="Hadoop hdfs文件系统namenode节点地址" />
             </Form.Item>
+            <Form.Item
+              label="写入文件名"
+              name="writer_filename"
+              rules={[
+                {
+                  required: true,
+                  message: '请输入文件名称',
+                },
+              ]}>
+              <Input placeholder="请输入文件名称" />
+            </Form.Item>
             <Form.Item
               label="文件类型"
               name="writer_file_type"
@@ -174,14 +196,23 @@ export default function LoadFormConfig({ loadDataForm, dataSourceList, updateTab
               ]}>
               <Select
                 options={[
-                  { label: '追加', value: 'append' },
-                  { label: '覆盖', value: 'cover' },
+                  { label: 'append', value: 'append' },
+                  { label: 'nonConflict', value: 'nonConflict' },
+                  { label: 'truncate', value: 'truncate' },
                 ]}
                 allowClear
                 placeholder="写入模式"
               />
             </Form.Item>
-            <Form.Item label="分隔符" name="writer_field_delimiter">
+            <Form.Item
+              label="分隔符"
+              name="writer_field_delimiter"
+              rules={[
+                {
+                  required: true,
+                  message: '请输入分隔符!',
+                },
+              ]}>
               <Input placeholder="读取字段的分隔符" />
             </Form.Item>
           </>

+ 58 - 11
src/module/datasource/component/SyncTaskAdd.jsx

@@ -3,7 +3,7 @@ import StepOne from './StepOne'
 import StepTwo from './StepTwo'
 import StepThree from './StepThree'
 import StepFour from './StepFour'
-import React, { useState, useEffect } from 'react'
+import React, { useState, useEffect, useRef } from 'react'
 import DataTableStruct from './DataTableStruct'
 import styled from 'styled-components'
 import { MenuFoldOutlined, MenuUnfoldOutlined } from '@ant-design/icons'
@@ -37,6 +37,8 @@ export default function SyncTaskAdd() {
   // jsonValue
   const jsonValue = React.createRef()
 
+  const cronRef = useRef()
+
   // 步骤数
   const [currentStep, setCurrentStep] = useState(0)
 
@@ -52,7 +54,11 @@ export default function SyncTaskAdd() {
   // 完成状态
   const [isfinishBuild, setIsFinishBuild] = useState(false)
 
-  const [dataxJson, setDataxJson] = useState({})
+  const [dataxJson, setDataxJson] = useState(null)
+
+  const [partitionNum, setPartitionNum] = useState(0)
+
+  const [cron_data, setCronData] = useState(null)
 
   // 提取源表单表结构
   const [drawDataStruct, setDrawDataStruct] = useState([])
@@ -105,6 +111,13 @@ export default function SyncTaskAdd() {
     setCurrentStep(currentStep + num)
   }
 
+  // 暂存cron表达式
+  const saveCron = () => {
+    if (Object.keys(cronRef.current.getCronData).length !== 0) {
+      setCronData(cronRef.current.getCronData)
+    }
+  }
+
   // 监听步骤设置当前表单
   useEffect(() => {
     switch (currentStep) {
@@ -132,6 +145,12 @@ export default function SyncTaskAdd() {
 
   // 关闭
   const onClose = () => {
+    if (currentStep === 3) {
+      saveCron()
+      if (jsonValue.current) {
+        setDataxJson(JSON.parse(jsonValue.current.jsonValue.editor.getValue()))
+      }
+    }
     setVisible(false)
   }
 
@@ -155,6 +174,8 @@ export default function SyncTaskAdd() {
     fetchDataSourceList()
   }, [])
 
+  const [syncMapSaving, setSyncMapSaving] = useState([])
+
   // reader_columns
   const [reader_columns, setReaderColumns] = useState([])
 
@@ -164,6 +185,7 @@ export default function SyncTaskAdd() {
   // 下一步
   const nextStep = () => {
     if (currentStep === 2) {
+      setSyncMapSaving(stepThreeRef.current.syncMappings)
       setReaderColumns(
         stepThreeRef.current.syncMappings.map(item => {
           return item.key.drawData
@@ -195,9 +217,7 @@ export default function SyncTaskAdd() {
     const reader_tables = [reader_form['datasource_table']]
     const writer_datasource_id = writer_form['datasource_name']
     const writer_tables = [writer_form['datasource_table']]
-    const writer_filename = dataSourceList.find(
-      item => item.key === writer_datasource_id
-    ).datasource_name
+    const writer_filename = writer_form['writer_filename']
     // 基础固定参数
     const base_params = {
       reader_datasource_id,
@@ -291,6 +311,7 @@ export default function SyncTaskAdd() {
 
   // 构建表单
   const build = async () => {
+    saveCron()
     const buildParams = formatBuildData()
     if (!buildParams) {
       message.error('获取表单参数失败')
@@ -299,7 +320,6 @@ export default function SyncTaskAdd() {
     setBuilding(true)
     const { data } = await buildJsonData(buildParams)
     if (data.code === 200) {
-      const jsonData = data.data.json
       message.success('构建成功')
       const json = data.data.json
       setDataxJson(json)
@@ -319,7 +339,9 @@ export default function SyncTaskAdd() {
       ...fields,
       job_json,
       inc_start_time: moment(fields.inc_start_time).unix(),
+      user_id: 'test',
     }
+    params['partition_num'] = partitionNum
     const { data } = await createJob(params)
     if (data.code === 200) {
       message.success('提交成功')
@@ -332,9 +354,22 @@ export default function SyncTaskAdd() {
 
   // 提交表单
   const submit = () => {
-    const jsonEditorValue = jsonValue.current.jsonValue.editor.getValue()
+    if (jsonValue.current) {
+      setDataxJson(JSON.parse(jsonValue.current.jsonValue.editor.getValue()))
+    }
     try {
-      const job_json = JSON.stringify(JSON.parse(jsonEditorValue))
+      let job_json = JSON.stringify(dataxJson)
+      if (jsonValue.current) {
+        job_json = JSON.stringify(
+          JSON.parse(jsonValue.current.jsonValue.editor.getValue())
+        )
+      }
+      if (Object.keys(cronRef.current.getCronData).length !== 0) {
+        currentForm.setFieldValue(
+          'cron_expression',
+          cronRef.current.getCronData
+        )
+      }
       currentForm
         .validateFields()
         .then(() => {
@@ -382,26 +417,38 @@ export default function SyncTaskAdd() {
           onRef={stepThreeRef}
           drawDataForm={drawDataForm}
           loadDataForm={loadDataForm}
+          syncMapSaving={syncMapSaving}
         />
       )}
 
       {/* 设置同步参数 */}
-      {currentStep === 3 && <StepFour syncDataForm={syncDataForm} />}
+      {currentStep === 3 && (
+        <StepFour
+          onRef={cronRef}
+          syncDataForm={syncDataForm}
+          cron_data={cron_data}
+          partitionNumHandle={{
+            num: partitionNum,
+            func: val => setPartitionNum(val),
+          }}
+        />
+      )}
 
       {/* 按扭操作 */}
       {/* 表结构预览 */}
       {(currentStep === 0 || currentStep === 1 || currentStep === 3) && (
         <>
-          {currentStep !== 3 && (
+          {dataxJson !== null && (
             <Button
               type="primary"
               onClick={showDrawer}
               className="check__btn"
               size="large"
               icon={<MenuFoldOutlined />}>
-              表结构预览
+              {currentStep !== 3 ? '表结构预览' : '构建'}
             </Button>
           )}
+
           <Drawer
             title={currentStep === 3 ? '构建' : '表结构预览'}
             placement="right"

+ 9 - 11
src/module/datasource/component/utils/JsonEditor.jsx

@@ -1,10 +1,9 @@
-import React, { useEffect, useImperativeHandle, useRef, useState } from "react";
-import CodeMirror from '@uiw/react-codemirror';
-import 'codemirror/keymap/sublime';
-import 'codemirror/theme/monokai.css';
+import React, { useImperativeHandle, useRef } from 'react'
+import CodeMirror from '@uiw/react-codemirror'
+import 'codemirror/keymap/sublime'
+import 'codemirror/theme/monokai.css'
 
-
-export default function JsonEditor({code, onRef}) {
+export default function JsonEditor({ code, onRef }) {
   const jsonEditor = useRef()
   useImperativeHandle(onRef, () => {
     return {
@@ -13,14 +12,13 @@ export default function JsonEditor({code, onRef}) {
   })
   return (
     <CodeMirror
-      ref={(editor) => jsonEditor.current = editor}
-      value={JSON.stringify(JSON.parse(code),null,2)}
+      ref={editor => (jsonEditor.current = editor)}
+      value={JSON.stringify(JSON.parse(code), null, 2)}
       options={{
         theme: 'monokai',
         keyMap: 'sublime',
         mode: 'json',
       }}
-      // onChange={changeValue}
     />
-  );
-}
+  )
+}

+ 24 - 6
src/module/datasource/page/DatasourceView.jsx

@@ -15,12 +15,23 @@ const Datasource = styled.div`
 export default function DatasourceView() {
   // 数据源管理ref
   const sourceRef = React.createRef()
+  const logRef = React.createRef()
+  // 当前所处页面
+  const [currentKey, setCurrentKey] = useState('1')
+  const [currentLog, setCurrentLog] = useState(null)
+
   // 更新数据源列表
   const updateDataSource = () => {
     sourceRef.current.updateSourceList()
   }
-  // 当前所处页面
-  const [currentKey, setCurrentKey] = useState('1')
+  const updateLogs = () => {
+    logRef.current?.updateLogList()
+  }
+
+  const selectLog = id => {
+    setCurrentLog(id)
+    setCurrentKey('3')
+  }
   // Tab标签右边按钮
   const tabExtraBtn = () => {
     switch (currentKey) {
@@ -33,7 +44,7 @@ export default function DatasourceView() {
           </Link>
         )
       default:
-        return <></>
+        return
     }
   }
   return (
@@ -42,15 +53,22 @@ export default function DatasourceView() {
       <Tabs
         type="card"
         tabBarExtraContent={tabExtraBtn()}
-        onChange={key => setCurrentKey(key)}>
+        activeKey={currentKey}
+        onTabClick={key => {
+          if (key === '3') updateLogs()
+        }}
+        onChange={key => {
+          setCurrentKey(key)
+          setCurrentLog(null)
+        }}>
         <TabPane tab="数据源管理" key="1">
           <DataSourceManage onRef={sourceRef} />
         </TabPane>
         <TabPane tab="同步配置" key="2">
-          <DatasourceSyncView />
+          <DatasourceSyncView selectLog={id => selectLog(id)} />
         </TabPane>
         <TabPane tab="日志管理" key="3">
-          <DatasourceLog />
+          <DatasourceLog onRef={logRef} logId={currentLog} />
         </TabPane>
       </Tabs>
     </Datasource>

+ 113 - 68
src/module/datasource/services/index.js

@@ -1,77 +1,122 @@
 import request from '../../../utils/request'
 
 // 获取数据源列表
-export const getDataSourceList = params => request({
-  url: `/jpt/datasource/`,
-  method: 'get',
-  params
-})
+export const getDataSourceList = params =>
+  request({
+    url: `/jpt/datasource/`,
+    method: 'get',
+    params,
+  })
 
 // 删除指定数据源
-export const delDataSource = id => request({
-  url: `/jpt/datasource/` + id,
-  method: 'delete',
-})
+export const delDataSource = id =>
+  request({
+    url: `/jpt/datasource/` + id,
+    method: 'delete',
+  })
 
 // 测试数据源连接
-export const testDataSourceConnection = params => request({
-  url: '/jpt/datasource/test',
-  method: 'post',
-  data: { ...params }
-})
+export const testDataSourceConnection = params =>
+  request({
+    url: '/jpt/datasource/test',
+    method: 'post',
+    data: { ...params },
+  })
 
 // 创建数据源
-export const createDataSource = params => request({
-  url: `/jpt/datasource/`,
-  method: 'post',
-  data: { ...params }
-})
-
-export const getJobList = params => request({
-  url: 'jpt/jobinfo/',
-  method: 'get',
-  params
-})
-
-export const updateJobList = (id, params) => request({
-  url: 'jpt/jobinfo/' + id,
-  method: 'put',
-  data: { ...params }
-})
-
-export const getTableNamesList = params => request({
-  url: `/jpt/datasource/table_names?ds_id=${params}`,
-  method: 'post',
-})
-
-export const getTableData = params => request({
-  url: `/jpt/datasource/preview?ds_id=${params.id}&table_name=${params.table_name}`,
-  method: 'post',
-})
-
-export const getTableSchema = params => request({
-  url: `/jpt/datasource/table_schema?ds_id=${params.id}&table_name=${params.table_name}`,
-  method: 'post',
-})
-
-export const getJoblog = () => request({
-  url: `/jpt/joblog`,
-  method: 'get',
-})
-
-export const getOnceJoblog = params => request({
-  url: `/jpt/joblog/getOnce/` + params,
-  method: 'get',
-})
-
-export const buildJsonData = params => request({
-  url: '/jpt/datax',
-  method: 'post',
-  data: { ...params }
-})
-
-export const createJob = params => request({
-  url: 'jpt/jobinfo/',
-  method: 'post',
-  data: {...params}
-})
+export const createDataSource = params =>
+  request({
+    url: `/jpt/datasource/`,
+    method: 'post',
+    data: { ...params },
+  })
+
+export const getJobList = params =>
+  request({
+    url: `jpt/jobinfo/?page=${params.page}&size=${params.size}`,
+    method: 'get',
+  })
+
+export const updateJobList = (id, params) =>
+  request({
+    url: 'jpt/jobinfo/' + id,
+    method: 'put',
+    data: { ...params },
+  })
+
+export const updateJobStatus = params =>
+  request({
+    url: 'jpt/jobinfo/update_trigger_status/',
+    method: 'put',
+    data: params,
+  })
+
+export const delJob = id =>
+  request({
+    url: 'jpt/jobinfo/' + id,
+    method: 'delete',
+  })
+
+export const getJobInfo = id =>
+  request({
+    url: `jpt/jobinfo/info?job_id=${id}`,
+    method: 'get',
+  })
+
+export const getTableNamesList = params =>
+  request({
+    url: `/jpt/datasource/table_names?ds_id=${params}`,
+    method: 'post',
+  })
+
+export const getTableData = params =>
+  request({
+    url: `/jpt/datasource/preview?ds_id=${params.id}&table_name=${params.table_name}`,
+    method: 'post',
+  })
+
+export const getTableSchema = params =>
+  request({
+    url: `/jpt/datasource/table_schema?ds_id=${params.id}&table_name=${params.table_name}`,
+    method: 'post',
+  })
+
+export const getJoblog = params =>
+  request({
+    url: `/jpt/joblog/?page=${params.page}&size=${params.size}${
+      params.id ? '&job_id=' + params.id : ''
+    }`,
+    method: 'get',
+  })
+
+export const getOnceJoblog = id =>
+  request({
+    url: `/jpt/joblog/getOnce?run_id=${id}`,
+    method: 'get',
+  })
+
+export const buildJsonData = params =>
+  request({
+    url: '/jpt/datax',
+    method: 'post',
+    data: { ...params },
+  })
+
+export const createJob = params =>
+  request({
+    url: 'jpt/jobinfo/',
+    method: 'post',
+    data: { ...params },
+  })
+
+export const executeOnce = id =>
+  request({
+    url: `jpt/jobinfo/execute?job_id=${id}`,
+    method: 'post',
+  })
+
+export const refreshLogsStatus = str =>
+  request({
+    url: '/jpt/joblog/logs_status/' + str,
+    method: 'get',
+  })

+ 91 - 16
src/module/tasklog/component/LogTable.jsx

@@ -2,7 +2,10 @@ import React, { useState, useEffect } from 'react'
 import { Table, Space, message } from 'antd'
 import moment from 'moment'
 import { useNavigate, useLocation } from 'react-router-dom'
-import { getLogList } from '../services/index'
+import { getLogList, refreshLogsStatus } from '../services/index'
+import styled from 'styled-components'
+
+const LogList = styled.div``
 
 const LogTable = () => {
   // 初始化日志列表
@@ -10,6 +13,10 @@ const LogTable = () => {
   // 表格Loading状态
   const [dataLoading, setDataLoading] = useState(false)
 
+  const [currentPage, setCurrentPage] = useState(1)
+  const [currentPageSize, setCurrentPageSize] = useState(10)
+  const [dataTotal, setDataTotal] = useState(0)
+
   const navigate = useNavigate()
 
   const { state } = useLocation()
@@ -18,7 +25,9 @@ const LogTable = () => {
     message.success('终止成功')
   }
   const checkLog = id => {
-    navigate('/task-log/log-watcher', { state: { id } })
+    navigate('/task-log/log-watcher', {
+      state: { id },
+    })
   }
   const columns = [
     {
@@ -66,9 +75,17 @@ const LogTable = () => {
       render: code => (
         <span
           style={{
-            color: code === 0 ? '#FF4D4F' : code === 1 ? '#52C41A' : '#4A4A4A',
+            color: code === 3 ? '#FF4D4F' : code === 2 ? '#52C41A' : '#4A4A4A',
           }}>
-          {code === 0 ? '失败' : code === 1 ? '成功' : '无'}
+          {code === 3
+            ? '失败'
+            : code === 2
+            ? '成功'
+            : code === 1
+            ? '运行中'
+            : code === 0
+            ? '队列中'
+            : '暂无'}
         </span>
       ),
     },
@@ -96,42 +113,100 @@ const LogTable = () => {
     },
   ]
 
-  const fetchJoblog = async () => {
+  const fetchJoblog = async (page = 1, pageSize = 10) => {
     setDataLoading(true)
-    const { data } = await getLogList(state?.id)
+    const { data } = await getLogList({
+      page: page,
+      size: pageSize,
+      id: state?.id,
+    })
     if (data.code === 200) {
-      const list = data.data.map(item => {
+      setDataTotal(data.data.extra_data.total)
+      setCurrentPage(data.data.extra_data.page)
+      setCurrentPageSize(data.data.extra_data.size)
+      const refreshLogs = []
+      const list = data.data.item.map(item => {
+        if ([0, 1].includes(item.execute_result)) {
+          refreshLogs.push(item.id)
+        }
         return {
           key: item.id,
           jobName: item.job_name,
-          triggerTime: moment(item.trigger_time * 1000).format(
+          triggerTime: moment(Number(item.trigger_time) * 1000).format(
             'YYYY.MM.DD HH:MM'
           ),
           triggerResult: item.trigger_result,
-          handleTime: moment(item.executor_time * 1000).format(
+          handleTime: moment(Number(item.execute_time) * 1000).format(
             'YYYY.MM.DD HH:MM'
           ),
-          handleResult: item.executor_result,
+          handleResult: item.execute_result,
           taskType: item.job_type,
           taskTag: item.job_tag,
+          afJobId: item.af_job_id,
+          runId: item.run_id,
         }
       })
       setLogList(list)
+      refreshStatus(refreshLogs)
     }
     setDataLoading(false)
   }
 
+  const refreshStatus = async refreshLogs => {
+    if (refreshLogs.length !== 0) {
+      const { data } = await refreshLogsStatus(refreshLogs.toString())
+      if (data.code === 200) {
+        const list = []
+        let logs = []
+        console.log(data.data)
+        const keys = Object.keys(data.data)
+        keys.forEach(item => {
+          if ([2, 3].includes(data.data[item])) {
+            logs = logList.map(logData => {
+              if (logData.id === item) {
+                logData.execute_result = data.data[item]
+              }
+              return logData
+            })
+          } else {
+            list.push(item)
+          }
+        })
+        if (logs.length !== 0) {
+          setLogList(logs)
+        }
+        setTimeout(() => {
+          refreshStatus(list)
+        }, 10000)
+      } else {
+        message.error(data.msg)
+      }
+    }
+  }
+
+  const pageChange = (page, pageSize) => {
+    fetchJoblog(page, pageSize)
+  }
+
   useEffect(() => {
     fetchJoblog()
   }, [])
 
   return (
-    <Table
-      columns={columns}
-      dataSource={logList}
-      bordered
-      loading={dataLoading}
-    />
+    <LogList>
+      <Table
+        columns={columns}
+        dataSource={logList}
+        bordered
+        loading={dataLoading}
+        pagination={{
+          current: currentPage,
+          total: dataTotal,
+          pageSize: currentPageSize,
+          onChange: pageChange,
+        }}
+      />
+    </LogList>
   )
 }
 

+ 25 - 25
src/module/tasklog/component/TaskLogWatcher.jsx

@@ -2,12 +2,13 @@ import { Card, message, Table, Tree } from 'antd'
 import React, { useEffect, useState } from 'react'
 import { useLocation } from 'react-router-dom'
 import styled from 'styled-components'
-import { getLogInfo, getJobLog } from '../services/index'
+import { getLogInfo } from '../services/index'
+import moment from 'moment'
 
 const LogWrapper = styled.div`
   padding: 20px;
   display: flex;
-  height: 800px;
+  height: 100vh;
   column-gap: 20px;
   .job-list {
     flex: 1;
@@ -53,7 +54,7 @@ const TaskLogWatcher = () => {
   // 选中的作业
   const [selectJob, setSelectJob] = useState(null)
   // 单/多作业类型
-  const [taskType, setTaskType] = useState(null)
+  // const [taskType, setTaskType] = useState(null)
 
   const [treeData, setTreeData] = useState([])
 
@@ -64,9 +65,9 @@ const TaskLogWatcher = () => {
       key: 'index',
     },
     {
-      title: '作业名称',
-      dataIndex: 'jobName',
-      key: 'jobName',
+      title: '执行时间',
+      dataIndex: 'executeTime',
+      key: 'executeTime',
     },
     {
       title: '执行结果',
@@ -86,23 +87,28 @@ const TaskLogWatcher = () => {
   const fetchOnceJoblog = async () => {
     const { data } = await getLogInfo(state.id)
     if (data.code === 200) {
-      setTaskType(data.data.job_type)
-      if (data.data.job_type === '单作业离线任务')
-        formatSingleTask(data.data.logs)
-      if (data.data.job_type === '多作业离线任务')
-        formatMultiTask(data.data.logs)
+      // setTaskType(data.data.job_type)
+      // if (data.data.job_type === '单作业离线任务')
+      //   formatSingleTask(data.data.logs)
+      // if (data.data.job_type === '多作业离线任务')
+      //   formatMultiTask(data.data.logs)
+      formatSingleTask(data.data)
     } else {
       message.error(data.msg)
     }
   }
 
   const formatSingleTask = data => {
+    console.log(data)
     const list = data.map((item, index) => ({
-      key: item.id,
+      key: index,
+      id: item.id,
       index: index + 1,
-      jobName: item.node_name,
-      job_log_uri: item.job_log_uri,
-      handleResult: item.executor_result,
+      executeTime: moment(Number(item.execute_time) * 1000).format(
+        'YYYY.MM.DD HH:MM'
+      ),
+      log: item.log,
+      handleResult: item.execute_result,
     }))
     setJobList(list)
     onClickTableNode(list[0])
@@ -128,19 +134,12 @@ const TaskLogWatcher = () => {
     setLogData('请选择节点查看任务')
   }
 
-  const fetchJobLogInfo = async uri => {
-    const { data } = await getJobLog(uri)
-    setLogData(data)
-  }
-
   const onClickTableNode = record => {
     setSelectJob(record.key)
-    fetchJobLogInfo(record.job_log_uri)
+    setLogData(record.log)
   }
 
-  const onClickTreeNode = (_, info) => {
-    if (info.node.uri) fetchJobLogInfo(info.node.uri)
-  }
+  const onClickTreeNode = (_, info) => {}
 
   useEffect(() => {
     fetchOnceJoblog()
@@ -183,7 +182,8 @@ const TaskLogWatcher = () => {
             fontSize: '14px',
             fontWeight: '700',
           }}>
-          {taskType === '单作业离线任务' ? singleTable : multiTree}
+          {/* {taskType === '单作业离线任务' ? singleTable : multiTree} */}
+          {singleTable}
         </Card>
       </div>
       <div className="log-info">

+ 11 - 3
src/module/tasklog/services/index.js

@@ -1,16 +1,18 @@
 import request from '../../../utils/request'
 
 // 定时任务日志列表
-export const getLogList = id =>
+export const getLogList = params =>
   request({
-    url: `/jpt/jm_job_log/${id ? '?job_id=' + id : ''}`,
+    url: `/jpt/jm_job_log/?page=${params.page}&size=${params.size}${
+      params.id ? '&job_id=' + params.id : ''
+    }`,
     method: 'get',
   })
 
 // 定时任务日志详情
 export const getLogInfo = id =>
   request({
-    url: `/jpt/jm_job_log/logs?job_history_id=${id}`,
+    url: `/jpt/jm_job_log/logs?run_id=${id}`,
     method: 'get',
   })
 
@@ -20,3 +22,9 @@ export const getJobLog = uri =>
     url: `/jpt/files/jm_job_log/?uri=${uri}`,
     method: 'get',
   })
+
+export const refreshLogsStatus = str =>
+  request({
+    url: '/jpt/jm_job_log/logs_status/' + str,
+    method: 'get',
+  })

+ 8 - 7
src/module/taskmgmt/page/TaskMgmtView.jsx

@@ -50,8 +50,13 @@ const TaskMgmtView = () => {
   const navigate = useNavigate()
 
   // 切换状态
-  const changeTaskState = (checked, id) => {
-    updateJmJobStatus({ id, status: checked ? 1 : 0 })
+  const changeTaskState = async (checked, id) => {
+    const { data } = await updateJmJobStatus({ id, status: checked ? 1 : 0 })
+    if (data.code === 200) {
+      message.success(data.data.status ? '开启成功' : '关闭成功')
+    } else {
+      message.error(data.msg)
+    }
   }
   //执行
   const runTimeOnce = async id => {
@@ -183,11 +188,7 @@ const TaskMgmtView = () => {
             return (
               <img
                 key={index}
-                src={
-                  item.executor_result === 0 || item.executor_result === 0
-                    ? errImgUrl
-                    : successImgUrl
-                }
+                src={item.execute_result !== 2 ? errImgUrl : successImgUrl}
                 alt=""
                 className="icon_wrapper"
               />

+ 64 - 47
src/module/workmgmt/component/JobCreate.jsx

@@ -16,7 +16,7 @@ import styled from 'styled-components'
 import JobStepOne from './JobStepOne'
 import JobStepTwo from './JobStepTwo'
 import JobStepThree from './JobStepThree'
-import { useNavigate, useLocation } from 'react-router'
+import { useNavigate, useLocation } from 'react-router-dom'
 import {
   getJobTypes,
   createJob,
@@ -24,6 +24,8 @@ import {
   getJobInfo,
   deleteTag,
   updateJobInfo,
+  uploadJar,
+  uploadPy,
 } from '../services'
 
 const JobCre = styled.div`
@@ -323,15 +325,18 @@ export default function JobCreate() {
   const DagSubmit = async () => {
     const res = await getDagInfomation(dagData)
     if (res.status === 200) {
-      const nodes = res.data.nodes.filter(item => item.op === 'datasource')
+      const innodes = res.data.nodes.filter(item => item.op === 'datasource')
+      const outnodes = res.data.nodes.filter(item => item.op === 'outputsource')
       const relation_list = []
-      nodes.forEach((item, index) => {
+      innodes.forEach((item, index) => {
         relation_list.push({
           type: 'input',
           datasource_id: InputDataForm.getFieldValue(`datasource${index}`),
           node_uuid: item.id,
           table: InputDataForm.getFieldValue(`sourceTable${index}`),
         })
+      })
+      outnodes.forEach((item, index) => {
         relation_list.push({
           type: 'output',
           datasource_id: OutputDataForm.getFieldValue(`datasource${index}`),
@@ -347,7 +352,7 @@ export default function JobCreate() {
         user_id: 'test',
         project_id: 'test',
         // 镜像
-        image_url: '/test/images/exa',
+        image_url: '',
         relation_list,
         execute_command: '',
       }
@@ -430,14 +435,28 @@ export default function JobCreate() {
   const finishSubmit = async () => {
     const params = {
       ...currentForm.getFieldValue(),
-      script_file: scriptFile,
       type: currentJobType,
-      dag_uuid: 'test',
+      dag_uuid: '',
       user_id: 'test',
       project_id: 'test',
-      dag_url: 'test',
+      dag_url: '',
       relation_list: [],
     }
+    if (radioValue === 'fromList') {
+      params['script_file'] = scriptFile
+    } else {
+      const file = new FormData()
+      fileList.forEach(item => {
+        file.append('file', item)
+      })
+      const { data } =
+        currentJobType === 'Java' ? await uploadJar(file) : await uploadPy(file)
+      if (data.code === 200) {
+        params['script_file'] = data.data
+      } else {
+        message.error(data.msg)
+      }
+    }
     let res = {}
     if (state?.id) {
       res = await updateJobInfo(state.id, params)
@@ -456,10 +475,10 @@ export default function JobCreate() {
     currentForm
       .validateFields()
       .then(() => {
-        if (radioValue === 'fromList' || fileList.length > 0) {
+        if (scriptFile !== null || fileList.length > 0) {
           finishSubmit()
         } else {
-          message.error('请选择本地文件')
+          message.error('请选择本')
         }
       })
       .catch(err => {
@@ -651,7 +670,7 @@ export default function JobCreate() {
                 },
               ]}>
               <Select placeholder="请选择镜像..." allowClear>
-                <Option value="test">test</Option>
+                <Option value="SXKJ:32775/java:1.0">/java:1.0</Option>
               </Select>
             </FormItem>
 
@@ -668,15 +687,21 @@ export default function JobCreate() {
               <RadioGroup onChange={onChange}>
                 <Space direction="vertical" className="jsSel">
                   <Radio value={'fromList'}>
-                    <Select
-                      placeholder="在工作目录中选择"
-                      allowClear
-                      className="javaSelect"
-                      value={scriptFile}
-                      onChange={onScriptFileChange}
-                      disabled={radioValue === 'fromList' ? false : true}>
-                      <Option value="test">test</Option>
-                    </Select>
+                    <span
+                      onClick={e => {
+                        e.preventDefault()
+                        e.stopPropagation()
+                      }}>
+                      <Select
+                        placeholder="在工作目录中选择"
+                        allowClear
+                        className="javaSelect"
+                        value={scriptFile}
+                        onChange={onScriptFileChange}
+                        disabled={radioValue === 'fromList' ? false : true}>
+                        <Option value="test">test</Option>
+                      </Select>
+                    </span>
                   </Radio>
 
                   <Radio value={'fromLocal'}>
@@ -692,15 +717,7 @@ export default function JobCreate() {
                 </Space>
               </RadioGroup>
             </FormItem>
-            <FormItem
-              label="执行命令"
-              name="execute_command"
-              rules={[
-                {
-                  required: true,
-                  message: '请配置执行命令',
-                },
-              ]}>
+            <FormItem label="执行命令" name="execute_command">
               <Input placeholder="请输入执行命令..." allowClear></Input>
             </FormItem>
             <FormItem {...tailLayout}>
@@ -798,7 +815,9 @@ export default function JobCreate() {
                 },
               ]}>
               <Select placeholder="请选择镜像..." allowClear>
-                <Option value="test">test</Option>
+                <Option value="SXKJ:32775/pod_python:1.1">
+                  /pod_python:1.1
+                </Option>
               </Select>
             </FormItem>
 
@@ -815,15 +834,21 @@ export default function JobCreate() {
               <RadioGroup onChange={onChange} className="PyScript_file">
                 <Space direction="vertical">
                   <Radio value={'fromList'}>
-                    <Select
-                      placeholder="在工作目录中选择"
-                      allowClear
-                      className="javaSelect"
-                      value={scriptFile}
-                      onChange={onScriptFileChange}
-                      disabled={radioValue === 'fromList' ? false : true}>
-                      <Option value="test">test</Option>
-                    </Select>
+                    <span
+                      onClick={e => {
+                        e.preventDefault()
+                        e.stopPropagation()
+                      }}>
+                      <Select
+                        placeholder="在工作目录中选择"
+                        allowClear
+                        className="javaSelect"
+                        value={scriptFile}
+                        onChange={onScriptFileChange}
+                        disabled={radioValue === 'fromList' ? false : true}>
+                        <Option value="test">test</Option>
+                      </Select>
+                    </span>
                   </Radio>
                   <Radio value={'fromLocal'}>
                     <Upload {...UpProps}>
@@ -838,15 +863,7 @@ export default function JobCreate() {
                 </Space>
               </RadioGroup>
             </FormItem>
-            <FormItem
-              label="执行命令"
-              name="execute_command"
-              rules={[
-                {
-                  required: true,
-                  message: '请配置执行命令',
-                },
-              ]}>
+            <FormItem label="执行命令" name="execute_command">
               <Input placeholder="请输入执行命令..." allowClear></Input>
             </FormItem>
             <FormItem {...tailLayout}>

+ 9 - 5
src/module/workmgmt/component/JobItems.jsx

@@ -1,10 +1,8 @@
-/* eslint-disable jsx-a11y/anchor-is-valid */
 import { Button, Form, Input, message, Popconfirm, Table } from 'antd'
 import React, { useContext, useEffect, useRef, useState } from 'react'
 import styled from 'styled-components'
-import { Link } from 'react-router-dom'
+import { Link, useNavigate } from 'react-router-dom'
 import { getJobDataList, deleteJob } from '../services'
-import { useNavigate } from 'react-router'
 const EditableContext = React.createContext(null)
 const FormItem = Form.Item
 
@@ -13,6 +11,10 @@ const JobItems = styled.div`
   .delButton {
     margin-left: 40px;
   }
+  .actionBtn {
+    cursor: pointer;
+    color: #1881da;
+  }
   .ant-table-wrapper {
     margin-right: 12px;
     .ant-table {
@@ -195,14 +197,16 @@ const App = () => {
       render: (_, record) =>
         dataSource.length >= 1 ? (
           <>
-            <Button onClick={() => editJobitem(record.key)}>编辑</Button>
+            <span className="actionBtn" onClick={() => editJobitem(record.key)}>
+              编辑
+            </span>
 
             <Popconfirm
               title="确认删除?"
               okText="确认"
               cancelText="取消"
               onConfirm={() => handleDelete(record.key)}>
-              <a className="delButton">删除</a>
+              <span className="delButton actionBtn">删除</span>
             </Popconfirm>
           </>
         ) : null,

+ 16 - 11
src/module/workmgmt/component/JobStepThree.jsx

@@ -13,6 +13,7 @@ import syncUrl from '../style/img/sync.png'
 const StepTwo = styled.div`
   display: flex;
   .StepTwo-content {
+    min-width: 50%;
     margin: 20px 30px 0;
     padding: 20px;
     height: 450px;
@@ -110,6 +111,10 @@ const StepTwo = styled.div`
       .Dagdatasource {
         margin-left: 10px;
       }
+      .nonData {
+        color: transparent;
+        background: transparent;
+      }
     }
   }
 `
@@ -140,7 +145,9 @@ export default function JobStepTwo({ OutputDataForm, dagData, checkSync }) {
   const getDagInfo = async dag_url => {
     const data = await getDagInfomation(dag_url)
     if (data.status === 200) {
-      const dag_nodes = data.data.nodes.filter(item => item.op === 'datasource')
+      const dag_nodes = data.data.nodes.filter(
+        item => item.op === 'outputsource'
+      )
       setNodes(dag_nodes)
     }
   }
@@ -200,8 +207,8 @@ export default function JobStepTwo({ OutputDataForm, dagData, checkSync }) {
     if (list.length > datasourceSchemas.length) {
       list.forEach((item, index) => {
         syncList.push({
-          datasource: item,
-          dag: datasourceSchemas[index],
+          datasource: datasourceSchemas[index],
+          dag: item,
           result: false,
         })
       })
@@ -334,10 +341,9 @@ export default function JobStepTwo({ OutputDataForm, dagData, checkSync }) {
           {selectedNode?.map((item, index) => (
             <div key={index} className="detail-item">
               <div
-                className="dataSource"
-                style={{
-                  display: item?.datasource?.dataField ? 'block' : 'none',
-                }}>
+                className={`dataSource ${
+                  item?.datasource?.dataField ? '' : 'nonData'
+                }`}>
                 <Tooltip
                   title={`${item?.datasource?.dataField}.${item?.datasource?.dataType}`}>
                   <span>{`${item?.datasource?.dataField}.${item?.datasource?.dataType}`}</span>
@@ -351,10 +357,9 @@ export default function JobStepTwo({ OutputDataForm, dagData, checkSync }) {
                 />
               </span>
               <div
-                className="dataSource Dagdatasource"
-                style={{
-                  display: item?.dag?.dataField ? 'block' : 'none',
-                }}>
+                className={`dataSource Dagdatasource ${
+                  item?.dag?.dataField ? '' : 'nonData'
+                }`}>
                 <span>{`${item?.dag?.dataField}.${item?.dag?.dataType}`}</span>
               </div>
             </div>

+ 13 - 10
src/module/workmgmt/component/JobStepTwo.jsx

@@ -13,6 +13,7 @@ import syncUrl from '../style/img/sync.png'
 const StepTwo = styled.div`
   display: flex;
   .StepTwo-content {
+    min-width: 50%;
     margin: 20px 30px 0;
     padding: 20px;
     height: 450px;
@@ -110,6 +111,10 @@ const StepTwo = styled.div`
       .Dagdatasource {
         margin-left: 10px;
       }
+      .nonData {
+        color: transparent;
+        background: transparent;
+      }
     }
   }
 `
@@ -200,8 +205,8 @@ export default function JobStepTwo({ InputDataForm, dagData, checkSync }) {
     if (list.length > datasourceSchemas.length) {
       list.forEach((item, index) => {
         syncList.push({
-          datasource: item,
-          dag: datasourceSchemas[index],
+          datasource: datasourceSchemas[index],
+          dag: item,
           result: false,
         })
       })
@@ -334,10 +339,9 @@ export default function JobStepTwo({ InputDataForm, dagData, checkSync }) {
           {selectedNode?.map((item, index) => (
             <div key={index} className="detail-item">
               <div
-                className="dataSource"
-                style={{
-                  display: item?.datasource?.dataField ? 'block' : 'none',
-                }}>
+                className={`dataSource ${
+                  item?.datasource?.dataField ? '' : 'nonData'
+                }`}>
                 <Tooltip
                   title={`${item?.datasource?.dataField}.${item?.datasource?.dataType}`}>
                   <span>{`${item?.datasource?.dataField}.${item?.datasource?.dataType}`}</span>
@@ -351,10 +355,9 @@ export default function JobStepTwo({ InputDataForm, dagData, checkSync }) {
                 />
               </span>
               <div
-                className="dataSource Dagdatasource"
-                style={{
-                  display: item?.dag?.dataField ? 'block' : 'none',
-                }}>
+                className={`dataSource Dagdatasource ${
+                  item?.dag?.dataField ? '' : 'nonData'
+                }`}>
                 <span>{`${item?.dag?.dataField}.${item?.dag?.dataType}`}</span>
               </div>
             </div>

+ 14 - 0
src/module/workmgmt/services/index.js

@@ -83,3 +83,17 @@ export const updateJobInfo = (id, params) =>
     method: 'put',
     data: { ...params },
   })
+
+export const uploadJar = params =>
+  request({
+    url: '/jpt/files/java/',
+    method: 'post',
+    data: params,
+  })
+
+export const uploadPy = params =>
+  request({
+    url: '/jpt/files/python/',
+    method: 'post',
+    data: params,
+  })

+ 5 - 0
src/routes/index.js

@@ -1,5 +1,6 @@
 import DatasourceView from '../module/datasource/page/DatasourceView.jsx'
 import SyncTaskAdd from '../module/datasource/component/SyncTaskAdd.jsx'
+import EditSyncView from '../module/datasource/component/EditSyncView.jsx'
 import LogWatcher from '../module/datasource/component/LogWatcher.jsx'
 import TaskLog from '../module/tasklog/page/TaskLog.jsx'
 import TaskMgmtView from '../module/taskmgmt/page/TaskMgmtView.jsx'
@@ -22,6 +23,10 @@ export const routes = [
         path: '/datasource/log-watcher',
         component: LogWatcher,
       },
+      {
+        path: '/datasource/sync-edit',
+        component: EditSyncView,
+      },
     ],
   },
   {