|
|
|
@ -134,6 +134,7 @@
|
|
|
|
<el-table-column :label="t('ProductionPlan.TaskSummary.detailTableTaskCodeColumn')" align="center" prop="taskCode" sortable />
|
|
|
|
<el-table-column :label="t('ProductionPlan.TaskSummary.detailTableTaskCodeColumn')" align="center" prop="taskCode" sortable />
|
|
|
|
<el-table-column :label="t('ProductionPlan.TaskSummary.detailTableProductCodeColumn')" align="center" prop="barCode" sortable />
|
|
|
|
<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.detailTableProductNameColumn')" align="center" prop="productName" sortable />
|
|
|
|
|
|
|
|
<el-table-column label="设备名称" align="center" prop="deviceDisplayName" />
|
|
|
|
<el-table-column :label="t('ProductionPlan.TaskSummary.detailTableTotalNumberColumn')" align="center" prop="number" />
|
|
|
|
<el-table-column :label="t('ProductionPlan.TaskSummary.detailTableTotalNumberColumn')" align="center" prop="number" />
|
|
|
|
<el-table-column :label="t('ProductionPlan.TaskSummary.detailTablePlanNumberColumn')" align="center" prop="planNumber" />
|
|
|
|
<el-table-column :label="t('ProductionPlan.TaskSummary.detailTablePlanNumberColumn')" align="center" prop="planNumber" />
|
|
|
|
<el-table-column :label="t('ProductionPlan.TaskSummary.detailTableUnplanNumberColumn')" align="center">
|
|
|
|
<el-table-column :label="t('ProductionPlan.TaskSummary.detailTableUnplanNumberColumn')" align="center">
|
|
|
|
@ -143,11 +144,14 @@
|
|
|
|
</span>
|
|
|
|
</span>
|
|
|
|
</template>
|
|
|
|
</template>
|
|
|
|
</el-table-column>
|
|
|
|
</el-table-column>
|
|
|
|
<el-table-column :label="t('ProductionPlan.TaskSummary.detailTableOperateColumn')" align="center" width="100px">
|
|
|
|
<el-table-column :label="t('ProductionPlan.TaskSummary.detailTableOperateColumn')" align="center" width="180px">
|
|
|
|
<template #default="scope">
|
|
|
|
<template #default="scope">
|
|
|
|
<el-button link type="info" @click="openProductItemNeed(scope.row)">
|
|
|
|
<el-button link type="info" @click="openProductItemNeed(scope.row)">
|
|
|
|
{{ t('ProductionPlan.TaskSummary.detailActionMaterialLabel') }}
|
|
|
|
{{ t('ProductionPlan.TaskSummary.detailActionMaterialLabel') }}
|
|
|
|
</el-button>
|
|
|
|
</el-button>
|
|
|
|
|
|
|
|
<el-button link type="primary" @click="openDeviceRelationDialog(scope.row)">
|
|
|
|
|
|
|
|
关联设备
|
|
|
|
|
|
|
|
</el-button>
|
|
|
|
</template>
|
|
|
|
</template>
|
|
|
|
</el-table-column>
|
|
|
|
</el-table-column>
|
|
|
|
</el-table>
|
|
|
|
</el-table>
|
|
|
|
@ -165,12 +169,41 @@
|
|
|
|
@saved="handlePreviewSaved"
|
|
|
|
@saved="handlePreviewSaved"
|
|
|
|
/>
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<Dialog v-model="deviceRelationDialogVisible" title="关联设备" width="520px">
|
|
|
|
|
|
|
|
<el-form :model="deviceRelationForm" label-width="90px" v-loading="deviceRelationLoading">
|
|
|
|
|
|
|
|
<el-form-item label="关联设备">
|
|
|
|
|
|
|
|
<el-input
|
|
|
|
|
|
|
|
:model-value="deviceRelationDisplayText"
|
|
|
|
|
|
|
|
placeholder="点击选择设备"
|
|
|
|
|
|
|
|
readonly
|
|
|
|
|
|
|
|
@click="openDeviceSelectDialog"
|
|
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
|
|
</el-form>
|
|
|
|
|
|
|
|
<template #footer>
|
|
|
|
|
|
|
|
<el-button type="primary" :loading="deviceRelationSaving" @click="submitDeviceRelation">保存</el-button>
|
|
|
|
|
|
|
|
<el-button @click="deviceRelationDialogVisible = false">取消</el-button>
|
|
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
</Dialog>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<TableSelectDialog
|
|
|
|
|
|
|
|
ref="deviceSelectDialogRef"
|
|
|
|
|
|
|
|
title="选择设备"
|
|
|
|
|
|
|
|
:columns="deviceColumns"
|
|
|
|
|
|
|
|
:fetch-api="fetchDeviceLedgerPage"
|
|
|
|
|
|
|
|
row-key="id"
|
|
|
|
|
|
|
|
@confirm="handleDeviceSelectConfirm"
|
|
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
|
|
<ItemNeedIndex ref="itemNeedRef" />
|
|
|
|
<ItemNeedIndex ref="itemNeedRef" />
|
|
|
|
</template>
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
|
<script setup lang="ts">
|
|
|
|
import { DICT_TYPE } from '@/utils/dict'
|
|
|
|
import { DICT_TYPE } from '@/utils/dict'
|
|
|
|
import { TaskApi } from '@/api/mes/task'
|
|
|
|
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 ItemNeedIndex from '@/views/mes/bom/ItemNeedIndex.vue'
|
|
|
|
import { dateFormatter2 } from '@/utils/formatTime'
|
|
|
|
import { dateFormatter2 } from '@/utils/formatTime'
|
|
|
|
import TaskSchedulePreviewDialog from './TaskSchedulePreviewDialog.vue'
|
|
|
|
import TaskSchedulePreviewDialog from './TaskSchedulePreviewDialog.vue'
|
|
|
|
@ -191,6 +224,29 @@ const itemNeedRef = ref()
|
|
|
|
const taskTableRef = ref()
|
|
|
|
const taskTableRef = ref()
|
|
|
|
const detailTableRef = ref()
|
|
|
|
const detailTableRef = ref()
|
|
|
|
const previewScheduleList = ref<any[]>([])
|
|
|
|
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 = [
|
|
|
|
|
|
|
|
{ label: '设备编号', prop: 'deviceCode', minWidth: 140 },
|
|
|
|
|
|
|
|
{ label: '设备名称', prop: 'deviceName', minWidth: 160 },
|
|
|
|
|
|
|
|
{ label: '设备型号', prop: 'deviceModel', minWidth: 140 },
|
|
|
|
|
|
|
|
{ label: '所属车间', 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 = [
|
|
|
|
const scheduleRuleOptions = [
|
|
|
|
{ label: '订单优先级', value: 2 },
|
|
|
|
{ label: '订单优先级', value: 2 },
|
|
|
|
@ -227,6 +283,77 @@ const taskDetailRequestMap = new Map<number, Promise<any[]>>()
|
|
|
|
const currentDetailRequestId = ref(0)
|
|
|
|
const currentDetailRequestId = ref(0)
|
|
|
|
const taskTableBusy = computed(() => taskLoading.value || taskSelectionLoading.value)
|
|
|
|
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 () => {
|
|
|
|
const loadTaskList = async () => {
|
|
|
|
taskLoading.value = true
|
|
|
|
taskLoading.value = true
|
|
|
|
try {
|
|
|
|
try {
|
|
|
|
@ -276,7 +403,8 @@ const getTaskDetailList = async (taskRow?: any) => {
|
|
|
|
const list = (data?.list ?? []).map((item: any) => ({
|
|
|
|
const list = (data?.list ?? []).map((item: any) => ({
|
|
|
|
...item,
|
|
|
|
...item,
|
|
|
|
_parentTaskOrderPriority: taskRow?.isUrgent,
|
|
|
|
_parentTaskOrderPriority: taskRow?.isUrgent,
|
|
|
|
_parentTaskDeliveryDate: taskRow?.deliveryDate
|
|
|
|
_parentTaskDeliveryDate: taskRow?.deliveryDate,
|
|
|
|
|
|
|
|
deviceDisplayName: item.deviceDisplayName || item.deviceName || item.feedingPipelineName || ''
|
|
|
|
}))
|
|
|
|
}))
|
|
|
|
allDetailsMap.value[taskId] = list
|
|
|
|
allDetailsMap.value[taskId] = list
|
|
|
|
return list
|
|
|
|
return list
|
|
|
|
@ -445,6 +573,90 @@ const openProductItemNeed = (row: any) => {
|
|
|
|
const number = row.number - row.planNumber > 0 ? row.number - row.planNumber : 0
|
|
|
|
const number = row.number - row.planNumber > 0 ? row.number - row.planNumber : 0
|
|
|
|
itemNeedRef.value.open('product', row.productName, row.productId, number)
|
|
|
|
itemNeedRef.value.open('product', row.productName, row.productId, number)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const openDeviceRelationDialog = async (row: any) => {
|
|
|
|
|
|
|
|
if (!row?.productId) {
|
|
|
|
|
|
|
|
message.warning('当前明细没有关联产品,无法设置设备')
|
|
|
|
|
|
|
|
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'],
|
|
|
|
|
|
|
|
'设备'
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
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 || `设备ID:${id}`
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
.filter((item): item is { id: number; name: string } => Boolean(item))
|
|
|
|
|
|
|
|
selectedDeviceRows.value = payload.rows
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
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'],
|
|
|
|
|
|
|
|
'模具'
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
const payload: any = {
|
|
|
|
|
|
|
|
...currentData,
|
|
|
|
|
|
|
|
deviceIds: buildRelationIdList(deviceRelationForm.devices),
|
|
|
|
|
|
|
|
moldIds: buildRelationIdList(moldIds)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
delete payload.devices
|
|
|
|
|
|
|
|
delete payload.molds
|
|
|
|
|
|
|
|
await ProductApi.updateProduct(payload)
|
|
|
|
|
|
|
|
const displayText = deviceRelationForm.devices.map((item) => item.name).join('、')
|
|
|
|
|
|
|
|
const rowId = currentDeviceRelationRow.value?.id
|
|
|
|
|
|
|
|
if (rowId !== undefined) {
|
|
|
|
|
|
|
|
Object.values(allDetailsMap.value).forEach((rows) => {
|
|
|
|
|
|
|
|
rows.forEach((item: any) => {
|
|
|
|
|
|
|
|
if (item.id === rowId) {
|
|
|
|
|
|
|
|
item.deviceDisplayName = displayText
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
const target = detailList.value.find((item: any) => item.id === rowId)
|
|
|
|
|
|
|
|
if (target) {
|
|
|
|
|
|
|
|
target.deviceDisplayName = displayText
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
message.success('关联设备已保存')
|
|
|
|
|
|
|
|
deviceRelationDialogVisible.value = false
|
|
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
|
|
deviceRelationSaving.value = false
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const handlePreviewSaved = async () => {
|
|
|
|
const handlePreviewSaved = async () => {
|
|
|
|
dialogVisible.value = false
|
|
|
|
dialogVisible.value = false
|
|
|
|
|