This commit is contained in:
吴红兵
2025-12-02 10:37:49 +08:00
commit 1f645dad3e
1183 changed files with 147673 additions and 0 deletions

View File

@@ -0,0 +1,211 @@
<template>
<el-dialog v-model="visible" :close-on-click-modal="false" :title="form.jobId ? $t('common.editBtn') : $t('common.addBtn')" draggable>
<el-form ref="dataFormRef" :model="form" :rules="dataRules" formDialogRef label-width="120px" v-loading="loading">
<el-row :gutter="20">
<el-col :span="12" class="mb20">
<el-form-item :label="t('job.jobName')" prop="jobName">
<el-input v-model="form.jobName" :placeholder="t('job.inputjobNameTip')" />
</el-form-item>
</el-col>
<el-col :span="12" class="mb20">
<el-form-item :label="t('job.jobGroup')" prop="jobGroup">
<el-input v-model="form.jobGroup" :placeholder="t('job.inputjobGroupTip')" />
</el-form-item>
</el-col>
<el-col :span="12" class="mb20">
<el-form-item :label="t('job.jobType')" prop="jobType">
<el-select v-model="form.jobType" :placeholder="t('job.jobType')">
<el-option v-for="(item, index) in job_type" :key="index" :label="item.label" :value="item.value"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12" class="mb20" v-if="['1'].includes(form.jobType)">
<el-form-item :label="t('job.jobType1ClassName')" prop="className">
<el-input v-model="form.className" :placeholder="t('job.inputclassNameTip')" />
</el-form-item>
</el-col>
<el-col :span="12" class="mb20" v-if="['2'].includes(form.jobType)">
<el-form-item :label="t('job.jobType2ClassName')" prop="className">
<el-input v-model="form.className" :placeholder="t('job.inputBeanNameTip')" />
</el-form-item>
</el-col>
<el-col :span="12" class="mb20" v-if="['1', '2'].includes(form.jobType)">
<el-form-item :label="t('job.jobType1MethodName')" prop="methodName">
<el-input v-model="form.methodName" :placeholder="t('job.inputmethodNameTip')" />
</el-form-item>
</el-col>
<el-col :span="12" class="mb20" v-if="['3'].includes(form.jobType)">
<el-form-item :label="t('job.jobType3ExecutePath')" prop="executePath">
<el-input v-model="form.executePath" :placeholder="t('job.inputexecutePathTip')" />
</el-form-item>
</el-col>
<el-col :span="12" class="mb20" v-if="['4'].includes(form.jobType)">
<el-form-item :label="t('job.jobType4ExecutePath')" prop="executePath">
<el-input v-model="form.executePath" :placeholder="t('job.inputexecutePathTip')" />
</el-form-item>
</el-col>
<el-col :span="12" class="mb20">
<el-form-item prop="methodParamsValue">
<template #label> {{ t('job.methodParamsValue') }} <tip :content="t('job.singleParam')" /></template>
<el-input v-model="form.methodParamsValue" :placeholder="t('job.inputmethodParamsValueTip')" />
</el-form-item>
</el-col>
<el-col :span="12" class="mb20">
<el-form-item :label="t('job.cronExpression')" prop="cronExpression">
<crontab clearable @hide="popoverVis(false)" v-model="form.cronExpression"></crontab>
</el-form-item>
</el-col>
<el-col :span="12" class="mb20">
<el-form-item :label="t('job.misfirePolicy')" prop="misfirePolicy">
<el-select v-model="form.misfirePolicy" :placeholder="t('job.inputmisfirePolicyTip')">
<el-option v-for="(item, index) in misfire_policy" :key="index" :label="item.label" :value="item.value"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="24" class="mb20">
<el-form-item :label="t('job.remark')" prop="remark">
<el-input v-model="form.remark" :placeholder="t('job.inputremarkTip')" type="textarea" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button formDialogRef @click="visible = false">{{ $t('common.cancelButtonText') }}</el-button>
<el-button formDialogRef type="primary" @click="onSubmit" :disabled="loading">{{ $t('common.confirmButtonText') }}</el-button>
</span>
</template>
</el-dialog>
</template>
<script lang="ts" name="SysJobDialog" setup>
// 定义子组件向父组件传值/事件
import { useDict } from '/@/hooks/dict';
import { useMessage } from '/@/hooks/message';
import { addObj, getObj, putObj, validateJob } from '/@/api/daemon/job';
import { useI18n } from 'vue-i18n';
const emit = defineEmits(['refresh']);
const Crontab = defineAsyncComponent(() => import('/@/components/Crontab/index.vue'));
const { t } = useI18n();
// 定义变量内容
const dataFormRef = ref();
const visible = ref(false);
const loading = ref(false);
// 定义字典
const { misfire_policy, job_type } = useDict('job_status', 'job_execute_status', 'misfire_policy', 'job_type');
// 提交表单数据
const form = reactive({
jobId: '',
jobName: '',
jobGroup: '',
jobType: '',
executePath: '',
className: '',
methodName: '',
methodParamsValue: '',
cronExpression: '',
misfirePolicy: '',
jobStatus: '',
jobExecuteStatus: '',
remark: '',
});
const popoverVis = (bol: boolean) => {
popoverVisible.value = bol;
};
const popoverVisible = ref(false);
// 定义校验规则
const dataRules = reactive({
jobName: [{ required: true, message: '任务名称不能为空', trigger: 'blur' }],
jobGroup: [{ required: true, message: '任务组名不能为空', trigger: 'blur' }],
jobType: [{ required: true, message: '任务类型不能为空', trigger: 'blur' }],
cronExpression: [{ required: true, message: 'cron不能为空', trigger: 'blur' }],
misfirePolicy: [{ required: true, message: '策略不能为空', trigger: 'blur' }],
executePath: [{ required: true, message: '执行路径不能为空', trigger: 'blur' }],
className: [
{ required: true, message: '名称不能为空', trigger: 'blur' },
{
validator: (rule: any, value: any, callback: any) => {
validateJob(rule, value, callback, form);
},
trigger: 'blur',
},
],
methodName: [
{ required: true, message: '方法不能为空', trigger: 'blur' },
{
validator: (rule: any, value: any, callback: any) => {
validateJob(rule, value, callback, form);
},
trigger: 'blur',
},
],
methodParamsValue: [
{
validator: (rule: any, value: any, callback: any) => {
validateJob(rule, value, callback, form);
},
trigger: 'blur',
},
],
});
// 打开弹窗
const openDialog = (id: string) => {
visible.value = true;
form.jobId = '';
// 重置表单数据
nextTick(() => {
dataFormRef.value?.resetFields();
});
// 获取sysJob信息
if (id) {
form.jobId = id;
getsysJobData(id);
}
};
// 提交
const onSubmit = async () => {
const valid = await dataFormRef.value.validate().catch(() => {});
if (!valid) return false;
try {
loading.value = true;
form.jobId ? await putObj(form) : await addObj(form);
useMessage().success(t(form.jobId ? 'common.editSuccessText' : 'common.addSuccessText'));
visible.value = false;
emit('refresh');
} catch (err: any) {
useMessage().error('任务初始化异常');
} finally {
loading.value = false;
}
};
// 初始化表单数据
const getsysJobData = (id: string) => {
// 获取数据
getObj(id).then((res: any) => {
Object.assign(form, res.data);
});
};
// 暴露变量
defineExpose({
openDialog,
});
</script>

