瀏覽代碼

feat:【infra】任务管理:100%

YunaiV 4 月之前
父節點
當前提交
9861a4a9a1

+ 17 - 2
src/pages-infra/job/components/job-list.vue

@@ -34,6 +34,12 @@
             <text class="mr-8rpx text-[#999]">创建时间:</text>
             <text>{{ formatDateTime(item.createTime) || '-' }}</text>
           </view>
+          <!-- 查看日志按钮 -->
+          <view class="flex justify-end -mt-8">
+            <wd-button size="small" type="info" @click.stop="handleViewLog(item)">
+              调度日志
+            </wd-button>
+          </view>
         </view>
       </view>
 
@@ -69,6 +75,10 @@ import { DICT_TYPE } from '@/utils/constants'
 import { formatDateTime } from '@/utils/date'
 import JobSearchForm from './job-search-form.vue'
 
+const emit = defineEmits<{
+  viewLog: [jobId: number]
+}>()
+
 const { hasAccessByCodes } = useAccess()
 const total = ref(0)
 const list = ref<Job[]>([])
@@ -120,17 +130,22 @@ function loadMore() {
 /** 新增 */
 function handleAdd() {
   uni.navigateTo({
-    url: '/pages-system/job/job/form/index',
+    url: '/pages-infra/job/job/form/index',
   })
 }
 
 /** 查看详情 */
 function handleDetail(item: Job) {
   uni.navigateTo({
-    url: `/pages-system/job/job/detail/index?id=${item.id}`,
+    url: `/pages-infra/job/job/detail/index?id=${item.id}`,
   })
 }
 
+/** 查看调度日志 */
+function handleViewLog(item: Job) {
+  emit('viewLog', item.id)
+}
+
 /** 触底加载更多 */
 onReachBottom(() => {
   loadMore()

+ 19 - 3
src/pages-infra/job/components/log-list.vue

@@ -1,7 +1,7 @@
 <template>
   <view>
     <!-- 搜索组件 -->
-    <LogSearchForm @search="handleQuery" @reset="handleReset" />
+    <LogSearchForm :job-id="jobId" @search="handleQuery" @reset="handleReset" />
 
     <!-- 日志列表 -->
     <view class="p-24rpx">
@@ -53,12 +53,16 @@
 <script lang="ts" setup>
 import type { JobLog } from '@/api/infra/job/log'
 import type { LoadMoreState } from '@/http/types'
-import { ref } from 'vue'
+import { ref, watch } from 'vue'
 import { getJobLogPage } from '@/api/infra/job/log'
 import { DICT_TYPE } from '@/utils/constants'
 import { formatDateTime } from '@/utils/date'
 import LogSearchForm from './log-search-form.vue'
 
+const props = defineProps<{
+  jobId?: number
+}>()
+
 const total = ref(0)
 const list = ref<JobLog[]>([])
 const loadMoreState = ref<LoadMoreState>('loading')
@@ -109,7 +113,7 @@ function loadMore() {
 /** 查看详情 */
 function handleDetail(item: JobLog) {
   uni.navigateTo({
-    url: `/pages-system/job/log/detail/index?id=${item.id}`,
+    url: `/pages-infra/job/log/detail/index?id=${item.id}`,
   })
 }
 
@@ -122,4 +126,16 @@ onReachBottom(() => {
 onMounted(() => {
   getList()
 })
+
+/** 监听 jobId 变化,重新查询 */
+watch(
+  () => props.jobId,
+  () => {
+    if (props.jobId) {
+      queryParams.value.pageNo = 1
+      list.value = []
+      getList()
+    }
+  },
+)
 </script>

+ 15 - 2
src/pages-infra/job/components/log-search-form.vue

@@ -93,12 +93,16 @@
 </template>
 
 <script lang="ts" setup>
-import { computed, reactive, ref } from 'vue'
+import { computed, reactive, ref, watch } from 'vue'
 import { getDictLabel, getIntDictOptions } from '@/hooks/useDict'
 import { getNavbarHeight } from '@/utils'
 import { DICT_TYPE } from '@/utils/constants'
 import { formatDate, formatDateRange } from '@/utils/date'
 
+const props = defineProps<{
+  jobId?: number
+}>()
+
 const emit = defineEmits<{
   search: [data: Record<string, any>]
   reset: []
@@ -106,7 +110,7 @@ const emit = defineEmits<{
 
 const visible = ref(false)
 const formData = reactive({
-  jobId: undefined as string | undefined,
+  jobId: undefined as number | undefined,
   handlerName: undefined as string | undefined,
   status: -1, // -1 表示全部
   beginTime: [undefined, undefined] as [number | undefined, number | undefined],
@@ -166,4 +170,13 @@ function handleReset() {
   visible.value = false
   emit('reset')
 }
+
+/** 监听外部 jobId 变化 */
+watch(
+  () => props.jobId,
+  (val) => {
+    formData.jobId = val
+  },
+  { immediate: true },
+)
 </script>

+ 26 - 3
src/pages-infra/job/index.vue

@@ -15,17 +15,22 @@
       </wd-tabs>
     </view>
     <!-- 列表内容 -->
-    <JobList v-show="tabType === 'job'" />
-    <LogList v-show="tabType === 'log'" />
+    <JobList v-show="tabType === 'job'" @view-log="handleViewLog" />
+    <LogList v-show="tabType === 'log'" :job-id="selectedJobId" />
   </view>
 </template>
 
 <script lang="ts" setup>
-import { computed, ref } from 'vue'
+import { computed, onMounted, ref } from 'vue'
 import { navigateBackPlus } from '@/utils'
 import JobList from './components/job-list.vue'
 import LogList from './components/log-list.vue'
 
+const props = defineProps<{
+  tab?: string
+  jobId?: number | any
+}>()
+
 definePage({
   style: {
     navigationBarTitleText: '',
@@ -36,16 +41,34 @@ definePage({
 const tabTypes: string[] = ['job', 'log']
 const tabIndex = ref(0)
 const tabType = computed<string>(() => tabTypes[tabIndex.value])
+const selectedJobId = ref<number>() // 选中的任务 ID
 
 /** Tab 切换 */
 function handleTabChange({ index }: { index: number }) {
   tabIndex.value = index
 }
 
+/** 查看调度日志 */
+function handleViewLog(jobId: number) {
+  selectedJobId.value = jobId
+  tabIndex.value = 1 // 切换到调度日志 tab
+}
+
 /** 返回上一页 */
 function handleBack() {
   navigateBackPlus()
 }
+
+/** 初始化 */
+onMounted(() => {
+  // 支持通过 URL 参数切换到日志 tab
+  if (props.tab === 'log') {
+    tabIndex.value = 1
+    if (props.jobId) {
+      selectedJobId.value = Number(props.jobId)
+    }
+  }
+})
 </script>
 
 <style lang="scss" scoped>

+ 81 - 14
src/pages-infra/job/job/detail/index.vue

@@ -28,12 +28,6 @@
     <!-- 底部操作按钮 -->
     <view class="fixed bottom-0 left-0 right-0 bg-white p-24rpx">
       <view class="w-full flex gap-24rpx">
-        <wd-button
-          v-if="hasAccessByCodes(['infra:job:trigger'])"
-          class="flex-1" type="success" :loading="running" @click="handleRun"
-        >
-          执行一次
-        </wd-button>
         <wd-button
           v-if="hasAccessByCodes(['infra:job:update'])"
           class="flex-1" type="warning" @click="handleEdit"
@@ -46,19 +40,28 @@
         >
           删除
         </wd-button>
+        <wd-button
+          v-if="hasMoreActions"
+          class="flex-1" type="info" @click="moreActionVisible = true"
+        >
+          更多
+        </wd-button>
       </view>
     </view>
+
+    <!-- 更多操作菜单 -->
+    <wd-action-sheet v-model="moreActionVisible" :actions="moreActions" @select="handleMoreAction" />
   </view>
 </template>
 
 <script lang="ts" setup>
 import type { Job } from '@/api/infra/job'
-import { onMounted, ref } from 'vue'
+import { computed, onMounted, ref } from 'vue'
 import { useToast } from 'wot-design-uni'
-import { deleteJob, getJob, runJob } from '@/api/infra/job'
+import { deleteJob, getJob, runJob, updateJobStatus } from '@/api/infra/job'
 import { useAccess } from '@/hooks/useAccess'
 import { navigateBackPlus } from '@/utils'
-import { DICT_TYPE } from '@/utils/constants'
+import { DICT_TYPE, InfraJobStatusEnum } from '@/utils/constants'
 import { formatDateTime } from '@/utils/date'
 
 const props = defineProps<{
@@ -76,11 +79,30 @@ const { hasAccessByCodes } = useAccess()
 const toast = useToast()
 const formData = ref<Job>()
 const deleting = ref(false)
-const running = ref(false)
+
+const moreActionVisible = ref(false) // 更多操作菜单
+const moreActions = computed(() => {
+  const actions = []
+  // 执行一次权限
+  if (hasAccessByCodes(['infra:job:trigger'])) {
+    actions.push({ name: '执行一次', value: 'run' })
+  }
+  // 更新状态权限
+  if (hasAccessByCodes(['infra:job:update'])) {
+    const isRunning = formData.value?.status === InfraJobStatusEnum.NORMAL
+    actions.push({ name: isRunning ? '暂停任务' : '开启任务', value: 'update-status' })
+  }
+  // 查看调度日志权限
+  if (hasAccessByCodes(['infra:job:query'])) {
+    actions.push({ name: '调度日志', value: 'view-log' })
+  }
+  return actions
+})
+const hasMoreActions = computed(() => moreActions.value.length > 0)
 
 /** 返回上一页 */
 function handleBack() {
-  navigateBackPlus('/pages-system/job/index')
+  navigateBackPlus('/pages-infra/job/index')
 }
 
 /** 加载详情 */
@@ -108,12 +130,12 @@ function handleRun() {
       if (!res.confirm) {
         return
       }
-      running.value = true
       try {
+        toast.loading('执行中...')
         await runJob(props.id)
         toast.success('执行成功')
       } finally {
-        running.value = false
+        toast.close()
       }
     },
   })
@@ -122,7 +144,7 @@ function handleRun() {
 /** 编辑 */
 function handleEdit() {
   uni.navigateTo({
-    url: `/pages-system/job/job/form/index?id=${props.id}`,
+    url: `/pages-infra/job/job/form/index?id=${props.id}`,
   })
 }
 
@@ -152,6 +174,51 @@ function handleDelete() {
   })
 }
 
+/** 更多操作 */
+function handleMoreAction({ item }: { item: { value: string } }) {
+  if (item.value === 'run') {
+    handleRun()
+  } else if (item.value === 'update-status') {
+    handleUpdateStatus()
+  } else if (item.value === 'view-log') {
+    handleViewLog()
+  }
+}
+
+/** 更新任务状态 */
+function handleUpdateStatus() {
+  if (!props.id) {
+    return
+  }
+  const isRunning = formData.value?.status === InfraJobStatusEnum.NORMAL
+  const statusText = isRunning ? '暂停' : '开启'
+  uni.showModal({
+    title: '提示',
+    content: `确定要${statusText}该任务吗?`,
+    success: async (res) => {
+      if (!res.confirm) {
+        return
+      }
+      try {
+        toast.loading(`正在${statusText}中...`)
+        const newStatus = isRunning ? InfraJobStatusEnum.STOP : InfraJobStatusEnum.NORMAL
+        await updateJobStatus(props.id, newStatus)
+        toast.success(`${statusText}成功`)
+        await getDetail()
+      } finally {
+        toast.close()
+      }
+    },
+  })
+}
+
+/** 查看调度日志 */
+function handleViewLog() {
+  uni.navigateTo({
+    url: `/pages-infra/job/index?tab=log&jobId=${props.id}`,
+  })
+}
+
 /** 初始化 */
 onMounted(() => {
   getDetail()

+ 1 - 1
src/pages-infra/job/job/form/index.vue

@@ -131,7 +131,7 @@ const formRef = ref()
 
 /** 返回上一页 */
 function handleBack() {
-  navigateBackPlus('/pages-system/job/index')
+  navigateBackPlus('/pages-infra/job/index')
 }
 
 /** 加载详情 */

+ 1 - 1
src/pages-infra/job/log/detail/index.vue

@@ -54,7 +54,7 @@ const formData = ref<JobLog>()
 
 /** 返回上一页 */
 function handleBack() {
-  navigateBackPlus('/pages-system/job/index')
+  navigateBackPlus('/pages-infra/job/index')
 }
 
 /** 加载详情 */

+ 8 - 0
src/pages/index/index.ts

@@ -209,6 +209,14 @@ const menuGroupsData: MenuGroup[] = [
         url: '/pages-infra/web-socket/index',
         iconColor: '#36cfc9',
       },
+      {
+        key: 'job',
+        name: '定时任务',
+        icon: 'clock',
+        url: '/pages-infra/job/index',
+        iconColor: '#eb2f96',
+        permission: 'infra:job:query',
+      },
       {
         key: 'codegen',
         name: '代码生成',