mirror of
https://github.com/continew-org/continew-admin-ui.git
synced 2026-01-19 01:13:13 +08:00
refactor: 优化系统监控、代码生成、任务调度相关代码
This commit is contained in:
@@ -4,9 +4,7 @@
|
||||
:title="title"
|
||||
:mask-closable="false"
|
||||
:esc-to-close="false"
|
||||
:modal-style="{ maxWidth: '700px' }"
|
||||
:body-style="{ maxHeight: width >= 700 ? '76vh' : '100vh' }"
|
||||
:width="width >= 700 ? '90%' : '100%'"
|
||||
:width="width >= 600 ? 600 : '100%'"
|
||||
@before-ok="save"
|
||||
@close="reset"
|
||||
>
|
||||
@@ -172,6 +170,7 @@
|
||||
</a-row>
|
||||
</fieldset>
|
||||
</a-form>
|
||||
|
||||
<CronGeneratorModal ref="genModal" @ok="(e) => form.triggerInterval = e" />
|
||||
</a-modal>
|
||||
</template>
|
||||
@@ -179,7 +178,7 @@
|
||||
<script setup lang="ts">
|
||||
import { type ColProps, type FormInstance, Message } from '@arco-design/web-vue'
|
||||
import { useWindowSize } from '@vueuse/core'
|
||||
import { addJob, listGroup, updateJob } from '@/apis/schedule'
|
||||
import { addJob, listGroup, updateJob } from '@/apis/schedule/job'
|
||||
import { useForm } from '@/hooks'
|
||||
import { useDict } from '@/hooks/app'
|
||||
import CronGeneratorModal from '@/components/GenCron/CronModel/index.vue'
|
||||
@@ -187,8 +186,18 @@ import CronGeneratorModal from '@/components/GenCron/CronModel/index.vue'
|
||||
const emit = defineEmits<{
|
||||
(e: 'save-success'): void
|
||||
}>()
|
||||
const colProps: ColProps = { xs: 24, sm: 24, md: 12, lg: 12, xl: 12, xxl: 12 }
|
||||
|
||||
const { width } = useWindowSize()
|
||||
|
||||
const colProps: ColProps = { xs: 24, sm: 24, md: 12, lg: 12, xl: 12, xxl: 12 }
|
||||
|
||||
const dataId = ref()
|
||||
const visible = ref(false)
|
||||
const isUpdate = computed(() => !!dataId.value)
|
||||
const title = computed(() => (isUpdate.value ? '修改任务' : '新增任务'))
|
||||
const formRef = ref<FormInstance>()
|
||||
const groupList = ref()
|
||||
const genModal = ref()
|
||||
const { job_trigger_type_enum, job_task_type_enum, job_route_strategy_enum, job_block_strategy_enum } = useDict(
|
||||
'job_trigger_type_enum',
|
||||
'job_task_type_enum',
|
||||
@@ -196,11 +205,6 @@ const { job_trigger_type_enum, job_task_type_enum, job_route_strategy_enum, job_
|
||||
'job_block_strategy_enum',
|
||||
)
|
||||
|
||||
const dataId = ref()
|
||||
const isUpdate = computed(() => !!dataId.value)
|
||||
const title = computed(() => (isUpdate.value ? '修改任务' : '新增任务'))
|
||||
const formRef = ref<FormInstance>()
|
||||
const genModal = ref()
|
||||
const rules: FormInstance['rules'] = {
|
||||
groupName: [{ required: true, message: '请选择任务组' }],
|
||||
jobName: [{ required: true, message: '请输入任务名称' }],
|
||||
@@ -235,41 +239,31 @@ const reset = () => {
|
||||
resetForm()
|
||||
}
|
||||
|
||||
const groupList = ref()
|
||||
// 查询任务组列表
|
||||
const getGroupList = async () => {
|
||||
const { data } = await listGroup()
|
||||
groupList.value = data?.map((item: string) => ({
|
||||
label: item,
|
||||
value: item,
|
||||
}))
|
||||
}
|
||||
|
||||
const visible = ref(false)
|
||||
// 新增
|
||||
const onAdd = () => {
|
||||
reset()
|
||||
getGroupList()
|
||||
dataId.value = undefined
|
||||
visible.value = true
|
||||
}
|
||||
|
||||
// 修改
|
||||
const onUpdate = async (record: any) => {
|
||||
await getGroupList()
|
||||
reset()
|
||||
dataId.value = record.id
|
||||
Object.assign(form, record)
|
||||
// 切片任务,解析 argsStr 并赋值给 args
|
||||
if (form.taskType === 3 && form.argsStr) {
|
||||
try {
|
||||
const parsedArgs = JSON.parse(form.argsStr)
|
||||
args.value = parsedArgs.map((arg: any) => ({ value: arg }))
|
||||
} catch (error: any) {
|
||||
Message.error(error)
|
||||
}
|
||||
// 触发类型切换
|
||||
const triggerTypeChange = () => {
|
||||
switch (form.triggerType) {
|
||||
case 2:
|
||||
form.triggerInterval = 60
|
||||
break
|
||||
case 3:
|
||||
form.triggerInterval = ''
|
||||
break
|
||||
}
|
||||
visible.value = true
|
||||
}
|
||||
|
||||
// 新增切片参数
|
||||
const onAddArgs = () => {
|
||||
args.value.push({ value: '' })
|
||||
}
|
||||
|
||||
// 删除切片参数
|
||||
const onDeleteArgs = (index) => {
|
||||
args.value.splice(index, 1)
|
||||
}
|
||||
|
||||
// 打开生成表达式
|
||||
const openGeneratorCron = (cron: string) => {
|
||||
genModal.value.open(cron)
|
||||
}
|
||||
|
||||
// 保存
|
||||
@@ -295,30 +289,43 @@ const save = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
// 触发类型切换
|
||||
const triggerTypeChange = () => {
|
||||
switch (form.triggerType) {
|
||||
case 2:
|
||||
form.triggerInterval = 60
|
||||
break
|
||||
case 3:
|
||||
form.triggerInterval = ''
|
||||
break
|
||||
// 查询任务组列表
|
||||
const getGroupList = async () => {
|
||||
const { data } = await listGroup()
|
||||
groupList.value = data?.map((item: string) => ({
|
||||
label: item,
|
||||
value: item,
|
||||
}))
|
||||
}
|
||||
|
||||
// 新增
|
||||
const onAdd = async () => {
|
||||
reset()
|
||||
dataId.value = undefined
|
||||
if (!groupList.value.length) {
|
||||
await getGroupList()
|
||||
}
|
||||
visible.value = true
|
||||
}
|
||||
|
||||
// 新增切片参数
|
||||
const onAddArgs = () => {
|
||||
args.value.push({ value: '' })
|
||||
}
|
||||
// 删除切片参数
|
||||
const onDeleteArgs = (index) => {
|
||||
args.value.splice(index, 1)
|
||||
}
|
||||
|
||||
// 打开生成表达式
|
||||
const openGeneratorCron = (cron: string) => {
|
||||
genModal.value.open(cron)
|
||||
// 修改
|
||||
const onUpdate = async (record: any) => {
|
||||
reset()
|
||||
dataId.value = record.id
|
||||
if (!groupList.value.length) {
|
||||
await getGroupList()
|
||||
}
|
||||
Object.assign(form, record)
|
||||
// 切片任务,解析 argsStr 并赋值给 args
|
||||
if (form.taskType === 3 && form.argsStr) {
|
||||
try {
|
||||
const parsedArgs = JSON.parse(form.argsStr)
|
||||
args.value = parsedArgs.map((arg: any) => ({ value: arg }))
|
||||
} catch (error: any) {
|
||||
Message.error(error)
|
||||
}
|
||||
}
|
||||
visible.value = true
|
||||
}
|
||||
|
||||
defineExpose({ onAdd, onUpdate })
|
||||
|
||||
@@ -40,6 +40,9 @@ import type { JobResp } from '@/apis/schedule'
|
||||
import { useDict } from '@/hooks/app'
|
||||
|
||||
const { width } = useWindowSize()
|
||||
|
||||
const dataDetail = ref<JobResp>()
|
||||
const visible = ref(false)
|
||||
const { job_status_enum, job_trigger_type_enum, job_task_type_enum, job_route_strategy_enum, job_block_strategy_enum } = useDict(
|
||||
'job_status_enum',
|
||||
'job_trigger_type_enum',
|
||||
@@ -48,13 +51,11 @@ const { job_status_enum, job_trigger_type_enum, job_task_type_enum, job_route_st
|
||||
'job_block_strategy_enum',
|
||||
)
|
||||
|
||||
const visible = ref(false)
|
||||
const dataDetail = ref<JobResp>()
|
||||
// 详情
|
||||
const onDetail = (record: JobResp) => {
|
||||
// 打开
|
||||
const onOpen = (record: JobResp) => {
|
||||
dataDetail.value = record
|
||||
visible.value = true
|
||||
}
|
||||
|
||||
defineExpose({ onDetail })
|
||||
defineExpose({ onOpen })
|
||||
</script>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
:data="dataList"
|
||||
:columns="columns"
|
||||
:loading="loading"
|
||||
:scroll="{ x: '100%', y: '100%', minWidth: 1500 }"
|
||||
:scroll="{ x: '100%', y: '100%', minWidth: 1300 }"
|
||||
:pagination="pagination"
|
||||
:disabled-tools="['size']"
|
||||
:disabled-column-keys="['name']"
|
||||
@@ -33,7 +33,7 @@
|
||||
<template #default>新增</template>
|
||||
</a-button>
|
||||
</template>
|
||||
<template #jobName="{ record }">
|
||||
<template v-if="has.hasPermOr(['schedule:job:detail'])" #jobName="{ record }">
|
||||
<a-link @click="onDetail(record)">{{ record.jobName }}</a-link>
|
||||
</template>
|
||||
<template #triggerType="{ record }">
|
||||
@@ -63,12 +63,12 @@
|
||||
</template>
|
||||
<template #action="{ record }">
|
||||
<a-space>
|
||||
<a-link @click="onLog(record)">日志</a-link>
|
||||
<a-link v-permission="['schedule:log:list']" title="日志" @click="onLog(record)">日志</a-link>
|
||||
<a-popconfirm content="是否确定立即执行一次任务?" type="warning" @ok="onTrigger(record)">
|
||||
<a-link v-permission="['schedule:job:trigger']">执行</a-link>
|
||||
<a-link v-permission="['schedule:job:trigger']" title="执行">执行</a-link>
|
||||
</a-popconfirm>
|
||||
<a-link v-permission="['schedule:job:update']" @click="onUpdate(record)">修改</a-link>
|
||||
<a-link v-permission="['schedule:job:delete']" status="danger" @click="onDelete(record)">删除</a-link>
|
||||
<a-link v-permission="['schedule:job:update']" title="修改" @click="onUpdate(record)">修改</a-link>
|
||||
<a-link v-permission="['schedule:job:delete']" status="danger" title="删除" @click="onDelete(record)">删除</a-link>
|
||||
</a-space>
|
||||
</template>
|
||||
</GiTable>
|
||||
@@ -97,6 +97,7 @@ const { job_status_enum, job_trigger_type_enum, job_task_type_enum } = useDict('
|
||||
const queryForm = reactive<JobQuery>({
|
||||
groupName: '',
|
||||
})
|
||||
|
||||
const {
|
||||
tableData: dataList,
|
||||
loading,
|
||||
@@ -104,7 +105,6 @@ const {
|
||||
search,
|
||||
handleDelete,
|
||||
} = useTable((page) => listJob({ ...queryForm, ...page }), { immediate: false })
|
||||
|
||||
const columns: TableInstanceColumns[] = [
|
||||
{
|
||||
title: '序号',
|
||||
@@ -115,17 +115,22 @@ const columns: TableInstanceColumns[] = [
|
||||
{ title: '任务名称', dataIndex: 'jobName', slotName: 'jobName', minWidth: 100, ellipsis: true, tooltip: true },
|
||||
{ title: '调度类型', dataIndex: 'triggerType', slotName: 'triggerType', minWidth: 130 },
|
||||
{ title: '任务类型', dataIndex: 'taskType', slotName: 'taskType', minWidth: 130, ellipsis: true, tooltip: true },
|
||||
{ title: '状态', dataIndex: 'jobStatus', align: 'center', slotName: 'jobStatus' },
|
||||
{ title: '状态', dataIndex: 'jobStatus', slotName: 'jobStatus', align: 'center' },
|
||||
{ title: '描述', dataIndex: 'description', minWidth: 130, ellipsis: true, tooltip: true },
|
||||
{ title: '创建时间', dataIndex: 'createDt', width: 180 },
|
||||
{ title: '修改时间', dataIndex: 'updateDt', width: 180, show: false },
|
||||
{
|
||||
title: '操作',
|
||||
slotName: 'action',
|
||||
width: 130,
|
||||
width: 200,
|
||||
align: 'center',
|
||||
fixed: !isMobile() ? 'right' : undefined,
|
||||
show: has.hasPermOr(['schedule:job:trigger', 'schedule:job:update', 'schedule:job:delete']),
|
||||
show: has.hasPermOr([
|
||||
'schedule:log:list',
|
||||
'schedule:job:trigger',
|
||||
'schedule:job:update',
|
||||
'schedule:job:delete',
|
||||
]),
|
||||
},
|
||||
]
|
||||
|
||||
@@ -151,7 +156,7 @@ const reset = () => {
|
||||
// 删除
|
||||
const onDelete = (record: JobResp) => {
|
||||
return handleDelete(() => deleteJob(record.id), {
|
||||
content: `是否确定删除任务 [${record.jobName}]?`,
|
||||
content: `是否确定删除任务「${record.jobName}」?`,
|
||||
showModal: true,
|
||||
})
|
||||
}
|
||||
@@ -188,13 +193,15 @@ const onUpdate = (record: JobResp) => {
|
||||
const JobDetailDrawerRef = ref<InstanceType<typeof JobDetailDrawer>>()
|
||||
// 详情
|
||||
const onDetail = (record: JobResp) => {
|
||||
JobDetailDrawerRef.value?.onDetail(record)
|
||||
JobDetailDrawerRef.value?.onOpen(record)
|
||||
}
|
||||
|
||||
const router = useRouter()
|
||||
// 日志
|
||||
const onLog = (record: JobResp) => {
|
||||
router.push({ path: '/schedule/log', query: { jobId: record.id, jobName: record.jobName, groupName: record.groupName } })
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getGroupList()
|
||||
})
|
||||
|
||||
@@ -1,20 +1,24 @@
|
||||
<template>
|
||||
<a-modal
|
||||
v-model:visible="visible" title="任务日志详情" :body-style="{ maxHeight: '80vh', overflow: 'auto' }"
|
||||
:width="width >= 1500 ? 1500 : '100%'" :footer="false" @close="closed"
|
||||
<a-drawer
|
||||
v-model:visible="visible"
|
||||
title="任务日志详情"
|
||||
:width="width >= 1300 ? 1300 : '100%'"
|
||||
:footer="false"
|
||||
@close="closed"
|
||||
>
|
||||
<div style="display: flex;">
|
||||
<div style="padding: 10px 10px;">
|
||||
<div class="job_list">
|
||||
<div
|
||||
v-for="item in dataList" :key="item.id" :class="`job_list_item ${item.id === activeId ? 'active' : ''}`"
|
||||
v-for="item in dataList"
|
||||
:key="item.id"
|
||||
:class="`job_list_item ${item.id === activeId ? 'active' : ''}`"
|
||||
@click="onStartInfo(item)"
|
||||
>
|
||||
<div class="content">
|
||||
<span class="title">{{ item.clientInfo.split('@')[1] }}</span>
|
||||
<span class="status">
|
||||
<a-tag bordered :color="statusList[item.taskStatus].color">{{ statusList[item.taskStatus].title
|
||||
}}</a-tag>
|
||||
<a-tag bordered :color="statusList[item.taskStatus].color">{{ statusList[item.taskStatus].title }}</a-tag>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -24,17 +28,19 @@
|
||||
<GiCodeView :code-json="content" />
|
||||
</div>
|
||||
</div>
|
||||
</a-modal>
|
||||
</a-drawer>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useWindowSize } from '@vueuse/core'
|
||||
import dayjs from 'dayjs'
|
||||
import { useWindowSize } from '@vueuse/core'
|
||||
import { type JobInstanceQuery, type JobInstanceResp, type JobLogResp, listJobInstance, listJobInstanceLog } from '@/apis/schedule'
|
||||
|
||||
const { width } = useWindowSize()
|
||||
|
||||
const queryForm = reactive<JobInstanceQuery>({})
|
||||
const dataList = ref<JobInstanceResp[]>([])
|
||||
const visible = ref(false)
|
||||
const loading = ref(false)
|
||||
const activeId = ref<string | number>('')
|
||||
const statusList = {
|
||||
@@ -70,8 +76,6 @@ const statusList = {
|
||||
},
|
||||
}
|
||||
|
||||
const visible = ref(false)
|
||||
|
||||
// 格式化日志
|
||||
const formatLog = (log: any) => {
|
||||
const date = new Date(Number.parseInt(log.time_stamp))
|
||||
@@ -131,22 +135,23 @@ const getInstanceList = async (query: JobInstanceQuery = { ...queryForm }) => {
|
||||
}
|
||||
}
|
||||
|
||||
// 详情
|
||||
const onDetail = (record: JobLogResp) => {
|
||||
visible.value = true
|
||||
// 更新 queryForm
|
||||
queryForm.jobId = record.jobId
|
||||
queryForm.taskBatchId = record.id
|
||||
getInstanceList()
|
||||
}
|
||||
|
||||
const closed = () => {
|
||||
clearInterval(setIntervalNode.value)
|
||||
}
|
||||
onUnmounted(() => {
|
||||
clearInterval(setIntervalNode.value)
|
||||
})
|
||||
defineExpose({ onDetail })
|
||||
|
||||
// 打开
|
||||
const onOpen = async (record: JobLogResp) => {
|
||||
// 更新 queryForm
|
||||
queryForm.jobId = record.jobId
|
||||
queryForm.taskBatchId = record.id
|
||||
visible.value = true
|
||||
await getInstanceList()
|
||||
}
|
||||
|
||||
defineExpose({ onOpen })
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@@ -6,7 +6,7 @@
|
||||
:data="dataList"
|
||||
:columns="columns"
|
||||
:loading="loading"
|
||||
:scroll="{ x: '100%', y: '100%', minWidth: 1500 }"
|
||||
:scroll="{ x: '100%', y: '100%', minWidth: 1300 }"
|
||||
:pagination="pagination"
|
||||
:disabled-tools="['size']"
|
||||
@refresh="search"
|
||||
@@ -42,15 +42,16 @@
|
||||
</template>
|
||||
<template #action="{ record }">
|
||||
<a-space>
|
||||
<a-link @click="onDetail(record)">详情</a-link>
|
||||
<a-link v-permission="['schedule:log:detail']" title="详情" @click="onDetail(record)">详情</a-link>
|
||||
<a-popconfirm content="是否确定停止本次执行?" type="warning" @ok="onStop(record)">
|
||||
<a-link v-if="record.taskBatchStatus === 2" v-permission="['schedule:log:stop']" status="danger">停止</a-link>
|
||||
<a-link v-if="record.taskBatchStatus === 2" v-permission="['schedule:log:stop']" status="danger" title="停止">停止</a-link>
|
||||
</a-popconfirm>
|
||||
<a-popconfirm content="是否确定重试本次执行?" type="warning" @ok="onRetry(record)">
|
||||
<a-link
|
||||
v-if="record.taskBatchStatus === 4 || record.taskBatchStatus === 5 || record.taskBatchStatus === 6"
|
||||
v-permission="['schedule:log:retry']"
|
||||
status="danger"
|
||||
title="重试"
|
||||
>
|
||||
重试
|
||||
</a-link>
|
||||
@@ -59,7 +60,7 @@
|
||||
</template>
|
||||
</GiTable>
|
||||
|
||||
<JobLogDetailModal ref="JobLogDetailModalRef" />
|
||||
<LogDetailDrawer ref="LogDetailDrawerRef" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -67,7 +68,7 @@
|
||||
import { Message } from '@arco-design/web-vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import dayjs from 'dayjs'
|
||||
import JobLogDetailModal from './LogDetailModal.vue'
|
||||
import LogDetailDrawer from './LogDetailDrawer.vue'
|
||||
import { type JobLogQuery, type JobLogResp, listGroup, listJobLog, retryJob, stopJob } from '@/apis/schedule'
|
||||
import type { TableInstanceColumns } from '@/components/GiTable/type'
|
||||
import { useTable } from '@/hooks'
|
||||
@@ -85,13 +86,13 @@ const queryForm = reactive<JobLogQuery>({
|
||||
dayjs().endOf('day').format('YYYY-MM-DD HH:mm:ss'),
|
||||
],
|
||||
})
|
||||
|
||||
const {
|
||||
tableData: dataList,
|
||||
pagination,
|
||||
loading,
|
||||
search,
|
||||
} = useTable((page) => listJobLog({ ...queryForm, ...page }), { immediate: false })
|
||||
|
||||
const columns: TableInstanceColumns[] = [
|
||||
{
|
||||
title: '序号',
|
||||
@@ -108,10 +109,10 @@ const columns: TableInstanceColumns[] = [
|
||||
{
|
||||
title: '操作',
|
||||
slotName: 'action',
|
||||
width: 60,
|
||||
width: 130,
|
||||
align: 'center',
|
||||
fixed: !isMobile() ? 'right' : undefined,
|
||||
show: has.hasPermOr(['schedule:log:stop', 'schedule:log:retry']),
|
||||
show: has.hasPermOr(['schedule:log:detail', 'schedule:log:stop', 'schedule:log:retry']),
|
||||
},
|
||||
]
|
||||
|
||||
@@ -149,10 +150,10 @@ const onRetry = (record: JobLogResp) => {
|
||||
})
|
||||
}
|
||||
|
||||
const JobLogDetailModalRef = ref<InstanceType<typeof JobLogDetailModal>>()
|
||||
// 查看日志详情
|
||||
const LogDetailDrawerRef = ref<InstanceType<typeof LogDetailDrawer>>()
|
||||
// 详情
|
||||
const onDetail = (record: JobLogResp) => {
|
||||
JobLogDetailModalRef.value?.onDetail(record)
|
||||
LogDetailDrawerRef.value?.onOpen(record)
|
||||
}
|
||||
|
||||
const route = useRoute()
|
||||
|
||||
Reference in New Issue
Block a user