View File

@@ -0,0 +1,105 @@
export default {
job: {
index: '#',
jobLogBtn: 'log',
jobStartBtn: 'start',
jobRunBtn: 'execute',
jobPauseBtn: 'pause',
importsysJobTip: 'import SysJob',
jobId: 'jobId',
jobName: 'jobName',
jobGroup: 'jobGroup',
jobOrder: 'jobOrder',
jobType: 'jobType',
executePath: 'executePath',
className: 'className',
methodName: 'methodName',
methodParamsValue: 'methodParamsValue',
cronExpression: 'cronExpression',
misfirePolicy: 'misfirePolicy',
jobTenantType: 'jobTenantType',
jobStatus: 'jobStatus',
jobExecuteStatus: 'jobExecuteStatus',
createBy: 'createBy',
createTime: 'createTime',
updateBy: 'updateBy',
updateTime: 'updateTime',
startTime: 'startTime',
previousTime: 'previousTime',
nextTime: 'nextTime',
tenantId: 'tenantId',
remark: 'remark',
jobMessage: 'jobMessage',
jobLogStatus: 'jobLogStatus',
executeTime: 'executeTime',
exceptionInfo: 'exceptionInfo',
inputjobIdTip: 'input jobId',
inputjobNameTip: 'input jobName',
inputjobGroupTip: 'input jobGroup',
inputjobOrderTip: 'input jobOrder',
inputjobTypeTip: 'input jobType',
inputexecutePathTip: 'Please enter the Request Address',
inputclassNameTip: 'Please enter the Full Class Path',
inputmethodNameTip: 'input methodName',
inputmethodParamsValueTip: 'input methodParamsValue',
inputcronExpressionTip: 'input cronExpression',
inputmisfirePolicyTip: 'input misfirePolicy',
inputjobTenantTypeTip: 'input jobTenantType',
inputjobStatusTip: 'input jobStatus',
inputjobExecuteStatusTip: 'input jobExecuteStatus',
inputcreateByTip: 'input createBy',
inputcreateTimeTip: 'input createTime',
inputupdateByTip: 'input updateBy',
inputupdateTimeTip: 'input updateTime',
inputstartTimeTip: 'input startTime',
inputpreviousTimeTip: 'input previousTime',
inputnextTimeTip: 'input nextTime',
inputtenantIdTip: 'input tenantId',
inputremarkTip: 'input remark',
jobType1ClassName: 'Full Class Path',
jobType1MethodName: 'Method Name',
jobType2ClassName: 'Bean Name',
jobType2MethodName: 'Method Name',
jobType3ExecutePath: 'Request Address',
jobType4ExecutePath: 'Jar Path',
singleParam: 'Only single parameter is supported',
},
log: {
index: '#',
importsysJobLogTip: 'import SysJobLog',
jobLogId: 'jobLogId',
jobId: 'jobId',
jobName: 'jobName',
jobGroup: 'jobGroup',
jobOrder: 'jobOrder',
jobType: 'jobType',
executePath: 'executePath',
className: 'className',
methodName: 'methodName',
methodParamsValue: 'methodParamsValue',
cronExpression: 'cronExpression',
jobMessage: 'jobMessage',
jobLogStatus: 'jobLogStatus',
executeTime: 'executeTime',
exceptionInfo: 'exceptionInfo',
createTime: 'createTime',
tenantId: 'tenantId',
inputJobLogIdTip: 'input jobLogId',
inputJobIdTip: 'input jobId',
inputJobNameTip: 'input jobName',
inputJobGroupTip: 'input jobGroup',
inputJobOrderTip: 'input jobOrder',
inputJobTypeTip: 'input jobType',
inputExecutePathTip: 'input executePath',
inputClassNameTip: 'input className',
inputMethodNameTip: 'input methodName',
inputMethodParamsValueTip: 'input methodParamsValue',
inputCronExpressionTip: 'input cronExpression',
inputJobMessageTip: 'input jobMessage',
inputJobLogStatusTip: 'input jobLogStatus',
inputExecuteTimeTip: 'input executeTime',
inputExceptionInfoTip: 'input exceptionInfo',
inputCreateTimeTip: 'input createTime',
inputTenantIdTip: 'input tenantId',
},
};

