zhou zhou
8 小时以前 450c9d39c6eb3765642f977512202e3240ac9b03
rsf-design/src/views/manager/task/index.vue
@@ -9,7 +9,30 @@
    />
    <ElCard class="art-table-card">
      <ArtTableHeader v-model:columns="columnChecks" :loading="loading" @refresh="loadPageData" />
      <ArtTableHeader v-model:columns="columnChecks" :loading="loading" @refresh="loadPageData">
        <template #left>
          <ElSpace wrap>
            <ElButton
              v-if="!autoRunEnabled"
              type="primary"
              plain
              :loading="autoRunLoading"
              @click="handleToggleAutoRun(true)"
            >
              {{ t('pages.task.buttons.autoRun') }}
            </ElButton>
            <ElButton
              v-else
              type="warning"
              plain
              :loading="autoRunLoading"
              @click="handleToggleAutoRun(false)"
            >
              {{ t('pages.task.buttons.pauseAutoRun') }}
            </ElButton>
          </ElSpace>
        </template>
      </ArtTableHeader>
      <ArtTable
        :loading="loading"
@@ -21,6 +44,8 @@
      />
    </ElCard>
    <TaskFlowStepDialog v-model:visible="flowStepDialogVisible" :task-row="activeTaskRow" />
    <TaskDetailDrawer
      v-model:visible="detailDrawerVisible"
      :loading="detailLoading"
@@ -31,13 +56,15 @@
      @refresh="loadDetailResources"
      @size-change="handleDetailSizeChange"
      @current-change="handleDetailCurrentChange"
      @flow-step="handleOpenFlowStepFromDetail"
    />
  </div>
</template>
<script setup>
  import { ElMessage } from 'element-plus'
  import { computed, onMounted, reactive, ref } from 'vue'
  import { computed, h, onMounted, onUnmounted, reactive, ref } from 'vue'
  import { useI18n } from 'vue-i18n'
  import { useTableColumns } from '@/hooks/core/useTableColumns'
  import { guardRequestWithMessage } from '@/utils/sys/requestGuard'
  import {
@@ -45,12 +72,16 @@
    fetchCompleteTask,
    fetchPickTask,
    fetchRemoveTask,
    fetchTaskAutoRunFlag,
    fetchTaskDetail,
    fetchTaskItemPage,
    fetchTaskPage,
    fetchTopTask
    fetchTopTask,
    fetchUpdateTaskAutoRunFlag
  } from '@/api/task'
  import TaskDetailDrawer from './modules/task-detail-drawer.vue'
  import TaskExpandPanel from './modules/task-expand-panel.vue'
  import TaskFlowStepDialog from './modules/task-flow-step-dialog.vue'
  import { createTaskTableColumns } from './taskTable.columns'
  import {
    buildTaskPageQueryParams,
@@ -61,6 +92,7 @@
  } from './taskPage.helpers'
  defineOptions({ name: 'Task' })
  const { t } = useI18n()
  const loading = ref(false)
  const tableData = ref([])
