index.vue 15 KB

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