View File

@@ -0,0 +1,95 @@
export default {
job: {
index: '#',
jobLogBtn: '日志',
jobStartBtn: '启动',
jobRunBtn: '执行',
jobPauseBtn: '暂停',
importsysJobTip: '导入定时任务调度表',
jobId: '任务id',
jobName: '任务名称',
jobGroup: '任务组名',
jobOrder: '组内执行顺利',
jobType: '类型',
executePath: '执行路径',
className: '执行文件',
methodName: '执行方法',
methodParamsValue: '参数值',
cronExpression: 'cron表达式',
misfirePolicy: '错失执行策略',
jobTenantType: '1、多租户任务;2、非多租户任务',
jobStatus: '任务状态',
jobExecuteStatus: '执行状态',
createBy: '创建者',
createTime: '创建时间',
updateBy: '更新者',
updateTime: '更新时间',
startTime: '初次执行时间',
previousTime: '上次执行时间',
nextTime: '下次执行时间',
tenantId: '租户',
remark: '备注信息',
jobMessage: '日志信息',
jobLogStatus: '执行状态',
executeTime: '执行时间',
exceptionInfo: '异常信息',
inputjobIdTip: '请输入任务id',
inputjobNameTip: '请输入任务名称',
inputjobGroupTip: '请输入任务组名',
inputjobOrderTip: '请输入组内执行顺利',
inputjobTypeTip: '请输入类型',
inputexecutePathTip: '请输入请求地址',
inputclassNameTip: '请输入类全路径',
inputBeanNameTip: '请输入Bean名称',
inputmethodNameTip: '请输入方法名称',
inputmethodParamsValueTip: '请输入参数值',
inputcronExpressionTip: '请输入cron表达式',
inputmisfirePolicyTip: '请输入错失执行策略',
inputjobTenantTypeTip: '请输入1、多租户任务;2、非多租户任务',
inputjobStatusTip: '请输入任务状态',
inputjobExecuteStatusTip: '请输入执行状态',
inputcreateByTip: '请输入创建者',
inputcreateTimeTip: '请输入创建时间',
inputupdateByTip: '请输入更新者',
inputupdateTimeTip: '请输入更新时间',
inputstartTimeTip: '请输入初次执行时间',
inputpreviousTimeTip: '请输入上次执行时间',
inputnextTimeTip: '请输入下次执行时间',
inputtenantIdTip: '请输入租户',
inputremarkTip: '请输入备注信息',
jobType1ClassName: '类全路径',
jobType1MethodName: '方法名称',
jobType2ClassName: 'Bean名称',
jobType2MethodName: '方法名称',
jobType3ExecutePath: '请求地址',
jobType4ExecutePath: 'Jar包路径',
singleParam: '只支持单个参数',
},
log: {
index: '#',
importsysJobLogTip: '导入定时任务执行日志表',
jobLogId: '任务日志ID',
jobId: '任务id',
jobName: '任务名称',
jobGroup: '任务组名',
jobMessage: '日志信息',
jobLogStatus: '执行状态',
executeTime: '执行时间',
exceptionInfo: '异常信息',
createTime: '创建时间',
tenantId: '租户id',
inputJobLogIdTip: '请输入任务日志ID',
inputJobIdTip: '请输入任务id',
inputJobNameTip: '请输入任务名称',
inputJobGroupTip: '请输入任务组名',
inputMethodNameTip: '请输入任务方法',
inputMethodParamsValueTip: '请输入参数值',
inputCronExpressionTip: '请输入cron执行表达式',
inputJobMessageTip: '请输入日志信息',
inputJobLogStatusTip: '请输入执行状态',
inputExecuteTimeTip: '请输入执行时间',
inputExceptionInfoTip: '请输入异常信息',
inputCreateTimeTip: '请输入创建时间',
inputTenantIdTip: '请输入租户id',
},
};

