You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
besure_web/src/views/mes/tasksummary/components/TaskScheduleDialog.vue

760 lines
29 KiB
Vue

<template>
<Dialog v-model="dialogVisible" :title="t('ProductionPlan.TaskSummary.scheduleDialogTitle')" :initialFullscreen="true" destroy-on-close>
<div class="flex items-center gap-20px mb-16px">
<div class="flex items-center gap-8px">
<span class="text-red-500">*</span>
<span>{{ t('ProductionPlan.TaskSummary.scheduleRuleLabel') }}</span>
<el-select v-model="searchForm.sortRule" class="!w-220px" clearable>
<el-option v-for="item in scheduleRuleOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</div>
<div class="flex items-center gap-8px">
<span class="text-red-500">*</span>
<span>{{ t('ProductionPlan.TaskSummary.capacitySourceLabel') }}</span>
<el-select v-model="searchForm.capacityType" class="!w-220px">
<el-option v-for="item in capacityTypeOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</div>
<div class="flex items-center gap-8px">
<span class="text-red-500">*</span>
<span>{{ t('ProductionPlan.TaskSummary.workTimeLabel') }}</span>
<el-time-picker
v-model="searchForm.workTimeRange"
is-range
value-format="HH:mm"
format="HH:mm"
range-separator="To"
start-placeholder="Start time"
end-placeholder="End time"
class="!w-240px"
/>
</div>
<div class="flex items-center gap-8px">
<span>{{ t('ProductionPlan.TaskSummary.skipHolidayLabel') }}</span>
<el-switch v-model="searchForm.skipHoliday" />
</div>
<el-button type="primary" :loading="submitLoading" @click="handleSubmit" class="ml-auto">{{ t('ProductionPlan.TaskSummary.confirmScheduleButton') }}</el-button>
</div>
<div class="border border-solid border-gray-200 rounded p-16px mt-16px">
<el-form :inline="true" :model="searchForm" label-width="90px" class="-mb-8px">
<el-form-item :label="t('ProductionPlan.TaskSummary.scheduleFormCodeLabel')">
<el-input v-model="searchForm.code" :placeholder="t('ProductionPlan.TaskSummary.scheduleFormCodePlaceholder')" clearable class="!w-200px" />
</el-form-item>
<el-form-item :label="t('ProductionPlan.TaskSummary.scheduleFormOrderDateLabel')">
<el-date-picker
v-model="searchForm.orderDate"
type="daterange"
value-format="YYYY-MM-DD HH:mm:ss"
:start-placeholder="t('ProductionPlan.TaskSummary.startDatePlaceholder')"
:end-placeholder="t('ProductionPlan.TaskSummary.endDatePlaceholder')"
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
class="!w-260px"
/>
</el-form-item>
<el-form-item :label="t('ProductionPlan.TaskSummary.scheduleFormDeliveryDateLabel')">
<el-date-picker
v-model="searchForm.deliveryDate"
type="daterange"
value-format="YYYY-MM-DD HH:mm:ss"
:start-placeholder="t('ProductionPlan.TaskSummary.startDatePlaceholder')"
:end-placeholder="t('ProductionPlan.TaskSummary.endDatePlaceholder')"
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
class="!w-260px"
/>
</el-form-item>
<el-form-item :label="t('ProductionPlan.TaskSummary.scheduleFormRemarkLabel')">
<el-input v-model="searchForm.remark" :placeholder="t('ProductionPlan.TaskSummary.scheduleFormRemarkPlaceholder')" clearable class="!w-220px" />
</el-form-item>
<el-form-item :label="t('ProductionPlan.TaskSummary.scheduleFormInventoryTaskLabel')">
<div class="flex items-center gap-8px">
<el-switch v-model="searchForm.inventoryTaskSchedule" />
</div>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleSearch">
<Icon icon="ep:search" class="mr-5px" /> {{ t('ProductionPlan.TaskSummary.scheduleFormSearchButton') }}
</el-button>
<el-button @click="handleReset">
<Icon icon="ep:refresh" class="mr-5px" /> {{ t('ProductionPlan.TaskSummary.scheduleFormResetButton') }}
</el-button>
</el-form-item>
</el-form>
<el-table
ref="taskTableRef"
v-loading="taskTableBusy"
:data="taskList"
border
:stripe="true"
:show-overflow-tooltip="true"
highlight-current-row
class="mt-12px"
row-key="id"
@current-change="handleCurrentTaskChange"
@select="handleTaskSelect"
@select-all="handleTaskSelectAll"
>
<el-table-column type="selection" width="55" :reserve-selection="true" align="center" :selectable="isTaskRowSelectable" />
<el-table-column :label="t('ProductionPlan.TaskSummary.tableTaskCodeColumn')" align="center" prop="code" width="200px" sortable />
<el-table-column :label="t('ProductionPlan.TaskSummary.tableOrderDateColumn')" align="center" prop="orderDate" :formatter="dateFormatter2" sortable />
<el-table-column :label="t('ProductionPlan.TaskSummary.tableDeliveryDateColumn')" align="center" prop="deliveryDate" :formatter="dateFormatter2" sortable />
<el-table-column :label="t('ProductionPlan.Task.tableTaskTypeColumn')" align="center" prop="taskType" sortable>
<template #default="scope">
<dict-tag :type="DICT_TYPE.MES_TASK_TYPE" :value="scope.row.taskType" />
</template>
</el-table-column>
<el-table-column :label="t('ProductionPlan.TaskSummary.tableStatusColumn')" align="center" prop="status" sortable>
<template #default="scope">
<dict-tag :type="DICT_TYPE.MES_TASK_STATUS" :value="scope.row.status" />
</template>
</el-table-column>
<el-table-column :label="t('ProductionPlan.TaskSummary.tableScheduleCompletedColumn')" align="center">
<template #default="scope">
<el-tag :type="scope.row.isScheduled ? 'success' : 'info'">{{ scope.row.isScheduled ? t('ProductionPlan.TaskSummary.scheduleYesLabel') : t('ProductionPlan.TaskSummary.scheduleNoLabel') }}</el-tag>
</template>
</el-table-column>
<el-table-column :label="t('ProductionPlan.Task.tableIsUrgentColumn')" align="center" prop="isUrgent" sortable>
<template #default="scope">
<el-tag :type="String(scope.row.isUrgent) === '1' ? 'danger' : 'info'">
{{ String(scope.row.isUrgent) === '1' ? t('ProductionPlan.Task.urgentYesLabel') : t('ProductionPlan.Task.urgentNoLabel') }}
</el-tag>
</template>
</el-table-column>
<el-table-column :label="t('ProductionPlan.TaskSummary.tableRemarkColumn')" align="center" prop="remark" />
<el-table-column :label="t('ProductionPlan.TaskSummary.tableOperateColumn')" align="center" min-width="100px">
<template #default="scope">
<el-button link type="info" @click="openTaskItemNeed(scope.row)">
{{ t('ProductionPlan.TaskSummary.actionMaterialLabel') }}
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<Pagination
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="loadTaskList"
/>
<div class="text-18px font-bold mt-20px mb-12px">{{ t('ProductionPlan.TaskSummary.detailTabSummaryLabel') }}</div>
<el-table
ref="detailTableRef"
v-loading="detailLoading"
:data="detailList"
border
:stripe="true"
:show-overflow-tooltip="true"
row-key="id"
@select="handleDetailSelect"
@select-all="handleDetailSelectAll"
>
<el-table-column type="selection" width="55" :reserve-selection="true" align="center" />
<el-table-column :label="t('ProductionPlan.TaskSummary.detailTableTaskCodeColumn')" align="center" prop="taskCode" sortable width="200px"/>
<el-table-column :label="t('ProductionPlan.TaskSummary.detailTableProductCodeColumn')" align="center" prop="barCode" sortable />
<el-table-column :label="t('ProductionPlan.TaskSummary.detailTableProductNameColumn')" align="center" prop="productName" sortable />
<el-table-column :label="t('ProductionPlan.TaskSummary.detailTableDeviceNameColumn')" align="center" prop="deviceDisplayName" />
<el-table-column :label="t('ProductionPlan.TaskSummary.detailTableTotalNumberColumn')" align="center" prop="number" width="120px"/>
<el-table-column :label="t('ProductionPlan.TaskSummary.detailTablePlanNumberColumn')" align="center" prop="planNumber" width="120px"/>
<el-table-column :label="t('ProductionPlan.TaskSummary.detailTableUnplanNumberColumn')" align="center" width="120px">
<template #default="scope">
<span class="el-alert--warning" style="color: #e66126">
{{ scope.row.number - scope.row.planNumber > 0 ? scope.row.number - scope.row.planNumber : 0 }}
</span>
</template>
</el-table-column>
<el-table-column :label="t('ProductionPlan.TaskSummary.detailTableOperateColumn')" align="center" width="180px">
<template #default="scope">
<el-button link type="info" @click="openProductItemNeed(scope.row)">
{{ t('ProductionPlan.TaskSummary.detailActionMaterialLabel') }}
</el-button>
<el-button link type="primary" @click="openDeviceRelationDialog(scope.row)">
{{ t('ProductionPlan.TaskSummary.relateDeviceButton') }}
</el-button>
</template>
</el-table-column>
</el-table>
</div>
</Dialog>
<TaskSchedulePreviewDialog
v-model="previewVisible"
:schedule-list="previewScheduleList"
@saved="handlePreviewSaved"
/>
<Dialog v-model="deviceRelationDialogVisible" :title="t('ProductionPlan.TaskSummary.deviceRelationDialogTitle')" width="520px">
<el-form :model="deviceRelationForm" label-width="90px" v-loading="deviceRelationLoading">
<el-form-item :label="t('ProductionPlan.TaskSummary.deviceRelationLabel')">
<el-input
:model-value="deviceRelationDisplayText"
:placeholder="t('ProductionPlan.TaskSummary.deviceRelationPlaceholder')"
readonly
@click="openDeviceSelectDialog"
/>
</el-form-item>
</el-form>
<template #footer>
<el-button type="primary" :loading="deviceRelationSaving" @click="submitDeviceRelation">{{ t('ProductionPlan.TaskSummary.deviceRelationSaveButton') }}</el-button>
<el-button @click="deviceRelationDialogVisible = false">{{ t('ProductionPlan.TaskSummary.deviceRelationCancelButton') }}</el-button>
</template>
</Dialog>
<TableSelectDialog
ref="deviceSelectDialogRef"
:title="t('ProductionPlan.TaskSummary.selectDeviceDialogTitle')"
:columns="deviceColumns"
:fetch-api="fetchDeviceLedgerPage"
row-key="id"
@confirm="handleDeviceSelectConfirm"
/>
<ItemNeedIndex ref="itemNeedRef" />
</template>
<script setup lang="ts">
import { DICT_TYPE } from '@/utils/dict'
import { TaskApi } from '@/api/mes/task'
import { ProductApi } from '@/api/erp/product/product'
import { DeviceLedgerApi } from '@/api/mes/deviceledger'
import TableSelectDialog from '@/components/TableSelectDialog/TableSelectDialog.vue'
import ItemNeedIndex from '@/views/mes/bom/ItemNeedIndex.vue'
import { dateFormatter2 } from '@/utils/formatTime'
import TaskSchedulePreviewDialog from './TaskSchedulePreviewDialog.vue'
defineOptions({ name: 'TaskScheduleDialog' })
const message = useMessage()
const { t } = useI18n() //
const emit = defineEmits(['success'])
const dialogVisible = ref(false)
const taskLoading = ref(false)
const detailLoading = ref(false)
const taskSelectionLoading = ref(false)
const submitLoading = ref(false)
const previewVisible = ref(false)
const itemNeedRef = ref()
const taskTableRef = ref()
const detailTableRef = ref()
const previewScheduleList = ref<any[]>([])
const deviceSelectDialogRef = ref()
const deviceRelationDialogVisible = ref(false)
const deviceRelationLoading = ref(false)
const deviceRelationSaving = ref(false)
const currentDeviceRelationRow = ref<any>()
const currentProductData = ref<any>()
const selectedDeviceRows = ref<any[]>([])
const deviceRelationForm = reactive({
productId: undefined as number | undefined,
devices: [] as { id: number; name: string }[]
})
const deviceColumns = computed(() => [
{ label: t('ProductionPlan.TaskSummary.deviceColumnCode'), prop: 'deviceCode', minWidth: 140 },
{ label: t('ProductionPlan.TaskSummary.deviceColumnName'), prop: 'deviceName', minWidth: 160 },
{ label: t('ProductionPlan.TaskSummary.deviceColumnModel'), prop: 'deviceModel', minWidth: 140 },
{ label: t('ProductionPlan.TaskSummary.deviceColumnWorkshop'), prop: 'workshop', minWidth: 140 }
])
const deviceRelationDisplayText = computed(() => {
if (!deviceRelationForm.devices.length) return ''
if (!selectedDeviceRows.value.length) return deviceRelationForm.devices.map((item) => item.name).join('、')
return selectedDeviceRows.value.map((item) => item.deviceName || item.name || item.code || `ID:${item.id}`).join('、')
})
const scheduleRuleOptions = computed(() => [
{ label: t('ProductionPlan.TaskSummary.scheduleRulePriority'), value: 2 },
{ label: t('ProductionPlan.TaskSummary.scheduleRuleCategory'), value: 3 },
{ label: t('ProductionPlan.TaskSummary.scheduleRuleDelivery'), value: 4 }
])
const capacityTypeOptions = computed(() => [
{ label: t('ProductionPlan.TaskSummary.capacityTypeRated'), value: 1 },
{ label: t('ProductionPlan.TaskSummary.capacityTypeDailyAvg'), value: 2 },
{ label: t('ProductionPlan.TaskSummary.capacityTypeDataCollection'), value: 3 }
])
const searchForm = reactive({
inventoryTaskSchedule: false,
skipHoliday: false,
sortRule: 2 as number | undefined,
capacityType: 1 as number | undefined,
workTimeRange: ['08:00', '18:00'] as string[],
code: '',
orderDate: [] as string[],
deliveryDate: [] as string[],
remark: ''
})
const total = ref(0)
const queryParams = reactive({
pageNo: 1,
pageSize: 10
})
const taskList = ref<any[]>([])
const detailList = ref<any[]>([])
const currentTask = ref<any>()
const allDetailsMap = ref<Record<number, any[]>>({}) // store details per taskId
const taskDetailRequestMap = new Map<number, Promise<any[]>>()
const currentDetailRequestId = ref(0)
const taskTableBusy = computed(() => taskLoading.value || taskSelectionLoading.value)
const getRelationName = (item: Record<string, any>, nameKeys: string[], fallbackPrefix: string, id: number) => {
for (const key of nameKeys) {
const value = item[key]
if (value !== undefined && value !== null && String(value).trim() !== '') {
return String(value)
}
}
return `${fallbackPrefix}ID:${id}`
}
const normalizeRelationList = (
value: unknown,
fallbackRows: any[] | undefined,
nameKeys: string[],
fallbackPrefix: string
): { id: number; name: string }[] => {
const fallbackMap = new Map<number, string>()
;(fallbackRows || []).forEach((row) => {
const id = Number(row?.id)
if (!Number.isFinite(id)) return
fallbackMap.set(id, getRelationName(row, nameKeys, fallbackPrefix, id))
})
const parseArray = (source: unknown): any[] => {
if (Array.isArray(source)) return source
if (typeof source === 'string') {
const content = source.trim()
if (!content) return []
try {
const parsed = JSON.parse(content.startsWith('[') ? content : `[${content}]`)
return Array.isArray(parsed) ? parsed : []
} catch {
return content.split(',').map((item) => item.trim()).filter(Boolean)
}
}
return []
}
return parseArray(value)
.map((item) => {
if (typeof item === 'object' && item !== null) {
const id = Number((item as any).id)
if (!Number.isFinite(id)) return undefined
return {
id,
name: getRelationName(item as any, nameKeys, fallbackPrefix, id)
}
}
const id = Number(item)
if (!Number.isFinite(id)) return undefined
return {
id,
name: fallbackMap.get(id) || `${fallbackPrefix}ID:${id}`
}
})
.filter((item): item is { id: number; name: string } => Boolean(item))
}
const toDeviceRows = (devices: { id: number; name: string }[]) => {
return devices.map((item) => ({
id: item.id,
deviceName: item.name,
name: item.name
}))
}
const buildRelationIdList = (list: { id: number; name: string }[]) => {
return list.map((item) => Number(item.id)).filter((id) => Number.isFinite(id))
}
const fetchDeviceLedgerPage = (params: Record<string, any>) => {
return DeviceLedgerApi.getDeviceLedgerPage({
...params,
isScheduled: 1
})
}
const loadTaskList = async () => {
taskLoading.value = true
try {
const params: any = {
isScheduled: 0,
pageNo: queryParams.pageNo,
pageSize: queryParams.pageSize,
code: searchForm.code || undefined,
orderDate: searchForm.orderDate,
deliveryDate: searchForm.deliveryDate,
remark: searchForm.remark || undefined,
taskType: searchForm.inventoryTaskSchedule ? t('ProductionPlan.TaskSummary.inventoryTaskType') : undefined
}
const data = await TaskApi.getPlanTaskPage(params)
taskList.value = data?.list ?? []
total.value = data?.total ?? 0
if (taskList.value.length) {
await handleCurrentTaskChange(taskList.value[0])
} else {
currentTask.value = undefined
detailList.value = []
}
} finally {
taskLoading.value = false
}
}
const getTaskDetailList = async (taskRow?: any) => {
const taskId = taskRow?.id
if (!taskId) return []
if (allDetailsMap.value[taskId]) {
return allDetailsMap.value[taskId]
}
const pendingRequest = taskDetailRequestMap.get(taskId)
if (pendingRequest) {
return pendingRequest
}
const requestPromise = TaskApi.getTaskDetailPage({
pageNo: 1,
pageSize: 10,
taskId
})
.then((data: any) => {
const list = (data?.list ?? []).map((item: any) => ({
...item,
_parentTaskOrderPriority: taskRow?.isUrgent,
_parentTaskDeliveryDate: taskRow?.deliveryDate,
deviceDisplayName: item.deviceDisplayName || item.deviceName || item.feedingPipelineName || ''
}))
allDetailsMap.value[taskId] = list
return list
})
.finally(() => {
taskDetailRequestMap.delete(taskId)
})
taskDetailRequestMap.set(taskId, requestPromise)
return requestPromise
}
const handleCurrentTaskChange = async (row: any) => {
if (!row) return
currentTask.value = row
const requestId = ++currentDetailRequestId.value
detailLoading.value = true
try {
const list = await getTaskDetailList(row)
if (requestId !== currentDetailRequestId.value || currentTask.value?.id !== row.id) return
detailList.value = list
nextTick(() => {
const parentIsSelected = taskTableRef.value?.getSelectionRows()?.some((t: any) => t.id === row.id)
if (parentIsSelected) {
detailList.value.forEach(child => {
detailTableRef.value?.toggleRowSelection(child, true)
})
}
})
} finally {
if (requestId === currentDetailRequestId.value) {
detailLoading.value = false
}
}
}
const handleTaskSelect = async (selection: any[], row: any) => {
if (taskSelectionLoading.value) return
taskSelectionLoading.value = true
try {
const isSelected = selection.some((item) => item.id === row.id)
if (isSelected) {
await handleCurrentTaskChange(row)
const childList = allDetailsMap.value[row.id] || []
if (childList.length === 0) {
message.warning(t('ProductionPlan.TaskSummary.taskNoDetailWarning', { code: row.code }))
taskTableRef.value?.toggleRowSelection(row, false)
return
}
detailList.value.forEach((child) => {
detailTableRef.value?.toggleRowSelection(child, true)
})
return
}
const childList = allDetailsMap.value[row.id] || []
if (currentTask.value?.id === row.id) {
childList.forEach((child) => {
const rowInView = detailList.value.find(d => d.id === child.id)
if (rowInView) {
detailTableRef.value?.toggleRowSelection(rowInView, false)
}
})
}
} finally {
taskSelectionLoading.value = false
}
}
const handleTaskSelectAll = async (selection: any[]) => {
if (taskSelectionLoading.value) return
taskSelectionLoading.value = true
try {
const isAllSelected = selection.length > 0
if (isAllSelected) {
let hasEmptyChild = false
for (const row of selection) {
const childList = await getTaskDetailList(row)
if (childList.length === 0) {
hasEmptyChild = true
taskTableRef.value?.toggleRowSelection(row, false)
} else {
childList.forEach((child) => {
const rowInView = detailList.value.find(d => d.id === child.id)
if (rowInView) {
detailTableRef.value?.toggleRowSelection(rowInView, true)
}
})
}
}
if (hasEmptyChild) {
message.warning(t('ProductionPlan.TaskSummary.partialTaskNoDetailWarning'))
}
} else {
detailTableRef.value?.clearSelection()
}
} finally {
taskSelectionLoading.value = false
}
}
const handleDetailSelect = (selection: any[], row: any) => {
const taskId = row.taskId
const childList = allDetailsMap.value[taskId] || []
// check if all children for this task are selected
const allChildrenSelected = childList.every(child => selection.some(sel => sel.id === child.id))
const noChildrenSelected = childList.every(child => !selection.some(sel => sel.id === child.id))
const parentTask = taskList.value.find(task => task.id === taskId)
if (parentTask) {
if (allChildrenSelected) {
taskTableRef.value?.toggleRowSelection(parentTask, true)
} else if (noChildrenSelected) {
taskTableRef.value?.toggleRowSelection(parentTask, false)
}
}
}
const handleDetailSelectAll = (selection: any[]) => {
if (!currentTask.value?.id) return
const taskId = currentTask.value.id
const childList = allDetailsMap.value[taskId] || []
const parentTask = taskList.value.find(task => task.id === taskId)
if (parentTask) {
// if any child in current view is selected, consider it an intent to select parent (if not all were selected)
// selection will contain all selected items across pages, so we need to filter for current view's task
const currentViewSelection = selection.filter(sel => sel.taskId === taskId)
if (currentViewSelection.length === childList.length && childList.length > 0) {
taskTableRef.value?.toggleRowSelection(parentTask, true)
} else if (currentViewSelection.length === 0) {
taskTableRef.value?.toggleRowSelection(parentTask, false)
}
}
}
const isTaskRowSelectable = () => !taskTableBusy.value && !detailLoading.value
const handleSearch = async () => {
queryParams.pageNo = 1
await loadTaskList()
}
const handleReset = async () => {
searchForm.inventoryTaskSchedule = false
searchForm.skipHoliday = false
searchForm.sortRule = undefined
searchForm.capacityType = 1
searchForm.workTimeRange = ['08:00', '18:00']
searchForm.code = ''
searchForm.orderDate = []
searchForm.deliveryDate = []
searchForm.remark = ''
queryParams.pageNo = 1
await loadTaskList()
}
const openTaskItemNeed = (row: any) => {
if (!row?.id) return
itemNeedRef.value.open('task', t('ProductionPlan.TaskSummary.taskItemNeedPrefix') + (row?.code ?? ''), row.id)
}
const openProductItemNeed = (row: any) => {
if (!row?.productId) return
const number = row.number - row.planNumber > 0 ? row.number - row.planNumber : 0
itemNeedRef.value.open('product', row.productName, row.productId, number)
}
const openDeviceRelationDialog = async (row: any) => {
if (!row?.productId) {
message.warning(t('ProductionPlan.TaskSummary.detailNoProductWarning'))
return
}
currentDeviceRelationRow.value = row
deviceRelationDialogVisible.value = true
deviceRelationLoading.value = true
selectedDeviceRows.value = []
try {
const productData = await ProductApi.getProduct(Number(row.productId))
currentProductData.value = productData
const devices = normalizeRelationList(
(productData as any).devices ?? (productData as any).deviceIds,
(productData as any).deviceList,
['name', 'deviceName', 'code'],
t('ProductionPlan.TaskSummary.deviceColumnName')
)
deviceRelationForm.productId = Number(row.productId)
deviceRelationForm.devices = devices
selectedDeviceRows.value = toDeviceRows(devices)
} finally {
deviceRelationLoading.value = false
}
}
const openDeviceSelectDialog = () => {
const rows = selectedDeviceRows.value.length
? selectedDeviceRows.value.map((item) => ({ ...item, id: Number(item.id) }))
: toDeviceRows(deviceRelationForm.devices)
deviceSelectDialogRef.value?.open(rows)
}
const handleDeviceSelectConfirm = (payload: { ids: (number | string)[]; rows: any[] }) => {
deviceRelationForm.devices = payload.rows
.map((item) => {
const id = Number(item.id)
if (!Number.isFinite(id)) return undefined
return {
id,
name: item.deviceName || item.name || item.code || `${t('ProductionPlan.TaskSummary.deviceColumnName')}ID:${id}`
}
})
.filter((item): item is { id: number; name: string } => Boolean(item))
selectedDeviceRows.value = payload.rows
}
const refreshDetailListAfterDeviceSaved = async () => {
const taskId = Number(currentDeviceRelationRow.value?.taskId)
if (!Number.isFinite(taskId)) return
delete allDetailsMap.value[taskId]
if (Number(currentTask.value?.id) === taskId && currentTask.value) {
await handleCurrentTaskChange(currentTask.value)
}
}
const submitDeviceRelation = async () => {
if (!currentProductData.value || !deviceRelationForm.productId) return
deviceRelationSaving.value = true
try {
const currentData = currentProductData.value as any
const moldIds = normalizeRelationList(
currentData.molds ?? currentData.moldIds,
currentData.moldList,
['name', 'code'],
t('ProductionPlan.TaskSummary.deviceColumnName')
)
const payload: any = {
...currentData,
deviceIds: buildRelationIdList(deviceRelationForm.devices),
moldIds: buildRelationIdList(moldIds)
}
delete payload.devices
delete payload.molds
await ProductApi.updateProduct(payload)
await refreshDetailListAfterDeviceSaved()
message.success(t('ProductionPlan.TaskSummary.deviceRelationSaved'))
deviceRelationDialogVisible.value = false
} finally {
deviceRelationSaving.value = false
}
}
const handlePreviewSaved = async () => {
dialogVisible.value = false
await loadTaskList()
emit('success')
}
const hasValue = (value: unknown) => value !== null && value !== undefined && value !== ''
const handleSubmit = async () => {
if (searchForm.sortRule === undefined) {
message.warning(t('ProductionPlan.TaskSummary.selectScheduleRuleWarning'))
return
}
if (searchForm.capacityType === undefined) {
message.warning(t('ProductionPlan.TaskSummary.selectCapacitySourceWarning'))
return
}
const [workStartTime, workEndTime] = Array.isArray(searchForm.workTimeRange) ? searchForm.workTimeRange : []
if (!workStartTime || !workEndTime) {
message.warning(t('ProductionPlan.TaskSummary.selectWorkTimeWarning'))
return
}
const selectedRows = detailTableRef.value?.getSelectionRows() || []
if (selectedRows.length === 0) {
message.warning(t('ProductionPlan.TaskSummary.selectDetailWarning'))
return
}
submitLoading.value = true
try {
console.log(selectedRows)
const createReqVO = selectedRows.map((row: any) => {
const planNumber = row.number - row.planNumber > 0 ? row.number - row.planNumber : 0
return {
// PlanForm fields
productId: row.productId,
taskId: row.taskId,
taskDetailId: row.id,
planNumber: planNumber,
finishNumber: 0,
isPreProduction: 0,
isCode: true,
planStartTime: new Date().getTime(),
planEndTime: new Date().getTime(),
productCode: row.barCode,
// Image fields
orderPriority: row.isUrgent || row._parentTaskOrderPriority,
workOrderCode: row.taskCode,
deliveryDate: row._parentTaskDeliveryDate,
orderDetailDeliveryDate: hasValue(row.finishDate) ? row.finishDate : row._parentTaskDeliveryDate,
orderDetailId: row.id
}
})
const scheduleResult = await TaskApi.oneClickSchedule({
createReqVO,
sortRule: searchForm.sortRule,
capacityType: searchForm.capacityType,
workStartTime,
workEndTime,
skipHoliday: searchForm.skipHoliday
})
const scheduleData = Array.isArray(scheduleResult)
? scheduleResult
: Array.isArray((scheduleResult as any)?.data)
? (scheduleResult as any).data
: []
previewScheduleList.value = scheduleData
message.success(t('ProductionPlan.TaskSummary.scheduleSubmitted'))
previewVisible.value = true
emit('success')
} finally {
submitLoading.value = false
}
}
const open = async () => {
dialogVisible.value = true
await loadTaskList()
}
defineExpose({ open })
</script>