428 lines
17 KiB
Vue
428 lines
17 KiB
Vue
<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>
|