View File

@@ -0,0 +1,427 @@
<template>
<div class="layout-padding">
<div class="layout-padding-auto layout-padding-view">
<el-row class="ml10" v-show="showSearch">
<el-form :inline="true" :model="state.queryForm" ref="queryRef">
<el-form-item :label="$t('job.jobName')" prop="jobName">
<el-input :placeholder="$t('job.inputjobNameTip')" @keyup.enter="getDataList" clearable
v-model="state.queryForm.jobName"/>
</el-form-item>
<el-form-item :label="t('job.jobStatus')" prop="jobStatus">
<el-select :placeholder="t('job.inputjobStatusTip')" v-model="state.queryForm.jobStatus">
<el-option :key="index" :label="item.label" :value="item.value"
v-for="(item, index) in job_status"></el-option>
</el-select>
</el-form-item>
<el-form-item :label="t('job.jobExecuteStatus')" prop="jobExecuteStatus">
<el-select :placeholder="t('job.inputjobExecuteStatusTip')" v-model="state.queryForm.jobExecuteStatus">
<el-option :key="index" :label="item.label" :value="item.value"
v-for="(item, index) in job_execute_status"></el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-button @click="getDataList" icon="Search" type="primary">{{ $t('common.queryBtn') }}</el-button>
<el-button @click="resetQuery" icon="Refresh">{{ $t('common.resetBtn') }}</el-button>
</el-form-item>
</el-form>
</el-row>
<el-row>
<div class="mb8" style="width: 100%">
<el-button v-auth="'job_sys_job_add'" @click="formDialogRef.openDialog()" class="ml10" icon="folder-add"
type="primary">
{{ $t('common.addBtn') }}
</el-button>
<el-button plain v-auth="'job_sys_job_del'" :disabled="multiple" @click="handleDelete(undefined)" class="ml10"
icon="Delete" type="primary">
{{ $t('common.delBtn') }}
</el-button>
<right-toolbar
:export="'job_sys_job_add'"
@exportExcel="exportExcel"
@queryTable="getDataList"
class="ml10"
style="float: right; margin-right: 20px"
v-model:showSearch="showSearch"
>
<el-tooltip class="item" effect="dark" :content="$t('queryTree.view')" placement="top">
<el-button circle icon="Grid" @click="tableViewRef = !tableViewRef"></el-button>
</el-tooltip>
</right-toolbar>
</div>
</el-row>
<!-- 表格视图 -->
<el-table
v-if="tableViewRef"
:data="state.dataList"
@selection-change="handleSelectionChange"
style="width: 100%"
row-key="jobId"
v-loading="state.loading"
border
:cell-style="tableStyle.cellStyle"
:header-cell-style="tableStyle.headerCellStyle"
>
<el-table-column align="center" type="selection" width="40"/>
<el-table-column :label="t('job.index')" fixed="left" type="index" width="60"/>
<el-table-column :label="t('job.jobName')" fixed="left" prop="jobName" show-overflow-tooltip width="120"/>
<el-table-column :label="t('job.jobGroup')" prop="jobGroup" show-overflow-tooltip width="120"/>
<el-table-column :label="t('job.jobStatus')" prop="jobStatus" show-overflow-tooltip width="120">
<template #default="scope">
<dict-tag :options="job_status" :value="scope.row.jobStatus"></dict-tag>
</template>
</el-table-column>
<el-table-column :label="t('job.jobExecuteStatus')" prop="jobExecuteStatus" show-overflow-tooltip width="120">
<template #default="scope">
<dict-tag :options="job_execute_status" :value="scope.row.jobExecuteStatus"></dict-tag>
</template>
</el-table-column>
<el-table-column :label="t('job.startTime')" prop="startTime" show-overflow-tooltip width="120"/>
<el-table-column :label="t('job.previousTime')" prop="previousTime" show-overflow-tooltip width="120"/>
<el-table-column :label="t('job.nextTime')" prop="nextTime" show-overflow-tooltip width="120"/>
<el-table-column :label="t('job.jobType')" prop="jobType" show-overflow-tooltip width="120">
<template #default="scope">
<dict-tag :options="job_type" :value="scope.row.jobType"></dict-tag>
</template>
</el-table-column>
<el-table-column :label="t('job.executePath')" prop="executePath" show-overflow-tooltip width="120"/>
<el-table-column :label="t('job.className')" prop="className" show-overflow-tooltip width="120"/>
<el-table-column :label="t('job.methodName')" prop="methodName" show-overflow-tooltip width="120"/>
<el-table-column :label="t('job.methodParamsValue')" prop="methodParamsValue" show-overflow-tooltip
width="120"/>
<el-table-column :label="t('job.cronExpression')" prop="cronExpression" show-overflow-tooltip width="120"/>
<el-table-column :label="t('job.misfirePolicy')" prop="misfirePolicy" show-overflow-tooltip width="200">
<template #default="scope">
<dict-tag :options="misfire_policy" :value="scope.row.misfirePolicy"></dict-tag>
</template>
</el-table-column>
<el-table-column :label="$t('common.action')" fixed="right" width="300">
<template #default="scope">
<el-button @click="handleJobLog(scope.row)" text type="primary">{{ $t('job.jobLogBtn') }}</el-button>
<el-button v-auth="'job_sys_job_start_job'" @click="handleStartJob(scope.row)" text type="primary"
v-if="scope.row.jobStatus !== '2'"
>
{{ $t('job.jobStartBtn') }}
</el-button>
<el-button
v-auth="'job_sys_job_shutdown_job'"
@click="handleShutDownJob(scope.row)"
text
type="primary"
v-if="scope.row.jobStatus === '2'"
>
{{ $t('job.jobPauseBtn') }}
</el-button>
<el-button v-auth="'job_sys_job_edit'" @click="handleEditJob(scope.row)" text type="primary">
{{ $t('common.editBtn') }}
</el-button>
<el-button v-auth="'job_sys_job_start_job'" @click="handleRunJob(scope.row)" text type="primary">
{{ $t('job.jobRunBtn') }}
</el-button>
<el-button v-auth="'job_sys_job_del'" @click="handleDelete(scope.row)" text type="primary"
:disabled="scope.row.jobStatus === '2'">{{ $t('common.delBtn') }}
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 卡片视图 -->
<el-scrollbar v-if="!tableViewRef">
<div class="p-4">
<div class="mx-auto">
<!-- Main -->
<div class="grid grid-cols-4 gap-x-4 gap-y-6">
<div
v-for="job in state.dataList"
:key="job.jobId"
class="w-full max-w-sm mt-2 bg-gray-100 dark:bg-[#1d1e1f] rounded-lg border dark:border-[#303030]"
>
<div aria-label="header" class="flex items-center p-4 space-x-4">
<div aria-label="avatar" class="flex items-center mr-auto space-x-4">
<div class="flex flex-col flex-1 space-y-2">
<div class="text-lg font-bold text-gray-900 dark:text-gray-100">
{{ job.jobName }}
</div>
<p class="text-base font-normal text-gray-500 truncate dark:text-gray-400">
{{ job.jobGroup }} / {{ job_type.find((item: any) => item.value === job.jobType)?.label }}
</p>
</div>
</div>
</div>
<div aria-label="navigation" class="py-2">
<nav class="grid gap-1">
<div class="flex items-center w-full px-4 py-1 space-x-3 leading-6 text-gray-600 dark:text-gray-300 hover:bg-white dark:hover:bg-[#303030] rounded-md">
<span class="font-medium">{{ $t('job.jobStatus') }}:</span>
<span>{{ job_status.find((item: any) => item.value === job.jobStatus)?.label }}</span>
</div>
<div class="flex items-center w-full px-4 py-1 space-x-3 leading-6 text-gray-600 dark:text-gray-300 hover:bg-white dark:hover:bg-[#303030] rounded-md">
<span class="font-medium">{{ $t('job.cronExpression') }}:</span>
<span>{{ job.cronExpression }}</span>
</div>
<div class="flex items-center w-full px-4 py-1 space-x-3 leading-6 text-gray-600 dark:text-gray-300 hover:bg-white dark:hover:bg-[#303030] rounded-md">
<span class="font-medium">{{ $t('job.previousTime') }}:</span>
<span>{{ job.previousTime }}</span>
</div>
<div class="flex items-center w-full px-4 py-1 space-x-3 leading-6 text-gray-600 dark:text-gray-300 hover:bg-white dark:hover:bg-[#303030] rounded-md">
<span class="font-medium">{{ $t('job.nextTime') }}:</span>
<span>{{ job.nextTime }}</span>
</div>
<div class="flex items-center w-full px-4 py-1 space-x-3 leading-6 text-gray-600 dark:text-gray-300 hover:bg-white dark:hover:bg-[#303030] rounded-md">
<span class="font-medium">{{ $t('job.jobExecuteStatus') }}:</span>
<span>
<el-icon v-if="job.jobExecuteStatus === '1'"><CircleCloseFilled/></el-icon>
<el-icon v-if="job.jobExecuteStatus === '0'"><CircleCheckFilled/></el-icon>
</span>
</div>
</nav>
</div>
<div aria-label="footer" class="pt-2 border-t dark:border-[#303030]">
<div class="flex items-center justify-center min-w-full">
<nav class="flex">
<a
@click="handleJobLog(job)"
href="javascript:;"
class="px-2 py-2 text-primary hover:text-primary-400 dark:text-primary-300"
>
<span>{{ $t('job.jobLogBtn') }}</span>
</a>
<a
v-auth="'job_sys_job_start_job'"
@click="handleStartJob(job)"
v-if="job.jobStatus !== '2'"
href="javascript:;"
class="px-2 py-2 text-primary hover:text-primary-400 dark:text-primary-300"
>
<span>{{ $t('job.jobStartBtn') }}</span>
</a>
<a
v-auth="'job_sys_job_shutdown_job'"
@click="handleShutDownJob(job)"
v-if="job.jobStatus === '2'"
href="javascript:;"
class="px-2 py-2 text-primary hover:text-primary-400 dark:text-primary-300"
>
<span>{{ $t('job.jobPauseBtn') }}</span>
</a>
<a
v-auth="'job_sys_job_edit'" @click="handleEditJob(job)"
href="javascript:;"
class="px-2 py-2 text-primary hover:text-primary-400 dark:text-primary-300"
>
<span> {{ $t('common.editBtn') }}</span>
</a>
<a
v-auth="'job_sys_job_start_job'" @click="handleRunJob(job)"
href="javascript:;"
class="px-2 py-2 text-primary hover:text-primary-400 dark:text-primary-300"
>
<span>{{ $t('job.jobRunBtn') }}</span>
</a>
<a
v-auth="'job_sys_job_del'" @click="handleDelete(job)" type="primary"
v-if="job.jobStatus !== '2'"
href="javascript:;"
class="px-2 py-2 text-primary hover:text-primary-400 dark:text-primary-300"
>
<span>{{ $t('common.delBtn') }}</span>
</a>
</nav>
</div>
</div>
</div>
</div>
</div>
</div>
</el-scrollbar>
<pagination @current-change="currentChangeHandle" @size-change="sizeChangeHandle" v-bind="state.pagination"/>
</div>
<!-- 编辑新增 -->
<form-dialog @refresh="getDataList()" ref="formDialogRef"/>
<job-log ref="jobLogRef"></job-log>
</div>
</template>
<script lang="ts" name="systemSysJob" setup>
import {BasicTableProps, useTable} from '/@/hooks/table';
import {delObj, fetchList, runJobRa, shutDownJobRa, startJobRa} from '/@/api/daemon/job';
import {useMessage, useMessageBox} from '/@/hooks/message';
import {useDict} from '/@/hooks/dict';
import {useI18n} from 'vue-i18n';
// 引入组件
const FormDialog = defineAsyncComponent(() => import('./form.vue'));
const JobLog = defineAsyncComponent(() => import('./job-log.vue'));
// 获取国际化方法
const {t} = useI18n();
/** 表单弹窗引用 */
const formDialogRef = ref();
/** 作业日志引用 */
const jobLogRef = ref();
/** 表格视图 */
const tableViewRef = ref(false)
/** 搜索表单信息 */
const queryForm = reactive({
jobName: '',
jobGroup: '',
jobStatus: '',
jobExecuteStatus: '',
});
/** 是否展示搜索表单 */
const showSearch = ref(true);
// 多选变量
/** 选中的行 */
const selectedRows = ref([]);
/** 是否可以多选 */
const multiple = ref(true);
/** 查询字典 */
const {
job_status,
job_execute_status,
misfire_policy,
job_type
} = useDict('job_status', 'job_execute_status', 'misfire_policy', 'job_type');
/** 表格状态变量 */
const state = reactive<BasicTableProps>({
queryForm,
pageList: fetchList,
pagination: {
size: 8,
pageSizes: [4, 8, 12]
}
});
/** 获取表格数据方法 */
const {getDataList, currentChangeHandle, sizeChangeHandle, downBlobFile, tableStyle} = useTable(state);
/** 重置查询表单 */
const resetQuery = () => {
Object.keys(queryForm).forEach((key) => (queryForm[key] = ''));
getDataList();
};
/** 行选中事件 */
const handleSelectionChange = (rows: any) => {
selectedRows.value = rows;
multiple.value = !rows.length;
};
/** 导出Excel */
const exportExcel = () => {
downBlobFile('/job/sys-job/export', Object.assign(state.queryForm, {ids: selectedRows}), 'job.xlsx');
};
/** 查看作业日志 */
const handleJobLog = (row) => {
jobLogRef.value.openDialog(row.jobId);
};
/** 编辑作业 */
const handleEditJob = (row) => {
const jobStatus = row.jobStatus;
if (jobStatus === '1' || jobStatus === '3') {
formDialogRef.value.openDialog(row.jobId);
} else {
useMessage().error('运行中定时任务不可修改,请先暂停后操作');
}
};
/** 启动作业 */
const handleStartJob = async (row) => {
const jobStatus = row.jobStatus;
if (jobStatus === '1' || jobStatus === '3') {
try {
await useMessageBox().confirm(`即将发布或启动(任务名称: ${row.jobName}), 是否继续?`);
} catch {
return;
}
try {
await startJobRa(row.jobId);
getDataList();
useMessage().success(t('common.optSuccessText'));
} catch (err: any) {
useMessage().error(err.msg);
}
} else {
useMessage().error('定时任务已运行');
}
};
/** 暂停作业 */
const handleShutDownJob = async (row) => {
const jobStatus = row.jobStatus;
if (jobStatus === '2') {
try {
await useMessageBox().confirm(`即将暂停(任务名称: ${row.jobName}), 是否继续?`);
} catch {
return;
}
try {
await shutDownJobRa(row.jobId);
getDataList();
useMessage().success(t('common.optSuccessText'));
} catch (err: any) {
useMessage().error(err.msg);
}
} else {
useMessage().error('已暂停,不要重复操作');
}
};
/** 运行作业 */
const handleRunJob = async (row) => {
try {
await useMessageBox().confirm(`立刻执行一次任务(任务名称: ${row.jobName}), 是否继续?`);
} catch {
return;
}
try {
await runJobRa(row.jobId);
getDataList();
useMessage().success(t('common.optSuccessText'));
} catch (err: any) {
useMessage().error('运行失败');
}
};
/** 删除操作 */
const handleDelete = async (row) => {
if (!row) {
selectedRows.value.forEach(handleDelete);
return;
}
const {jobId, jobName} = row;
try {
await useMessageBox().confirm(`${t('common.delConfirmText')}(任务名称:${jobName})`);
} catch {
return;
}
try {
await delObj(jobId);
getDataList();
useMessage().success(t('common.delSuccessText'));
} catch (error: any) {
useMessage().error('删除失败');
}
};
</script>

