index.vue 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. <script lang="ts" setup>
  2. import { Crontab } from '@/components/Crontab'
  3. import { ref, unref } from 'vue'
  4. import * as JobApi from '@/api/infra/job'
  5. import { JobVO } from '@/api/infra/job/types'
  6. import { DICT_TYPE } from '@/utils/dict'
  7. import { useTable } from '@/hooks/web/useTable'
  8. import { useI18n } from '@/hooks/web/useI18n'
  9. import { FormExpose } from '@/components/Form'
  10. import { rules, allSchemas } from './job.data'
  11. import { useRouter } from 'vue-router'
  12. import { useMessage } from '@/hooks/web/useMessage'
  13. import { InfraJobStatusEnum } from '@/utils/constants'
  14. const message = useMessage()
  15. const { t } = useI18n() // 国际化
  16. const { push } = useRouter()
  17. // ========== 列表相关 ==========
  18. const { register, tableObject, methods } = useTable<JobVO>({
  19. getListApi: JobApi.getJobPageApi,
  20. delListApi: JobApi.deleteJobApi,
  21. exportListApi: JobApi.exportJobApi
  22. })
  23. const { getList, setSearchParams, delList, exportList } = methods
  24. // ========== CRUD 相关 ==========
  25. const actionLoading = ref(false) // 遮罩层
  26. const actionType = ref('') // 操作按钮的类型
  27. const dialogVisible = ref(false) // 是否显示弹出层
  28. const dialogTitle = ref('edit') // 弹出层标题
  29. const formRef = ref<FormExpose>() // 表单 Ref
  30. const cronExpression = ref('')
  31. const shortcuts = ref([
  32. {
  33. text: '每天8点和12点 (自定义追加)',
  34. value: '0 0 8,12 * * ?'
  35. }
  36. ])
  37. // 设置标题
  38. const setDialogTile = (type: string) => {
  39. dialogTitle.value = t('action.' + type)
  40. actionType.value = type
  41. dialogVisible.value = true
  42. }
  43. // 新增操作
  44. const handleCreate = () => {
  45. cronExpression.value = ''
  46. setDialogTile('create')
  47. // 重置表单
  48. unref(formRef)?.getElFormRef()?.resetFields()
  49. }
  50. // 修改操作
  51. const handleUpdate = async (row: JobVO) => {
  52. setDialogTile('update')
  53. // 设置数据
  54. const res = await JobApi.getJobApi(row.id)
  55. cronExpression.value = res.cronExpression
  56. unref(formRef)?.setValues(res)
  57. }
  58. const handleChangeStatus = async (row: JobVO) => {
  59. const text = row.status === InfraJobStatusEnum.STOP ? '开启' : '关闭'
  60. const status =
  61. row.status === InfraJobStatusEnum.STOP ? InfraJobStatusEnum.NORMAL : InfraJobStatusEnum.STOP
  62. message
  63. .confirm('确认要' + text + '定时任务编号为"' + row.id + '"的数据项?', t('common.reminder'))
  64. .then(async () => {
  65. row.status =
  66. row.status === InfraJobStatusEnum.NORMAL
  67. ? InfraJobStatusEnum.NORMAL
  68. : InfraJobStatusEnum.STOP
  69. await JobApi.updateJobStatusApi(row.id, status)
  70. message.success(text + '成功')
  71. await getList()
  72. })
  73. .catch(() => {
  74. row.status =
  75. row.status === InfraJobStatusEnum.NORMAL
  76. ? InfraJobStatusEnum.STOP
  77. : InfraJobStatusEnum.NORMAL
  78. })
  79. }
  80. // 执行日志
  81. const handleJobLog = (row: JobVO) => {
  82. if (row.id) {
  83. push('/job/job-log?id=' + row.id)
  84. } else {
  85. push('/job/job-log')
  86. }
  87. }
  88. // 执行一次
  89. const handleRun = (row: JobVO) => {
  90. message.confirm('确认要立即执行一次' + row.name + '?', t('common.reminder')).then(async () => {
  91. await JobApi.runJobApi(row.id)
  92. message.success('执行成功')
  93. getList()
  94. })
  95. }
  96. // 提交按钮
  97. const submitForm = async () => {
  98. const elForm = unref(formRef)?.getElFormRef()
  99. if (!elForm) return
  100. elForm.validate(async (valid) => {
  101. if (valid) {
  102. actionLoading.value = true
  103. // 提交请求
  104. try {
  105. const data = unref(formRef)?.formModel as JobVO
  106. data.cronExpression = cronExpression.value
  107. if (actionType.value === 'create') {
  108. await JobApi.createJobApi(data)
  109. message.success(t('common.createSuccess'))
  110. } else {
  111. await JobApi.updateJobApi(data)
  112. message.success(t('common.updateSuccess'))
  113. }
  114. // 操作成功,重新加载列表
  115. dialogVisible.value = false
  116. await getList()
  117. } finally {
  118. actionLoading.value = false
  119. }
  120. }
  121. })
  122. }
  123. // ========== 详情相关 ==========
  124. const detailRef = ref() // 详情 Ref
  125. // 详情操作
  126. const handleDetail = async (row: JobVO) => {
  127. // 设置数据
  128. const res = JobApi.getJobApi(row.id)
  129. detailRef.value = res
  130. setDialogTile('detail')
  131. }
  132. // ========== 初始化 ==========
  133. getList()
  134. </script>
  135. <template>
  136. <!-- 搜索工作区 -->
  137. <ContentWrap>
  138. <Search :schema="allSchemas.searchSchema" @search="setSearchParams" @reset="setSearchParams" />
  139. </ContentWrap>
  140. <ContentWrap>
  141. <!-- 操作工具栏 -->
  142. <div class="mb-10px">
  143. <el-button type="primary" v-hasPermi="['infra:job:create']" @click="handleCreate">
  144. <Icon icon="ep:zoom-in" class="mr-5px" /> {{ t('action.add') }}
  145. </el-button>
  146. <el-button
  147. type="warning"
  148. v-hasPermi="['infra:job:export']"
  149. :loading="tableObject.exportLoading"
  150. @click="exportList('定时任务.xls')"
  151. >
  152. <Icon icon="ep:download" class="mr-5px" /> {{ t('action.export') }}
  153. </el-button>
  154. <el-button type="info" v-hasPermi="['infra:job:query']" @click="handleJobLog">
  155. <Icon icon="ep:zoom-in" class="mr-5px" /> 执行日志
  156. </el-button>
  157. </div>
  158. <!-- 列表 -->
  159. <Table
  160. :columns="allSchemas.tableColumns"
  161. :selection="false"
  162. :data="tableObject.tableList"
  163. :loading="tableObject.loading"
  164. :pagination="{
  165. total: tableObject.total
  166. }"
  167. v-model:pageSize="tableObject.pageSize"
  168. v-model:currentPage="tableObject.currentPage"
  169. @register="register"
  170. >
  171. <template #status="{ row }">
  172. <DictTag :type="DICT_TYPE.INFRA_JOB_STATUS" :value="row.status" />
  173. </template>
  174. <template #action="{ row }">
  175. <el-button link type="primary" v-hasPermi="['infra:job:update']" @click="handleUpdate(row)">
  176. <Icon icon="ep:edit" class="mr-1px" /> {{ t('action.edit') }}
  177. </el-button>
  178. <el-button
  179. link
  180. type="primary"
  181. v-hasPermi="['infra:job:update']"
  182. @click="handleChangeStatus(row)"
  183. >
  184. <Icon icon="ep:edit" class="mr-1px" />
  185. {{ row.status === InfraJobStatusEnum.STOP ? '开启' : '暂停' }}
  186. </el-button>
  187. <el-button link type="primary" v-hasPermi="['infra:job:query']" @click="handleDetail(row)">
  188. <Icon icon="ep:view" class="mr-1px" /> {{ t('action.detail') }}
  189. </el-button>
  190. <el-button
  191. link
  192. type="primary"
  193. v-hasPermi="['infra:job:delete']"
  194. @click="delList(row.id, false)"
  195. >
  196. <Icon icon="ep:delete" class="mr-1px" /> {{ t('action.del') }}
  197. </el-button>
  198. <el-button link type="primary" v-hasPermi="['infra:job:trigger']" @click="handleRun(row)">
  199. <Icon icon="ep:view" class="mr-1px" /> 执行一次
  200. </el-button>
  201. <el-button link type="primary" v-hasPermi="['infra:job:query']" @click="handleJobLog(row)">
  202. <Icon icon="ep:view" class="mr-1px" /> 调度日志
  203. </el-button>
  204. </template>
  205. </Table>
  206. </ContentWrap>
  207. <Dialog v-model="dialogVisible" :title="dialogTitle">
  208. <!-- 对话框(添加 / 修改) -->
  209. <Form
  210. v-if="['create', 'update'].includes(actionType)"
  211. :schema="allSchemas.formSchema"
  212. :rules="rules"
  213. ref="formRef"
  214. >
  215. <template #cronExpression>
  216. <Crontab v-model="cronExpression" :shortcuts="shortcuts" />
  217. </template>
  218. </Form>
  219. <!-- 对话框(详情) -->
  220. <Descriptions
  221. v-if="actionType === 'detail'"
  222. :schema="allSchemas.detailSchema"
  223. :data="detailRef"
  224. >
  225. <template #status="{ row }">
  226. <DictTag :type="DICT_TYPE.INFRA_JOB_STATUS" :value="row.status" />
  227. </template>
  228. <template #retryInterval="{ row }">
  229. <span>{{ row.retryInterval + '毫秒' }} </span>
  230. </template>
  231. <template #monitorTimeout="{ row }">
  232. <span>{{ row.monitorTimeout > 0 ? row.monitorTimeout + ' 毫秒' : '未开启' }}</span>
  233. </template>
  234. </Descriptions>
  235. <!-- 操作按钮 -->
  236. <template #footer>
  237. <el-button
  238. v-if="['create', 'update'].includes(actionType)"
  239. type="primary"
  240. :loading="actionLoading"
  241. @click="submitForm"
  242. >
  243. {{ t('action.save') }}
  244. </el-button>
  245. <el-button @click="dialogVisible = false">{{ t('dialog.close') }}</el-button>
  246. </template>
  247. </Dialog>
  248. </template>