index.vue 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378
  1. <template>
  2. <div class="app-container">
  3. <doc-alert title="定时任务" url="https://doc.iocoder.cn/job/" />
  4. <doc-alert title="异步任务" url="https://doc.iocoder.cn/async-task/" />
  5. <doc-alert title="消息队列" url="https://doc.iocoder.cn/message-queue/" />
  6. <!-- 搜索栏 -->
  7. <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="100px">
  8. <el-form-item label="任务名称" prop="name">
  9. <el-input v-model="queryParams.name" placeholder="请输入任务名称" clearable @keyup.enter.native="handleQuery"/>
  10. </el-form-item>
  11. <el-form-item label="任务状态" prop="status">
  12. <el-select v-model="queryParams.status" placeholder="请选择任务状态" clearable>
  13. <el-option v-for="dict in this.getDictDatas(DICT_TYPE.INFRA_JOB_STATUS)"
  14. :key="dict.value" :label="dict.label" :value="dict.value"/>
  15. </el-select>
  16. </el-form-item>
  17. <el-form-item label="处理器的名字" prop="handlerName">
  18. <el-input v-model="queryParams.handlerName" placeholder="请输入处理器的名字" clearable @keyup.enter.native="handleQuery"/>
  19. </el-form-item>
  20. <el-form-item>
  21. <el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
  22. <el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
  23. </el-form-item>
  24. </el-form>
  25. <el-row :gutter="10" class="mb8">
  26. <el-col :span="1.5">
  27. <el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
  28. v-hasPermi="['infra:job:create']">新增</el-button>
  29. </el-col>
  30. <el-col :span="1.5">
  31. <el-button type="warning" icon="el-icon-download" size="mini" @click="handleExport" :loading="exportLoading"
  32. v-hasPermi="['infra:job:export']">导出</el-button>
  33. </el-col>
  34. <el-col :span="1.5">
  35. <el-button type="info" icon="el-icon-s-operation" size="mini" @click="handleJobLog"
  36. v-hasPermi="['infra:job:query']">执行日志</el-button>
  37. </el-col>
  38. <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
  39. </el-row>
  40. <el-table v-loading="loading" :data="jobList">
  41. <el-table-column label="任务编号" align="center" prop="id" />
  42. <el-table-column label="任务名称" align="center" prop="name" />
  43. <el-table-column label="任务状态" align="center" prop="status">
  44. <template v-slot="scope">
  45. <dict-tag :type="DICT_TYPE.INFRA_JOB_STATUS" :value="scope.row.status" />
  46. </template>
  47. </el-table-column>>
  48. <el-table-column label="处理器的名字" align="center" prop="handlerName" />
  49. <el-table-column label="处理器的参数" align="center" prop="handlerParam" />
  50. <el-table-column label="CRON 表达式" align="center" prop="cronExpression" />
  51. <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
  52. <template v-slot="scope">
  53. <el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
  54. v-hasPermi="['infra:job:update']">修改</el-button>
  55. <el-button size="mini" type="text" icon="el-icon-check" @click="handleChangeStatus(scope.row, true)"
  56. v-if="scope.row.status === InfJobStatusEnum.STOP" v-hasPermi="['infra:job:update']">开启</el-button>
  57. <el-button size="mini" type="text" icon="el-icon-close" @click="handleChangeStatus(scope.row, false)"
  58. v-if="scope.row.status === InfJobStatusEnum.NORMAL" v-hasPermi="['infra:job:update']">暂停</el-button>
  59. <el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
  60. v-hasPermi="['infra:job:delete']">删除</el-button>
  61. <el-dropdown size="mini" @command="(command) => handleCommand(command, scope.row)"
  62. v-hasPermi="['infra:job:trigger', 'infra:job:query']">
  63. <el-button size="mini" type="text" icon="el-icon-d-arrow-right">更多</el-button>
  64. <el-dropdown-menu slot="dropdown">
  65. <el-dropdown-item command="handleRun" icon="el-icon-caret-right"
  66. v-hasPermi="['infra:job:trigger']">执行一次</el-dropdown-item>
  67. <el-dropdown-item command="handleView" icon="el-icon-view"
  68. v-hasPermi="['infra:job:query']">任务详细</el-dropdown-item>
  69. <el-dropdown-item command="handleJobLog" icon="el-icon-s-operation"
  70. v-hasPermi="['infra:job:query']">调度日志</el-dropdown-item>
  71. </el-dropdown-menu>
  72. </el-dropdown>
  73. </template>
  74. </el-table-column>
  75. </el-table>
  76. <!-- 分页组件 -->
  77. <pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
  78. @pagination="getList"/>
  79. <!-- 添加或修改定时任务对话框 -->
  80. <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
  81. <el-form ref="form" :model="form" :rules="rules" label-width="120px">
  82. <el-form-item label="任务名称" prop="name">
  83. <el-input v-model="form.name" placeholder="请输入任务名称" />
  84. </el-form-item>
  85. <el-form-item label="处理器的名字" prop="handlerName">
  86. <el-input v-model="form.handlerName" placeholder="请输入处理器的名字" v-bind:readonly="form.id !== undefined" />
  87. </el-form-item>
  88. <el-form-item label="处理器的参数" prop="handlerParam">
  89. <el-input v-model="form.handlerParam" placeholder="请输入处理器的参数" />
  90. </el-form-item>
  91. <el-form-item label="CRON 表达式" prop="cronExpression">
  92. <el-input v-model="form.cronExpression" placeholder="请输入CRON 表达式">
  93. <template slot="append">
  94. <el-button type="primary" @click="handleShowCron">
  95. 生成表达式
  96. <i class="el-icon-time el-icon--right"></i>
  97. </el-button>
  98. </template>
  99. </el-input>
  100. </el-form-item>
  101. <el-form-item label="重试次数" prop="retryCount">
  102. <el-input v-model="form.retryCount" placeholder="请输入重试次数。设置为 0 时,不进行重试" />
  103. </el-form-item>
  104. <el-form-item label="重试间隔" prop="retryInterval">
  105. <el-input v-model="form.retryInterval" placeholder="请输入重试间隔,单位:毫秒。设置为 0 时,无需间隔" />
  106. </el-form-item>
  107. <el-form-item label="监控超时时间" prop="monitorTimeout">
  108. <el-input v-model="form.monitorTimeout" placeholder="请输入监控超时时间,单位:毫秒" />
  109. </el-form-item>
  110. </el-form>
  111. <div slot="footer" class="dialog-footer">
  112. <el-button type="primary" @click="submitForm">确 定</el-button>
  113. <el-button @click="cancel">取 消</el-button>
  114. </div>
  115. </el-dialog>
  116. <el-dialog title="Cron表达式生成器" :visible.sync="openCron" append-to-body class="scrollbar" destroy-on-close>
  117. <crontab @hide="openCron=false" @fill="crontabFill" :expression="expression"></crontab>
  118. </el-dialog>
  119. <!-- 任务详细 -->
  120. <el-dialog title="任务详细" :visible.sync="openView" width="700px" append-to-body>
  121. <el-form ref="form" :model="form" label-width="200px" size="mini">
  122. <el-row>
  123. <el-col :span="24">
  124. <el-form-item label="任务编号:">{{ form.id }}</el-form-item>
  125. <el-form-item label="任务名称:">{{ form.name }}</el-form-item>
  126. <el-form-item label="任务名称:">
  127. <dict-tag :type="DICT_TYPE.INFRA_JOB_STATUS" :value="form.status" />
  128. </el-form-item>
  129. <el-form-item label="处理器的名字:">{{ form.handlerName }}</el-form-item>
  130. <el-form-item label="处理器的参数:">{{ form.handlerParam }}</el-form-item>
  131. <el-form-item label="cron表达式:">{{ form.cronExpression }}</el-form-item>
  132. <el-form-item label="重试次数:">{{ form.retryCount }}</el-form-item>
  133. <el-form-item label="重试间隔:">{{ form.retryInterval + " 毫秒" }}</el-form-item>
  134. <el-form-item label="监控超时时间:">{{ form.monitorTimeout > 0 ? form.monitorTimeout + " 毫秒" : "未开启" }}</el-form-item>
  135. <el-form-item label="后续执行时间:">{{ Array.from(nextTimes, x => parseTime(x)).join('; ')}}</el-form-item>
  136. </el-col>
  137. </el-row>
  138. </el-form>
  139. <div slot="footer" class="dialog-footer">
  140. <el-button @click="openView = false">关 闭</el-button>
  141. </div>
  142. </el-dialog>
  143. </div>
  144. </template>
  145. <script>
  146. import { listJob, getJob, delJob, addJob, updateJob, exportJob, runJob, updateJobStatus, getJobNextTimes } from "@/api/infra/job";
  147. import { InfraJobStatusEnum } from "@/utils/constants";
  148. import Crontab from '@/components/Crontab'
  149. export default {
  150. components: { Crontab },
  151. name: "InfraJob",
  152. data() {
  153. return {
  154. // 遮罩层
  155. loading: true,
  156. // 导出遮罩层
  157. exportLoading: false,
  158. // 显示搜索条件
  159. showSearch: true,
  160. // 总条数
  161. total: 0,
  162. // 定时任务表格数据
  163. jobList: [],
  164. // 弹出层标题
  165. title: "",
  166. // 是否显示弹出层
  167. open: false,
  168. // 是否显示详细弹出层
  169. openView: false,
  170. // 是否显示Cron表达式弹出层
  171. openCron: false,
  172. // 传入的表达式
  173. expression: "",
  174. // 状态字典
  175. statusOptions: [],
  176. // 查询参数
  177. queryParams: {
  178. pageNo: 1,
  179. pageSize: 10,
  180. name: undefined,
  181. status: undefined,
  182. handlerName: undefined
  183. },
  184. // 表单参数
  185. form: {},
  186. // 表单校验
  187. rules: {
  188. name: [{ required: true, message: "任务名称不能为空", trigger: "blur" }],
  189. handlerName: [{ required: true, message: "处理器的名字不能为空", trigger: "blur" }],
  190. cronExpression: [{ required: true, message: "CRON 表达式不能为空", trigger: "blur" }],
  191. retryCount: [{ required: true, message: "重试次数不能为空", trigger: "blur" }],
  192. retryInterval: [{ required: true, message: "重试间隔不能为空", trigger: "blur" }],
  193. },
  194. nextTimes: [], // 后续执行时间
  195. // 枚举
  196. InfJobStatusEnum: InfraJobStatusEnum
  197. };
  198. },
  199. created() {
  200. this.getList();
  201. },
  202. methods: {
  203. /** 查询定时任务列表 */
  204. getList() {
  205. this.loading = true;
  206. listJob(this.queryParams).then(response => {
  207. this.jobList = response.data.list;
  208. this.total = response.data.total;
  209. this.loading = false;
  210. });
  211. },
  212. /** 取消按钮 */
  213. cancel() {
  214. this.open = false;
  215. this.reset();
  216. },
  217. /** 表单重置 */
  218. reset() {
  219. this.form = {
  220. id: undefined,
  221. name: undefined,
  222. handlerName: undefined,
  223. handlerParam: undefined,
  224. cronExpression: undefined,
  225. retryCount: undefined,
  226. retryInterval: undefined,
  227. monitorTimeout: undefined,
  228. };
  229. this.nextTimes = [];
  230. this.resetForm("form");
  231. },
  232. /** 搜索按钮操作 */
  233. handleQuery() {
  234. this.queryParams.pageNo = 1;
  235. this.getList();
  236. },
  237. /** 重置按钮操作 */
  238. resetQuery() {
  239. this.resetForm("queryForm");
  240. this.handleQuery();
  241. },
  242. /** 立即执行一次 **/
  243. handleRun(row) {
  244. this.$modal.confirm('确认要立即执行一次"' + row.name + '"任务吗?').then(function() {
  245. return runJob(row.id);
  246. }).then(() => {
  247. this.$modal.msgSuccess("执行成功");
  248. }).catch(() => {});
  249. },
  250. /** 任务详细信息 */
  251. handleView(row) {
  252. getJob(row.id).then(response => {
  253. this.form = response.data;
  254. this.openView = true;
  255. });
  256. // 获取下一次执行时间
  257. getJobNextTimes(row.id).then(response => {
  258. this.nextTimes = response.data;
  259. });
  260. },
  261. /** cron表达式按钮操作 */
  262. handleShowCron() {
  263. this.expression = this.form.cronExpression;
  264. this.openCron = true;
  265. },
  266. /** 确定后回传值 */
  267. crontabFill(value) {
  268. this.form.cronExpression = value;
  269. },
  270. /** 任务日志列表查询 */
  271. handleJobLog(row) {
  272. if (row.id) {
  273. this.$router.push({
  274. path:"/job/log",
  275. query:{
  276. jobId: row.id
  277. }
  278. });
  279. } else {
  280. this.$router.push("/job/log");
  281. }
  282. },
  283. /** 新增按钮操作 */
  284. handleAdd() {
  285. this.reset();
  286. this.open = true;
  287. this.title = "添加任务";
  288. },
  289. /** 修改按钮操作 */
  290. handleUpdate(row) {
  291. this.reset();
  292. const id = row.id;
  293. getJob(id).then(response => {
  294. this.form = response.data;
  295. this.open = true;
  296. this.title = "修改任务";
  297. });
  298. },
  299. /** 提交按钮 */
  300. submitForm: function() {
  301. this.$refs["form"].validate(valid => {
  302. if (valid) {
  303. if (this.form.id !== undefined) {
  304. updateJob(this.form).then(response => {
  305. this.$modal.msgSuccess("修改成功");
  306. this.open = false;
  307. this.getList();
  308. });
  309. } else {
  310. addJob(this.form).then(response => {
  311. this.$modal.msgSuccess("新增成功");
  312. this.open = false;
  313. this.getList();
  314. });
  315. }
  316. }
  317. });
  318. },
  319. /** 删除按钮操作 */
  320. handleDelete(row) {
  321. const ids = row.id;
  322. this.$modal.confirm('是否确认删除定时任务编号为"' + ids + '"的数据项?').then(function() {
  323. return delJob(ids);
  324. }).then(() => {
  325. this.getList();
  326. this.$modal.msgSuccess("删除成功");
  327. }).catch(() => {});
  328. },
  329. /** 更新状态操作 */
  330. handleChangeStatus(row, open) {
  331. const id = row.id;
  332. let status = open ? InfraJobStatusEnum.NORMAL : InfraJobStatusEnum.STOP;
  333. let statusStr = open ? '开启' : '关闭';
  334. this.$modal.confirm('是否确认' + statusStr + '定时任务编号为"' + id + '"的数据项?').then(function() {
  335. return updateJobStatus(id, status);
  336. }).then(() => {
  337. this.getList();
  338. this.$modal.msgSuccess(statusStr + "成功");
  339. }).catch(() => {});
  340. },
  341. // 更多操作触发
  342. handleCommand(command, row) {
  343. switch (command) {
  344. case "handleRun":
  345. this.handleRun(row);
  346. break;
  347. case "handleView":
  348. this.handleView(row);
  349. break;
  350. case "handleJobLog":
  351. this.handleJobLog(row);
  352. break;
  353. default:
  354. break;
  355. }
  356. },
  357. /** 导出按钮操作 */
  358. handleExport() {
  359. const queryParams = this.queryParams;
  360. this.$modal.confirm("是否确认导出所有定时任务数据项?").then(() => {
  361. this.exportLoading = true;
  362. return exportJob(queryParams);
  363. }).then(response => {
  364. this.$download.excel(response, '定时任务.xls');
  365. this.exportLoading = false;
  366. }).catch(() => {});
  367. }
  368. }
  369. };
  370. </script>