View File

@@ -0,0 +1,125 @@
<template>
<el-dialog v-model="visible" :close-on-click-modal="false" fullscreen title="运行日志" draggable>
<div class="layout-padding-auto layout-padding-view">
<el-row>
<div class="mb8" style="width: 100%">
<el-button
formDialogRef
:disabled="multiple"
icon="Delete"
type="primary"
class="ml10"
v-auth="'sys_log_del'"
@click="handleDelete(selectObjs)"
>
{{ $t('common.delBtn') }}
</el-button>
<right-toolbar
v-model:showSearch="showSearch"
class="ml10"
style="float: right; margin-right: 20px"
@queryTable="getDataList"
></right-toolbar>
</div>
</el-row>
<el-table
:data="state.dataList"
v-loading="state.loading"
style="width: 100%"
@selection-change="handleSelectionChange"
border
:cell-style="tableStyle.cellStyle"
:header-cell-style="tableStyle.headerCellStyle"
>
<el-table-column type="selection" width="40" align="center" />
<el-table-column type="index" :label="t('log.index')" width="80" />
<el-table-column prop="jobName" :label="t('log.jobName')" show-overflow-tooltip />
<el-table-column prop="jobMessage" :label="t('log.jobMessage')" show-overflow-tooltip />
<el-table-column prop="jobLogStatus" :label="t('log.jobLogStatus')" show-overflow-tooltip>
<template #default="scope">
<dict-tag :options="job_execute_status" :value="scope.row.jobLogStatus"></dict-tag>
</template>
</el-table-column>
<el-table-column prop="executeTime" :label="t('log.executeTime')" show-overflow-tooltip />
<el-table-column prop="exceptionInfo" :label="t('log.exceptionInfo')" show-overflow-tooltip />
<el-table-column prop="createTime" :label="t('log.createTime')" show-overflow-tooltip />
<el-table-column :label="$t('common.action')" width="150">
<template #default="scope">
<el-button text type="primary" v-auth="'cloud_log_edit'" @click="formDialogRef.openDialog(scope.row.jobLogId)"
>{{ $t('common.editBtn') }}
</el-button>
<el-button text type="primary" v-auth="'sys_log_del'" @click="handleDelete([scope.row.jobLogId])">{{ $t('common.delBtn') }} </el-button>
</template>
</el-table-column>
</el-table>
<pagination @size-change="sizeChangeHandle" @current-change="currentChangeHandle" v-bind="state.pagination" />
</div>
</el-dialog>
</template>
<script setup lang="ts" name="job-log">
import { BasicTableProps, useTable } from '/@/hooks/table';
import { fetchList, delObjs } from '/@/api/daemon/job-log';
import { useI18n } from 'vue-i18n';
import { useDict } from '/@/hooks/dict';
import { useMessage, useMessageBox } from '/@/hooks/message';
const { t } = useI18n();
const visible = ref(false);
const { job_execute_status } = useDict('job_type', 'job_execute_status');
// 定义变量内容
const formDialogRef = ref();
// 搜索变量
const showSearch = ref(true);
// 多选变量
const selectObjs = ref([]) as any;
const multiple = ref(true);
const state: BasicTableProps = reactive<BasicTableProps>({
queryForm: {
jobId: '',
},
pageList: fetchList,
createdIsNeed: false,
});
const { getDataList, currentChangeHandle, sizeChangeHandle, tableStyle } = useTable(state);
const openDialog = (id: string) => {
visible.value = true;
state.queryForm.jobId = id;
getDataList();
};
// 多选事件
const handleSelectionChange = (objs: { jobLogId: string }[]) => {
selectObjs.value = objs.map(({ jobLogId }) => jobLogId);
multiple.value = !objs.length;
};
// 删除操作
const handleDelete = async (ids: string[]) => {
try {
await useMessageBox().confirm(t('common.delConfirmText'));
} catch {
return;
}
try {
await delObjs(ids);
getDataList();
useMessage().success(t('common.delSuccessText'));
} catch (err: any) {
useMessage().error(err.msg);
}
};
// 暴露变量
defineExpose({
openDialog,
});
</script>
<style scoped></style>