| | |
| | | /> |
| | | |
| | | <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" |
| | |
| | | /> |
| | | </ElCard> |
| | | |
| | | <TaskFlowStepDialog v-model:visible="flowStepDialogVisible" :task-row="activeTaskRow" /> |
| | | |
| | | <TaskDetailDrawer |
| | | v-model:visible="detailDrawerVisible" |
| | | :loading="detailLoading" |
| | |
| | | @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 { |
| | |
| | | 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, |
| | |
| | | } from './taskPage.helpers' |
| | | |
| | | defineOptions({ name: 'Task' }) |
| | | const { t } = useI18n() |
| | | |
| | | const loading = ref(false) |
| | | const tableData = ref([]) |
| | |
| | | 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, |
| | |
| | | |
| | | 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 |
| | | } |
| | |
| | | |
| | | 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) { |
| | |
| | | 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() |
| | |
| | | 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) |
| | |
| | | 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)) |
| | |
| | | } |
| | | } |
| | | |
| | | 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 |
| | |
| | | |
| | | 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({ |
| | |
| | | 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 |
| | | } |
| | |
| | | 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> |