index.vue 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  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. // 修改操作
  49. const handleUpdate = async (row: JobVO) => {
  50. setDialogTile('update')
  51. // 设置数据
  52. const res = await JobApi.getJobApi(row.id)
  53. cronExpression.value = res.cronExpression
  54. unref(formRef)?.setValues(res)
  55. }
  56. const handleChangeStatus = async (row: JobVO) => {
  57. const text = row.status === InfraJobStatusEnum.STOP ? '开启' : '关闭'
  58. const status =
  59. row.status === InfraJobStatusEnum.STOP ? InfraJobStatusEnum.NORMAL : InfraJobStatusEnum.STOP
  60. message
  61. .confirm('确认要' + text + '定时任务编号为"' + row.id + '"的数据项?', t('common.reminder'))
  62. .then(async () => {
  63. row.status =
  64. row.status === InfraJobStatusEnum.NORMAL
  65. ? InfraJobStatusEnum.NORMAL
  66. : InfraJobStatusEnum.STOP
  67. await JobApi.updateJobStatusApi(row.id, status)
  68. message.success(text + '成功')
  69. await getList()
  70. })
  71. .catch(() => {
  72. row.status =
  73. row.status === InfraJobStatusEnum.NORMAL
  74. ? InfraJobStatusEnum.STOP
  75. : InfraJobStatusEnum.NORMAL
  76. })
  77. }
  78. // 执行日志
  79. const handleJobLog = (row: JobVO) => {
  80. if (row.id) {
  81. push('/job/job-log?id=' + row.id)
  82. } else {
  83. push('/job/job-log')
  84. }
  85. }
  86. // 执行一次
  87. const handleRun = (row: JobVO) => {
  88. message.confirm('确认要立即执行一次' + row.name + '?', t('common.reminder')).then(async () => {
  89. await JobApi.runJobApi(row.id)
  90. message.success('执行成功')
  91. getList()
  92. })
  93. }
  94. // 提交按钮
  95. const submitForm = async () => {
  96. const elForm = unref(formRef)?.getElFormRef()
  97. if (!elForm) return
  98. elForm.validate(async (valid) => {
  99. if (valid) {
  100. actionLoading.value = true
  101. // 提交请求
  102. try {
  103. const data = unref(formRef)?.formModel as JobVO
  104. data.cronExpression = cronExpression.value
  105. if (actionType.value === 'create') {
  106. await JobApi.createJobApi(data)
  107. message.success(t('common.createSuccess'))
  108. } else {
  109. await JobApi.updateJobApi(data)
  110. message.success(t('common.updateSuccess'))
  111. }
  112. // 操作成功,重新加载列表
  113. dialogVisible.value = false
  114. await getList()
  115. } finally {
  116. actionLoading.value = false
  117. }
  118. }
  119. })
  120. }
  121. // ========== 详情相关 ==========
  122. const detailRef = ref() // 详情 Ref
  123. // 详情操作
  124. const handleDetail = async (row: JobVO) => {
  125. // 设置数据
  126. const res = JobApi.getJobApi(row.id)
  127. detailRef.value = res
  128. setDialogTile('detail')
  129. }
  130. // ========== 初始化 ==========
  131. getList()
  132. </script>
  133. <template>
  134. <!-- 搜索工作区 -->
  135. <ContentWrap>
  136. <Search :schema="allSchemas.searchSchema" @search="setSearchParams" @reset="setSearchParams" />
  137. </ContentWrap>
  138. <ContentWrap>
  139. <!-- 操作工具栏 -->
  140. <div class="mb-10px">
  141. <el-button type="primary" v-hasPermi="['infra:job:create']" @click="handleCreate">
  142. <Icon icon="ep:zoom-in" class="mr-5px" /> {{ t('action.add') }}
  143. </el-button>
  144. <el-button
  145. type="warning"
  146. v-hasPermi="['infra:job:export']"
  147. :loading="tableObject.exportLoading"
  148. @click="exportList('定时任务.xls')"
  149. >
  150. <Icon icon="ep:download" class="mr-5px" /> {{ t('action.export') }}
  151. </el-button>
  152. <el-button type="info" v-hasPermi="['infra:job:query']" @click="handleJobLog">
  153. <Icon icon="ep:zoom-in" class="mr-5px" /> 执行日志
  154. </el-button>
  155. </div>
  156. <!-- 列表 -->
  157. <Table
  158. :columns="allSchemas.tableColumns"
  159. :selection="false"
  160. :data="tableObject.tableList"
  161. :loading="tableObject.loading"
  162. :pagination="{
  163. total: tableObject.total
  164. }"
  165. v-model:pageSize="tableObject.pageSize"
  166. v-model:currentPage="tableObject.currentPage"
  167. @register="register"
  168. >
  169. <template #status="{ row }">
  170. <DictTag :type="DICT_TYPE.INFRA_JOB_STATUS" :value="row.status" />
  171. </template>
  172. <template #action="{ row }">
  173. <el-button link type="primary" v-hasPermi="['infra:job:update']" @click="handleUpdate(row)">
  174. <Icon icon="ep:edit" class="mr-1px" /> {{ t('action.edit') }}
  175. </el-button>
  176. <el-button
  177. link
  178. type="primary"
  179. v-hasPermi="['infra:job:update']"
  180. @click="handleChangeStatus(row)"
  181. >
  182. <Icon icon="ep:edit" class="mr-1px" />
  183. {{ row.status === InfraJobStatusEnum.STOP ? '开启' : '暂停' }}
  184. </el-button>
  185. <el-button link type="primary" v-hasPermi="['infra:job:query']" @click="handleDetail(row)">
  186. <Icon icon="ep:view" class="mr-1px" /> {{ t('action.detail') }}
  187. </el-button>
  188. <el-button
  189. link
  190. type="primary"
  191. v-hasPermi="['infra:job:delete']"
  192. @click="delList(row.id, false)"
  193. >
  194. <Icon icon="ep:delete" class="mr-1px" /> {{ t('action.del') }}
  195. </el-button>
  196. <el-button link type="primary" v-hasPermi="['infra:job:trigger']" @click="handleRun(row)">
  197. <Icon icon="ep:view" class="mr-1px" /> 执行一次
  198. </el-button>
  199. <el-button link type="primary" v-hasPermi="['infra:job:query']" @click="handleJobLog(row)">
  200. <Icon icon="ep:view" class="mr-1px" /> 调度日志
  201. </el-button>
  202. </template>
  203. </Table>
  204. </ContentWrap>
  205. <XModal v-model="dialogVisible" :title="dialogTitle">
  206. <!-- 对话框(添加 / 修改) -->
  207. <Form
  208. v-if="['create', 'update'].includes(actionType)"
  209. :schema="allSchemas.formSchema"
  210. :rules="rules"
  211. ref="formRef"
  212. >
  213. <template #cronExpression>
  214. <Crontab v-model="cronExpression" :shortcuts="shortcuts" />
  215. </template>
  216. </Form>
  217. <!-- 对话框(详情) -->
  218. <Descriptions
  219. v-if="actionType === 'detail'"
  220. :schema="allSchemas.detailSchema"
  221. :data="detailRef"
  222. >
  223. <template #retryInterval="{ row }">
  224. <span>{{ row.retryInterval + '毫秒' }} </span>
  225. </template>
  226. <template #monitorTimeout="{ row }">
  227. <span>{{ row.monitorTimeout > 0 ? row.monitorTimeout + ' 毫秒' : '未开启' }}</span>
  228. </template>
  229. </Descriptions>
  230. <!-- 操作按钮 -->
  231. <template #footer>
  232. <el-button
  233. v-if="['create', 'update'].includes(actionType)"
  234. type="primary"
  235. :loading="actionLoading"
  236. @click="submitForm"
  237. >
  238. {{ t('action.save') }}
  239. </el-button>
  240. <el-button @click="dialogVisible = false">{{ t('dialog.close') }}</el-button>
  241. </template>
  242. </XModal>
  243. </template>