@@ -70,6 +102,10 @@
  const detailData = ref({})
  const detailTableData = ref([])
  const activeTaskRow = ref(null)
  const flowStepDialogVisible = ref(false)
  const autoRunEnabled = ref(false)
  const autoRunLoading = ref(false)
  let refreshTimer = null
  const pagination = reactive({
    current: 1,
@@ -85,106 +121,166 @@
  const searchItems = computed(() => [
    {
      label: '关键字',
      label: t('pages.task.search.condition'),
      key: 'condition',
      type: 'input',
      props: { clearable: true, placeholder: '请输入任务号/库位/托盘码' }
      props: { clearable: true, placeholder: t('pages.task.placeholder.condition') }
    },
    {
      label: '任务号',
      label: t('pages.task.search.taskCode'),
      key: 'taskCode',
      type: 'input',
      props: { clearable: true, placeholder: '请输入任务号' }
      props: { clearable: true, placeholder: t('pages.task.placeholder.taskCode') }
    },
    {
      label: '源库位',
      label: t('pages.task.search.orgLoc'),
      key: 'orgLoc',
      type: 'input',
      props: { clearable: true, placeholder: '请输入源库位' }
      props: { clearable: true, placeholder: t('pages.task.placeholder.orgLoc') }
    },
    {
      label: '目标库位',
      label: t('pages.task.search.targLoc'),
      key: 'targLoc',
      type: 'input',
      props: { clearable: true, placeholder: '请输入目标库位' }
      props: { clearable: true, placeholder: t('pages.task.placeholder.targLoc') }
    },
    {
      label: '托盘码',
      label: t('pages.task.search.barcode'),
      key: 'barcode',
      type: 'input',
      props: { clearable: true, placeholder: '请输入托盘码' }
      props: { clearable: true, placeholder: t('pages.task.placeholder.barcode') }
    }
  ])
  const detailColumns = computed(() => [
    {
      type: 'globalIndex',
      label: '序号',
      label: t('table.index'),
      width: 72,
      align: 'center'
    },
    {
      prop: 'orderTypeLabel',
      label: '单据类型',
      label: t('pages.orders.delivery.detail.type'),
      minWidth: 120,
      showOverflowTooltip: true
    },
    {
      prop: 'wkTypeLabel',
      label: '业务类型',
      label: t('pages.orders.delivery.detail.wkType'),
      minWidth: 120,
      showOverflowTooltip: true
    },
    {
      prop: 'platOrderCode',
      label: t('pages.task.expand.platOrderCode'),
      minWidth: 150,
      showOverflowTooltip: true
    },
    {
      prop: 'platWorkCode',
      label: '工单号',
      label: t('pages.orders.transfer.detail.relatedCode'),
      minWidth: 150,
      showOverflowTooltip: true
    },
    {
      prop: 'platItemId',
      label: '行号',
      label: t('pages.orders.delivery.table.platItemId'),
      minWidth: 100,
      showOverflowTooltip: true
    },
    {
      prop: 'projectCode',
      label: t('pages.task.expand.projectCode'),
      minWidth: 140,
      showOverflowTooltip: true
    },
    {
      prop: 'matnrCode',
      label: '物料编码',
      label: t('pages.orders.delivery.table.matnrCode'),
      minWidth: 150,
      showOverflowTooltip: true
    },
    {
      prop: 'maktx',
      label: '物料名称',
      label: t('pages.orders.delivery.table.maktx'),
      minWidth: 220,
      showOverflowTooltip: true
    },
    {
      prop: 'batch',
      label: '批次',
      label: t('table.batch'),
      minWidth: 140,
      showOverflowTooltip: true
    },
    {
      prop: 'unit',
      label: '单位',
      label: t('table.unit'),
      width: 100
    },
    {
      prop: 'anfme',
      label: '数量',
      label: t('pages.orders.delivery.table.anfme'),
      width: 100,
      align: 'right'
    },
    {
      prop: 'updateByText',
      label: '更新人',
      prop: 'workQty',
      label: t('pages.task.expand.workQty'),
      width: 100,
      align: 'right'
    },
    {
      prop: 'qty',
      label: t('pages.task.expand.qty'),
      width: 100,
      align: 'right'
    },
    {
      prop: 'spec',
      label: t('pages.task.expand.spec'),
      minWidth: 140,
      showOverflowTooltip: true
    },
    {
      prop: 'model',
      label: t('pages.task.expand.model'),
      minWidth: 140,
      showOverflowTooltip: true
    },
    {
      prop: 'createByText',
      label: t('table.createBy'),
      minWidth: 120,
      showOverflowTooltip: true
    },
    {
      prop: 'createTimeText',
      label: t('table.createTime'),
      minWidth: 180,
      showOverflowTooltip: true
    },
    {
      prop: 'updateByText',
      label: t('pages.orders.delivery.detail.updateBy'),
      minWidth: 120,
      showOverflowTooltip: true
    },
    {
      prop: 'statusText',
      label: t('table.status'),
      minWidth: 120,
      showOverflowTooltip: true
    },
    {
      prop: 'memo',
      label: t('table.remark'),
      minWidth: 180,
      showOverflowTooltip: true
    },
    {
      prop: 'updateTimeText',
      label: '更新时间',
      label: t('pages.orders.delivery.detail.updateTime'),
      minWidth: 180,
      showOverflowTooltip: true
    }
@@ -192,9 +288,17 @@
  async function openDetailDrawer(row) {
    activeTaskRow.value = row
    detailData.value = normalizeTaskRow(row)
    detailPagination.current = 1
    detailDrawerVisible.value = true
    await loadDetailResources()
  }
  function handleOpenFlowStepFromDetail() {
    if (!activeTaskRow.value) {
      return
    }
    flowStepDialogVisible.value = true
  }
  async function handleActionClick(action, row) {
@@ -204,25 +308,35 @@
        return
      }
      if (action.key === 'flowStep') {
        activeTaskRow.value = row
        flowStepDialogVisible.value = true
        return
      }
      if (action.key === 'complete') {
        await confirmTaskAction(`确定完成任务 ${row.taskCode || ''} 吗?`)
        await confirmTaskAction(
          t('pages.task.messages.completeConfirm', { code: row.taskCode || '' })
        )
        await fetchCompleteTask(row.id)
        ElMessage.success('任务完成成功')
        ElMessage.success(t('pages.task.messages.completeSuccess'))
      } else if (action.key === 'remove') {
        await confirmTaskAction(`确定取消任务 ${row.taskCode || ''} 吗?`)
        await confirmTaskAction(
          t('pages.task.messages.removeConfirm', { code: row.taskCode || '' })
        )
        await fetchRemoveTask(row.id)
        ElMessage.success('任务取消成功')
        ElMessage.success(t('pages.task.messages.removeSuccess'))
      } else if (action.key === 'check') {
        await confirmTaskAction(`确定执行盘点出库任务 ${row.taskCode || ''} 吗?`)
        await confirmTaskAction(t('pages.task.messages.checkConfirm', { code: row.taskCode || '' }))
        await fetchCheckTask(row.id)
        ElMessage.success('盘点出库成功')
        ElMessage.success(t('pages.task.messages.checkSuccess'))
      } else if (action.key === 'pick') {
        await confirmTaskAction(`确定执行拣料出库任务 ${row.taskCode || ''} 吗?`)
        await confirmTaskAction(t('pages.task.messages.pickConfirm', { code: row.taskCode || '' }))
        await fetchPickTask(row.id)
        ElMessage.success('拣料出库成功')
        ElMessage.success(t('pages.task.messages.pickSuccess'))
      } else if (action.key === 'top') {
        await fetchTopTask(row.id)
        ElMessage.success('任务置顶成功')
        ElMessage.success(t('pages.task.messages.topSuccess'))
      }
      await loadPageData()
@@ -233,11 +347,21 @@
      if (error === 'cancel') {
        return
      }
      ElMessage.error(error?.message || '任务操作失败')
      ElMessage.error(error?.message || t('pages.task.messages.actionFailed'))
    }
  }
  const { columns, columnChecks } = useTableColumns(() => createTaskTableColumns(handleActionClick))
  const { columns, columnChecks } = useTableColumns(() =>
    createTaskTableColumns({
      handleActionClick,
      createExpandContent: (row) => ({
        name: 'TaskExpandPanelHost',
        render() {
          return h(TaskExpandPanel, { row })
        }
      })
    })
  )
  function updatePaginationState(target, response, fallbackCurrent, fallbackSize) {
    target.total = Number(response?.total || 0)
@@ -262,7 +386,7 @@
          current: pagination.current,
          size: pagination.size
        },
        { timeoutMessage: '任务列表加载超时,已停止等待' }
        { timeoutMessage: t('pages.task.messages.listTimeout') }
      )
      tableData.value = Array.isArray(response?.records)
        ? response.records.map((record) => normalizeTaskRow(record))
@@ -273,6 +397,41 @@
    }
  }
  async function loadAutoRunConfig() {
    autoRunLoading.value = true
    try {
      const response = await guardRequestWithMessage(
        fetchTaskAutoRunFlag(),
        { val: false },
        {
          timeoutMessage: t('pages.task.messages.autoRunTimeout')
        }
      )
      const rawValue = response?.val
      autoRunEnabled.value =
        rawValue === true || rawValue === 'true' || rawValue === 1 || rawValue === '1'
    } finally {
      autoRunLoading.value = false
    }
  }
  async function handleToggleAutoRun(enabled) {
    autoRunLoading.value = true
    try {
      await fetchUpdateTaskAutoRunFlag(enabled)
      autoRunEnabled.value = enabled
      ElMessage.success(
        enabled
          ? t('pages.task.messages.autoRunOnSuccess')
          : t('pages.task.messages.autoRunOffSuccess')
      )
    } catch (error) {
      ElMessage.error(error?.message || t('pages.task.messages.autoRunUpdateFailed'))
    } finally {
      autoRunLoading.value = false
    }
  }
  async function loadDetailResources() {
    if (!activeTaskRow.value?.id) {
      return
@@ -280,9 +439,9 @@
    detailLoading.value = true
    try {
      const [detailResponse, taskItemResponse] = await Promise.all([
        guardRequestWithMessage(fetchTaskDetail(activeTaskRow.value.id), {}, {
          timeoutMessage: '任务详情加载超时,已停止等待'
      const [taskDetailResult, taskItemResult] = await Promise.allSettled([
        guardRequestWithMessage(fetchTaskDetail(activeTaskRow.value.id), activeTaskRow.value, {
          timeoutMessage: t('pages.task.messages.detailTimeout')
        }),
        guardRequestWithMessage(
          fetchTaskItemPage({
@@ -296,15 +455,44 @@
            current: detailPagination.current,
            size: detailPagination.size
          },
          { timeoutMessage: '任务明细加载超时,已停止等待' }
          { timeoutMessage: t('pages.task.messages.itemsTimeout') }
        )
      ])
      detailData.value = normalizeTaskRow(detailResponse)
      detailTableData.value = Array.isArray(taskItemResponse?.records)
      const taskDetailResponse =
        taskDetailResult.status === 'fulfilled' ? taskDetailResult.value : activeTaskRow.value
      const taskItemResponse =
        taskItemResult.status === 'fulfilled'
          ? taskItemResult.value
          : {
              records: [],
              total: 0,
              current: detailPagination.current,
              size: detailPagination.size
            }
      activeTaskRow.value = {
        ...activeTaskRow.value,
        ...taskDetailResponse
      }
      detailData.value = normalizeTaskRow(activeTaskRow.value)
      detailTableData.value = Array.isArray(taskItemResponse.records)
        ? taskItemResponse.records.map((record) => normalizeTaskItemRow(record))
        : []
      updatePaginationState(detailPagination, taskItemResponse, detailPagination.current, detailPagination.size)
      updatePaginationState(
        detailPagination,
        taskItemResponse,
        detailPagination.current,
        detailPagination.size
      )
      if (taskDetailResult.status === 'rejected' && taskItemResult.status === 'rejected') {
        throw taskDetailResult.reason || taskItemResult.reason
      }
    } catch (error) {
      detailTableData.value = []
      detailData.value = normalizeTaskRow(activeTaskRow.value)
      ElMessage.error(error?.message || t('pages.task.messages.detailLoadFailed'))
    } finally {
      detailLoading.value = false
    }
@@ -348,5 +536,26 @@
    loadDetailResources()
  }
  onMounted(loadPageData)
  function startAutoRefresh() {
    clearAutoRefresh()
    refreshTimer = window.setInterval(() => {
      void loadPageData()
    }, 5000)
  }
  function clearAutoRefresh() {
    if (refreshTimer) {
      window.clearInterval(refreshTimer)
      refreshTimer = null
    }
  }
  onMounted(async () => {
    await Promise.all([loadPageData(), loadAutoRunConfig()])
    startAutoRefresh()
  })
  onUnmounted(() => {
    clearAutoRefresh()
  })
</script>