From 0c7e1a7bce0911d788f58cd7cbb61d84b559494f Mon Sep 17 00:00:00 2001 From: liutao <790864623@qq.com> Date: Thu, 28 May 2026 11:19:57 +0800 Subject: [PATCH 01/18] =?UTF-8?q?=E5=85=B3=E9=94=AE=E4=BB=B6=E5=8F=96?= =?UTF-8?q?=E6=B6=88=E6=95=B0=E9=87=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/mes/criticalComponent/CriticalComponentForm.vue | 4 ++-- src/views/mes/criticalComponent/index.vue | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/views/mes/criticalComponent/CriticalComponentForm.vue b/src/views/mes/criticalComponent/CriticalComponentForm.vue index 6bf29149..0de68532 100644 --- a/src/views/mes/criticalComponent/CriticalComponentForm.vue +++ b/src/views/mes/criticalComponent/CriticalComponentForm.vue @@ -44,14 +44,14 @@ type="textarea" /> - + From 74088e67d5dc68794b6ae2e29e7cef587906f528 Mon Sep 17 00:00:00 2001 From: liutao <790864623@qq.com> Date: Thu, 28 May 2026 11:34:05 +0800 Subject: [PATCH 02/18] =?UTF-8?q?=E5=85=B3=E9=94=AE=E4=BB=B6=E5=8F=96?= =?UTF-8?q?=E6=B6=88=E6=95=B0=E9=87=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/mes/deviceledger/DeviceLedgerForm.vue | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/views/mes/deviceledger/DeviceLedgerForm.vue b/src/views/mes/deviceledger/DeviceLedgerForm.vue index 424a5ca8..d40932bf 100644 --- a/src/views/mes/deviceledger/DeviceLedgerForm.vue +++ b/src/views/mes/deviceledger/DeviceLedgerForm.vue @@ -281,8 +281,8 @@ min-width="140" sortable /> - + Date: Thu, 28 May 2026 15:06:02 +0800 Subject: [PATCH 03/18] =?UTF-8?q?=E8=AE=BE=E5=A4=87=E5=8F=B0=E8=B4=A6?= =?UTF-8?q?=E7=BC=96=E8=BE=91=E9=A1=B5=E9=9D=A2=E8=B7=AF=E7=94=B1=E8=B7=B3?= =?UTF-8?q?=E8=BD=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mes/deviceledger/detail/editIndex.vue | 1001 +++++++++++++++++ src/views/mes/deviceledger/index.vue | 5 +- 2 files changed, 1005 insertions(+), 1 deletion(-) create mode 100644 src/views/mes/deviceledger/detail/editIndex.vue diff --git a/src/views/mes/deviceledger/detail/editIndex.vue b/src/views/mes/deviceledger/detail/editIndex.vue new file mode 100644 index 00000000..00093e5e --- /dev/null +++ b/src/views/mes/deviceledger/detail/editIndex.vue @@ -0,0 +1,1001 @@ + + + + + diff --git a/src/views/mes/deviceledger/index.vue b/src/views/mes/deviceledger/index.vue index 78df14de..08912b42 100644 --- a/src/views/mes/deviceledger/index.vue +++ b/src/views/mes/deviceledger/index.vue @@ -219,7 +219,7 @@ {{ t('EquipmentManagement.EquipmentLedger.detail') }} - {{ t('EquipmentManagement.EquipmentLedger.edit') }} @@ -712,6 +712,9 @@ const handleDetail = (id: number) => { router.push({ name: 'MesDeviceLedgerDetail', params: { id } }) } +const handleEditDetail = (id: number) => { + router.push({name: 'MesDeviceLedgerEditDetail', params: {id } }) +} /** 查询列表 */ const getList = async () => { loading.value = true From 5325880ecb8f30badd95af18d844e2fd9baceede Mon Sep 17 00:00:00 2001 From: liutao <790864623@qq.com> Date: Thu, 28 May 2026 17:18:14 +0800 Subject: [PATCH 04/18] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mes/deviceledger/detail/editIndex.vue | 1310 +++++++++-------- 1 file changed, 666 insertions(+), 644 deletions(-) diff --git a/src/views/mes/deviceledger/detail/editIndex.vue b/src/views/mes/deviceledger/detail/editIndex.vue index 00093e5e..30acd00b 100644 --- a/src/views/mes/deviceledger/detail/editIndex.vue +++ b/src/views/mes/deviceledger/detail/editIndex.vue @@ -9,7 +9,7 @@ :rules="formRules" label-width="110px" v-loading="formLoading || fileUploading" - element-loading-text="资料上传中,请稍候" + :element-loading-text="t('common.loading')" > @@ -109,7 +109,7 @@ - + - + + :placeholder="t('EquipmentManagement.EquipmentLedger.placeholderBeijianIds')" @clear="clearBeijian" @click="openBeijianDialog"/> @@ -138,11 +137,11 @@ @@ -178,9 +177,11 @@ {{ t('action.export') }} - + + @@ -191,6 +192,122 @@ + + + + + + + + + + + + + + + {{ t('common.query') }} + + + + {{ t('common.reset') }} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{ t('common.query') }} + + + + {{ t('common.reset') }} + + + + + + + + + + + + + + + + + + + + @@ -199,14 +316,15 @@ import { dateFormatter, formatDate } from '@/utils/formatTime' import download from '@/utils/download' import { DeviceLedgerApi, DeviceLedgerVO } from '@/api/mes/deviceledger' import { DeviceTypeApi, DeviceTypeTreeVO } from '@/api/mes/devicetype' -import { CriticalComponentApi } from '@/api/mes/criticalComponent' -import { TicketManagementApi } from '@/api/mes/ticketManagement' -import { DvRepairApi } from '@/api/mes/dvrepair' +import {CriticalComponentApi, CriticalComponentVO} from '@/api/mes/criticalComponent' +import { getSimpleUserList, UserVO } from '@/api/system/user' +import {ProductApi, ProductVO} from '@/api/erp/product/product' import QrcodeActionCard from '@/components/QrcodeActionCard/index.vue' import { useTagsViewStore } from '@/store/modules/tagsView' -import { downloadByUrl } from '@/utils/filt' -import {ref} from "vue"; - +import type { FormRules } from 'element-plus' +import { ref } from 'vue' +import {ElTable} from "element-plus"; +import { DICT_TYPE } from '@/utils/dict' defineOptions({ name: 'MesDeviceLedgerEditDetail' }) const { t } = useI18n() @@ -214,461 +332,606 @@ const message = useMessage() const route = useRoute() const { delView } = useTagsViewStore() const { currentRoute } = useRouter() - const deviceId = computed(() => Number(route.params.id)) const detailLoading = ref(false) const tableLoading = ref(false) const detailData = ref() const detailActiveTab = ref('criticalComponent') +const criticalComponentDialogVisible = ref(false) +const beijianDialogVisible = ref(false) +const criticalComponentDraft = ref([]) +const beijianDraft = ref([]) +const list = ref([]) +const bjList = ref([]) +const loading = ref(true) +const total = ref(0) +const bjTotal = ref(0) +const selectedIds = ref([]) +const bjSelectedIds = ref([]) +// 表格引用 +const multipleTableRef = ref>() +const bjMultipleTableRef = ref>() +const queryFormRef = ref() +const bjQueryFormRef = ref() +const parseIdsValue = (value: any): number[] => { + if (!value) return [] + if (Array.isArray(value)) return value.map((v) => Number(v)).filter((v) => !Number.isNaN(v)) + return String(value) + .split(',') + .map((v) => Number(v.trim())) + .filter((v) => !Number.isNaN(v)) +} +const selectedRows = ref([]) // 存储所有选中的行 +const bjSelectedRows = ref([]) // 存储所有选中的行 +const queryParams = reactive({ + pageNo: 1, + pageSize: 10, + code: undefined as string | undefined, + name: undefined as string | undefined, + description: undefined as string | undefined, + remark: undefined as string | undefined, + createTime: [] as string[] +}) -const deviceTypeNameMap = ref>({}) -const formData = ref({ +const bjQueryParams = reactive({ + pageNo: 1, + pageSize: 10, + name: undefined, + categoryId: undefined }) -const buildDeviceTypeNameMap = (nodes: DeviceTypeTreeVO[]) => { - const map: Record = {} - const stack = [...nodes] - while (stack.length) { - const node = stack.pop()! - if (typeof node.id === 'number') map[node.id] = node.name - if (Array.isArray(node.children) && node.children.length) stack.push(...node.children) - } - deviceTypeNameMap.value = map -} -const getDeviceTypeName = (value: any) => { - const id = typeof value === 'number' ? value : Number(value) - if (!Number.isNaN(id) && deviceTypeNameMap.value[id]) return deviceTypeNameMap.value[id] - return value ?? '' +const handleQuery = () => { + queryParams.pageNo = 1 + getList() +} +const resetQuery = () => { + queryFormRef.value.resetFields() + handleQuery() } -const detailTemplateJson = computed(() => { - const templateJson = detailData.value?.templateJson - if (!templateJson) return undefined - if (typeof templateJson === 'string') { - try { - return JSON.parse(templateJson) - } catch { - return undefined - } - } - return templateJson -}) - -type FileUrlItem = { - fileName: string - fileUrl: string +/** 配件搜索按钮操作 */ +const bjHandleQuery = () => { + bjQueryParams.pageNo = 1 + bjGetList() } -const getFileNameFromUrl = (url: string) => { - const cleanUrl = url.split('?')[0] - const fileName = cleanUrl.substring(cleanUrl.lastIndexOf('/') + 1) - return decodeURIComponent(fileName || url) +/** 配件重置按钮操作 */ +const bjResetQuery = () => { + bjQueryFormRef.value.resetFields() + bjHandleQuery() } -const parseFileUrlList = (value: any): FileUrlItem[] => { - if (!value) return [] - let rawList: any[] = [] - if (Array.isArray(value)) { - rawList = value - } else if (typeof value === 'string') { - const trimmed = value.trim() - if (!trimmed) return [] - try { - const parsed = JSON.parse(trimmed) - rawList = Array.isArray(parsed) ? parsed : [parsed] - } catch { - rawList = trimmed - .split(',') - .map((url) => ({ fileUrl: url.trim() })) - .filter((item) => item.fileUrl) - } - } else if (typeof value === 'object') { - rawList = [value] - } - return rawList - .map((item) => { - if (!item) return undefined - const fileUrl = typeof item === 'string' ? item : item.fileUrl - if (!fileUrl) return undefined - const normalizedUrl = String(fileUrl).trim() - if (!normalizedUrl) return undefined - const fileName = - typeof item === 'string' - ? getFileNameFromUrl(normalizedUrl) - : item.fileName || getFileNameFromUrl(normalizedUrl) - return { - fileName: String(fileName), - fileUrl: normalizedUrl - } - }) - .filter((item): item is FileUrlItem => Boolean(item)) +const handleSelectionChange = (rows: CriticalComponentVO[]) => { + selectedIds.value = rows.map((r) => r.id).filter((id): id is number => typeof id === 'number') + // 获取当前页所有行的 id + const currentPageIds = rows.map(item => item.id) + + // 从已选中的数组中移除当前页的数据 + selectedRows.value = selectedRows.value.filter( + item => !currentPageIds.includes(item.id) + ) + // 添加当前页新选中的数据 + selectedRows.value.push(...rows) } -const fileUrlList = computed(() => parseFileUrlList(detailData.value?.fileUrl)) +// 存储当前已选中的行 +const currentSelectedRows = ref([]) -const handleDownloadFile = (row: FileUrlItem) => { - downloadByUrl({ - url: row.fileUrl, - fileName: row.fileName - }) -} +// select 事件:row 是当前操作的行,selected 是操作后的状态 +const handleSelect = (selection, row) => { + // 判断是选中还是取消选中 + const isSelected = selection.includes(row) + if (isSelected) { + // console.log(`✅ 行被选中: ID=${row.id}, Name=${row.name}`) + ids.value.push(row.id) + } else { -const buildDetailPrintData = () => { - return { - id: detailData.value?.id, - deviceCode: detailData.value?.deviceCode, - deviceName: detailData.value?.deviceName, - deviceSpec: detailData.value?.deviceSpec, - deviceBrand: detailData.value?.deviceBrand, - deviceModel: detailData.value?.deviceModel, - deviceLocation: detailData.value?.deviceLocation, - remark: detailData.value?.remark ?? detailData.value?.deviceRemark, - qrcodeUrl: detailData.value?.qrcodeUrl + ids.value = ids.value.filter( + item => item !== row.id + ) + // console.log(`❌ 行被取消选中: ID=${row.id}, Name=${row.name}`) + } + // 更新当前选中状态 + currentSelectedRows.value = selection +} + + +const handleSelectAll = (selection) => { + ids.value = selection?.map((row) => row.id).filter((id) => id !== undefined) ?? [] + + /* let newVar = selection?.map((row) => row.id).filter((id) => id !== undefined) ?? []; + newVar.forEach(row => { + ids.value.push(row) + })*/ +} +const confirmCriticalComponentDialog = () => { + //let ids = selectedRows.value.map(item => item.id); + //const validMap = new Set(criticalComponentOptions.value.map((item) => item.value)) + //const selected = Array.from(new Set(criticalComponentDraft.value.map((v) => Number(v)).filter((v) => validMap.has(v)))) + formData.value.componentIds = filterValidSelectedIds(ids.value, criticalComponentOptions.value) + criticalComponentDialogVisible.value = false + //multipleTableRef.value.clearSelection() +} +const filterValidSelectedIds = (selectedIds: any[], options: SelectionOption[]) => { + const validIds = new Set(options.map((item) => item.value)) + return Array.from( + new Set( + (selectedIds ?? []) + .map((id) => normalizeNumberish(id)) + .filter((id): id is number => id !== undefined && validIds.has(id)) + ) + ) +} +const confirmBeijianDialog = () => { + // let ids = bjSelectedRows.value.map(item => item.id); + /* const validMap = new Set(beijianOptions.value.map((item) => item.value)) + const selected = Array.from(new Set(beijianDraft.value.map((v) => Number(v)).filter((v) => validMap.has(v))))*/ + formData.value.beijianIds = filterValidSelectedIds(bjIds.value, beijianOptions.value) + beijianDialogVisible.value = false + //multipleTableRef.value.clearSelection() +} + +const bjHandleSelectionChange = (rows: CriticalComponentVO[]) => { + bjSelectedIds.value = rows.map((r) => r.id).filter((id): id is number => typeof id === 'number') + // 获取当前页所有行的 id + const currentPageIds = rows.map(item => item.id) + + // 从已选中的数组中移除当前页的数据 + bjSelectedRows.value = bjSelectedRows.value.filter( + item => !currentPageIds.includes(item.id) + ) + // 添加当前页新选中的数据 + bjSelectedRows.value.push(...rows) +} + +// 存储当前已选中的行 +const bjCurrentSelectedRows = ref([]) + +// select 事件:row 是当前操作的行,selected 是操作后的状态 +const bjHandleSelect = (selection, row) => { + // 判断是选中还是取消选中 + const isSelected = selection.includes(row) + if (isSelected) { + // console.log(`✅ 行被选中: ID=${row.id}, Name=${row.name}`) + bjIds.value.push(row.id) + } else { + bjIds.value = bjIds.value.filter( + item => item !== row.id + ) + // console.log(`❌ 行被取消选中: ID=${row.id}, Name=${row.name}`) } + // 更新当前选中状态 + bjCurrentSelectedRows.value = selection } -const formatDetailDate = (value: any) => { - if (!value) return '' - return formatDate(new Date(value), 'YYYY-MM-DD') + +const bjHandleSelectAll = (selection) => { + bjIds.value = selection?.map((row) => row.id).filter((id) => id !== undefined) ?? [] } -const formatHistoryTime = (value: any) => { - const raw = value ?? '-' - if (Array.isArray(raw) && raw.length >= 3) { - const [y, m, d, hh, mm, ss] = raw - const pad = (n: any) => String(n).padStart(2, '0') - if (hh !== undefined) return `${y}-${pad(m)}-${pad(d)} ${pad(hh)}:${pad(mm)}:${pad(ss)}` - return `${y}-${pad(m)}-${pad(d)}` - } - const tryDateFromNumber = (v: any) => { - const num = Number(v) - if (!Number.isFinite(num)) return undefined - const s = String(v).trim() - const ms = s.length === 10 ? num * 1000 : num - const d = new Date(ms) - if (Number.isNaN(d.getTime())) return undefined - return d - } - const tryDateFromString = (v: any) => { - const s = String(v).trim() - if (!s) return undefined - const d = new Date(s) - if (Number.isNaN(d.getTime())) return undefined - return d + +const getList = async () => { + loading.value = true + try { + const data = await CriticalComponentApi.getCriticalComponentPage(queryParams) + list.value = data.list + total.value = data.total + // 数据加载后,重新设置选中状态 + nextTick(() => { + toggleSelection() + }) + } finally { + loading.value = false } - const d = typeof raw === 'number' ? tryDateFromNumber(raw) : tryDateFromNumber(raw) ?? tryDateFromString(raw) - if (d) return formatDate(d, 'YYYY-MM-DD HH:mm:ss') - return String(raw) } -const getResultLabel = (value: any) => { - const v = value === '' || value === null || value === undefined ? undefined : String(value) - if (!v) return '-' - const upper = v.toUpperCase() - if (v === '0') return '待检测' - if (v === '1' || upper === 'OK') return '通过' - if (v === '2' || upper === 'NG') return '不通过' - return v +/** 配件查询列表 */ +const bjGetList = async () => { + loading.value = true + try { + bjQueryParams.categoryId = 5 + const data = await ProductApi.getProductPage(bjQueryParams) + bjList.value = data.list + bjTotal.value = data.total + // 数据加载后,重新设置选中状态 + nextTick(() => { + bjToggleSelection() + }) + } finally { + loading.value = false + } } +// 切换选中状态 +const toggleSelection = () => { + if (!multipleTableRef.value || !selectedRows.value.length) return -const getResultTagType = (value: any) => { - const v = value === '' || value === null || value === undefined ? undefined : String(value) - if (!v) return 'info' - const upper = v.toUpperCase() - if (v === '1' || upper === 'OK') return 'success' - if (v === '2' || upper === 'NG') return 'danger' - if (v === '0') return 'info' - return 'info' -} + // 遍历当前页的数据 + list.value.forEach(row => { + // 检查这一行是否在已选中数组中 + const isSelected = selectedRows.value.some(item => item.id === row.id) -const parseImages = (value: any): string[] => { - if (!value) return [] - if (Array.isArray(value)) return value.map(String).filter(Boolean) - const cleaned = String(value).replace(/[`'"]/g, '').trim() - return cleaned - .split(',') - .map((v) => v.trim()) - .filter(Boolean) -} - -const deviceImageList = computed(() => parseImages(detailData.value?.images)) -const firstDeviceImage = computed(() => deviceImageList.value[0] ?? '') -const deviceManagerName = computed(() => (detailData.value as any)?.deviceManagerName ?? detailData.value?.deviceManager ?? '') - -type HistoryStepItem = { - key: string - name: string - result: any - method?: any - criteria?: any - images?: string[] - remark?: any - taskTime?: any - createTime?: any -} -type HistoryStepGroup = { key: string; time: string; operator: string; items: HistoryStepItem[] } - -const buildStepGroups = ( - rows: any[], - options: { timeField: string; nameFieldCandidates: string[]; resultFieldCandidates: string[] } -) => { - const groups = new Map() - for (const row of rows ?? []) { - const time = formatHistoryTime(row?.taskTime ?? row?.[options.timeField] ?? row?.createTime) - const operator = String(row?.operator ?? row?.creatorName ?? row?.creator ?? '-') - const groupKey = `${row?.managementId ?? ''}__${time}__${operator}` - const name = - options.nameFieldCandidates - .map((k) => row?.[k]) - .find((v) => v !== undefined && v !== null && String(v).trim() !== '') ?? '-' - const result = - options.resultFieldCandidates - .map((k) => row?.[k]) - .find((v) => v !== undefined && v !== null) ?? undefined - const item: HistoryStepItem = { - key: String(row?.id ?? `${groupKey}__${String(name)}`), - name: String(name), - result, - method: row?.inspectionMethod, - criteria: row?.judgmentCriteria, - images: parseImages(row?.images), - remark: row?.remark, - taskTime: row?.taskTime, - createTime: row?.createTime - } - if (!groups.has(groupKey)) { - groups.set(groupKey, { key: groupKey, time, operator, items: [item] }) + if (isSelected) { + // 如果应该选中,就选中 + multipleTableRef.value!.toggleRowSelection(row, true) } else { - groups.get(groupKey)!.items.push(item) + // 否则取消选中 + multipleTableRef.value!.toggleRowSelection(row, false) } - } - return Array.from(groups.values()).sort((a, b) => String(b.time).localeCompare(String(a.time))) + }) } -const inspectionHistory = ref([]) -const maintainHistory = ref([]) +// 切换选中状态 +const bjToggleSelection = () => { + if (!bjMultipleTableRef.value || !bjSelectedRows.value.length) return -const inspectionStepGroups = computed(() => { - return buildStepGroups(inspectionHistory.value, { - timeField: 'inspectionTime', - nameFieldCandidates: ['inspectionItemName', 'name'], - resultFieldCandidates: ['inspectionResult'] - }) -}) + // 遍历当前页的数据 + bjList.value.forEach(row => { + // 检查这一行是否在已选中数组中 + const isSelected = bjSelectedRows.value.some(item => item.id === row.id) -const maintainStepGroups = computed(() => { - return buildStepGroups(maintainHistory.value, { - timeField: 'inspectionTime', - nameFieldCandidates: ['maintainItemName', 'inspectionItemName', 'name'], - resultFieldCandidates: ['maintainResult', 'inspectionResult'] + if (isSelected) { + // 如果应该选中,就选中 + bjMultipleTableRef.value!.toggleRowSelection(row, true) + } else { + // 否则取消选中 + bjMultipleTableRef.value!.toggleRowSelection(row, false) + } }) -}) +} -type RepairHistoryRow = { - id?: any - repairId?: any - repairCode?: any - repairName?: any - subjectId?: any - subjectCode?: any - subjectName?: any - subjectContent?: any - subjectStandard?: any - malfunction?: any - malfunctionUrl?: any - repairDes?: any - remark?: any - createTime?: any - finishDate?: any - result?: any - repairResult?: any - malfunctionImages?: string[] -} -type RepairHistoryGroup = { key: string; name: string; items: RepairHistoryRow[] } - -const repairActiveNames = ref([]) -const repairList = ref([]) - -const repairGroups = computed(() => { - const groupsMap = new Map() - for (const row of repairList.value ?? []) { - const key = String(row.repairCode ?? row.repairId ?? row.subjectName ?? '-') - if (!groupsMap.has(key)) { - groupsMap.set(key, { - key, - name: String(row.repairName ?? row.repairCode ?? key), - items: [] - }) +const ids = ref([]) +const bjIds = ref([]) +const openCriticalComponentDialog = () => { + criticalComponentDraft.value = [...(formData.value.componentIds ?? [])] + criticalComponentDialogVisible.value = true + ids.value = [...(formData.value.componentIds ?? [])] + setDefaultSelections() +} +const openBeijianDialog = () => { + beijianDraft.value = [...(formData.value.beijianIds ?? [])] + beijianDialogVisible.value = true + bjIds.value = [...(formData.value.beijianIds ?? [])] + setBJDefaultSelections() +} + +// 设置默认选中的行 +const setDefaultSelections = () => { + // 等待DOM更新完成 + nextTick(() => { + if (!multipleTableRef.value) return + multipleTableRef.value.clearSelection() + const rawSubjectIds = toRaw(formData.value.componentIds) + if (rawSubjectIds.length != 0) { + let row = { + id: undefined + } + multipleTableRef.value.toggleRowSelection(row, true) } - const group = groupsMap.get(key)! - group.items.push({ - ...row, - malfunctionImages: parseImages(row?.malfunctionUrl) + // 遍历数据,找到需要选中的行 + list.value.forEach(row => { + let id = row.id; + if (rawSubjectIds.includes(row.id)) { + multipleTableRef.value.toggleRowSelection(row, true) + } }) - } - const groups = Array.from(groupsMap.values()).filter((g) => g.items.length) - return groups.sort((a, b) => { - const at = formatHistoryTime(a.items?.[0]?.finishDate ?? a.items?.[0]?.createTime) - const bt = formatHistoryTime(b.items?.[0]?.finishDate ?? b.items?.[0]?.createTime) - return String(bt).localeCompare(String(at)) }) -}) - -const inspectionExportLoading = ref(false) -const inspectionDateRange = ref(undefined) -const maintainExportLoading = ref(false) -const maintainDateRange = ref(undefined) -const repairExportLoading = ref(false) -const repairDateRange = ref(undefined) -const criticalExportLoading = ref(false) -const spareExportLoading = ref(false) +} -const fetchInspectionHistory = async () => { - if (!deviceId.value) return - const params: any = { deviceId: deviceId.value } - if (inspectionDateRange.value && inspectionDateRange.value.length === 2) { - params.startTime = inspectionDateRange.value[0] - params.endTime = inspectionDateRange.value[1] +// 设置默认选中的行 +const setBJDefaultSelections = () => { + // 等待DOM更新完成 + nextTick(() => { + if (!bjMultipleTableRef.value) return + bjMultipleTableRef.value.clearSelection() + const rawSubjectIds = toRaw(formData.value.beijianIds) + if (rawSubjectIds.length != 0) { + let row = { + id: undefined + } + bjMultipleTableRef.value.toggleRowSelection(row, true) + } + // 遍历数据,找到需要选中的行 + bjList.value.forEach(row => { + let id = row.id; + if (rawSubjectIds.includes(row.id)) { + bjMultipleTableRef.value.toggleRowSelection(row, true) + } + }) + }) +} +const normalizeNumberish = (value: any): number | undefined => { + if (value === null || value === undefined || value === '') return undefined + if (typeof value === 'number') return Number.isFinite(value) ? value : undefined + if (typeof value === 'string') { + const trimmed = value.trim() + if (!trimmed) return undefined + const n = Number(trimmed) + return Number.isFinite(n) ? n : undefined } - const data = await TicketManagementApi.getInspectionByDeviceId(params) - inspectionHistory.value = Array.isArray(data) ? data : [] + return undefined } -const handleQueryInspection = async () => { - await fetchInspectionHistory() +const normalizeYmd = (value: any): string | undefined => { + if (value === null || value === undefined || value === '') return undefined + if (typeof value === 'string') { + const trimmed = value.trim() + const matched = trimmed.match(/^(\d{4}-\d{2}-\d{2})/) + if (matched?.[1]) return matched[1] + const parsed = Date.parse(trimmed) + if (!Number.isNaN(parsed)) return formatDate(new Date(parsed), 'YYYY-MM-DD') + return trimmed + } + if (typeof value === 'number') return formatDate(new Date(value), 'YYYY-MM-DD') + if (value instanceof Date) return formatDate(value, 'YYYY-MM-DD') + return formatDate(new Date(value), 'YYYY-MM-DD') } -const handleResetInspection = async () => { - inspectionDateRange.value = undefined - await fetchInspectionHistory() -} +const initFormData = () => ({ + id: undefined, + images: undefined, + deviceCode: undefined, + isCode: true, + deviceName: undefined, + deviceStatus: undefined, + deviceBrand: undefined, + deviceModel: undefined, + deviceSpec: undefined, + isScheduled: 0, + ratedCapacity: undefined, + deviceType: undefined as number | undefined, + deviceLine: undefined as number | undefined, + supplier: undefined, + workshop: undefined, + deviceLocation: undefined, + systemOrg: undefined, + deviceManagerIds: [] as number[], + productionDate: undefined, + factoryEntryDate: undefined, + remark: undefined, + componentIds: [] as number[], + beijianIds: [] as number[], + fileUrl: '', + qrcodeUrl: undefined, + templateJson: undefined, + sort: undefined, + dvId: undefined +}) -const fetchMaintainHistory = async () => { - if (!deviceId.value) return - const params: any = { deviceId: deviceId.value } - if (maintainDateRange.value && maintainDateRange.value.length === 2) { - params.startTime = maintainDateRange.value[0] - params.endTime = maintainDateRange.value[1] +const formLoading = ref(false) +const fileUploading = ref(false) +const formType = ref('update') +const formRef = ref() +const formData = ref({ + ...initFormData() +}) +const isScheduledEnabled = computed(() => Number(formData.value.isScheduled) === 1) +const validateDeviceCode = (_rule, value, callback) => { + if (Boolean(formData.value.isCode)) { + callback() + return + } + if (value === undefined || value === null || String(value).trim() === '') { + callback(new Error(t('EquipmentManagement.EquipmentLedger.validatorDeviceCodeRequired'))) + return + } + callback() +} +const validateScheduledRequired = (label: string) => (_rule, value, callback) => { + if (!isScheduledEnabled.value) { + callback() + return } - const data = await TicketManagementApi.getMaintenanceByDeviceId(params) - maintainHistory.value = Array.isArray(data) ? data : [] + const normalized = normalizeNumberish(value) + if (normalized === undefined) { + callback(new Error(String(label))) + return + } + callback() } +const formRules = reactive({ + deviceCode: [{ validator: validateDeviceCode, trigger: ['blur', 'change'] }], + deviceName: [{ required: true, message: t('EquipmentManagement.EquipmentLedger.placeholderDeviceName'), trigger: 'blur' }], + deviceType: [{ required: true, message: t('EquipmentManagement.EquipmentLedger.placeholderDeviceType'), trigger: 'change' }], + ratedCapacity: [{ validator: validateScheduledRequired('ratedCapacity'), trigger: ['blur', 'change'] }] +}) +const treeSelectProps = { label: 'name', children: 'children' } +const deviceTypeTree = ref([]) +const users = ref([]) +type SelectionOption = { label: string; value: number } +const criticalComponentOptions = ref([]) +const beijianOptions = ref([]) +const savedCriticalComponentOptions = ref([]) +const savedBeijianOptions = ref([]) + +const buildCriticalComponentOptions = (items: any[] = []): SelectionOption[] => + (items ?? []) + .map((item: any) => { + const id = normalizeNumberish(item?.id) + if (id === undefined) return undefined + const code = item.code ? String(item.code) : '' + const name = item.name ? String(item.name) : '' + const label = code && name ? `${code}-${name}` : name || code || String(id) + return { label, value: id } + }) + .filter((item): item is SelectionOption => Boolean(item)) + +const buildBeijianOptions = (items: any[] = []): SelectionOption[] => + (items ?? []) + .map((item: any) => { + const id = normalizeNumberish(item?.id) + if (id === undefined) return undefined + const code = item.barCode ? String(item.barCode) : '' + const name = item.name ? String(item.name) : '' + const label = code && name ? `${code}-${name}` : name || code || String(id) + return { label, value: id } + }) + .filter((item): item is SelectionOption => Boolean(item)) + +const mergeSelectionOptions = (...groups: SelectionOption[][]): SelectionOption[] => { + const optionMap = new Map() + groups.flat().forEach((item) => optionMap.set(item.value, item)) + return Array.from(optionMap.values()) +} + +const formatSelectedSummary = (ids: number[], options: SelectionOption[]) => { + const optionMap = new Map(options.map((item) => [item.value, item.label])) + const labels = ids.map((id) => optionMap.get(id)).filter((v): v is string => Boolean(v)) + if (!labels.length) return '' + if (labels.length <= 3) return labels.join(', ') + return `${labels.slice(0, 3).join(', ')}...${labels.length}` +} + +const criticalComponentDisplay = computed(() => + formatSelectedSummary( + formData.value.componentIds ?? [], + mergeSelectionOptions(criticalComponentOptions.value, savedCriticalComponentOptions.value) + ) +) +const beijianDisplay = computed(() => + formatSelectedSummary(formData.value.beijianIds ?? [], mergeSelectionOptions(beijianOptions.value, savedBeijianOptions.value)) +) +const criticalComponentTableData = computed(() => { + const detailList = (detailData.value as any)?.componentList + if (Array.isArray(detailList) && detailList.length) return detailList + + const selectedIds = new Set((formData.value.componentIds ?? []).map((id: any) => Number(id)).filter((id: number) => !Number.isNaN(id))) + if (!selectedIds.size) return [] + + const optionMap = new Map() + ;[...criticalComponentOptions.value, ...savedCriticalComponentOptions.value].forEach((item: any) => { + optionMap.set(Number(item.value), item) + }) -const handleQueryMaintain = async () => { - await fetchMaintainHistory() + return Array.from(selectedIds).map((id) => { + const option = optionMap.get(id) + const label = option?.label ?? String(id) + const [code, ...nameParts] = label.split('-') + return { + id, + code: nameParts.length ? code : '', + name: nameParts.length ? nameParts.join('-') : label, + description: '', + remark: '', + createTime: undefined + } + }) +}) + +const clearCriticalComponent = () => { + formData.value.componentIds = [] +} +const clearBeijian = () => { + formData.value.beijianIds = [] } -const handleResetMaintain = async () => { - maintainDateRange.value = undefined - await fetchMaintainHistory() +const handleFileUploadingChange = (uploading: boolean) => { + fileUploading.value = uploading } -const fetchRepairHistory = async () => { - if (!deviceId.value) return - const params: any = { deviceId: deviceId.value } - if (repairDateRange.value && repairDateRange.value.length === 2) { - params.startTime = repairDateRange.value[0] - params.endTime = repairDateRange.value[1] +watch( + () => formData.value.isScheduled, + () => { + formRef.value?.clearValidate?.(['ratedCapacity']) } - const data = await DvRepairApi.getRepairListByDeviceId(params) - repairList.value = Array.isArray(data) ? data : [] -} +) -const handleQueryRepair = async () => { - await fetchRepairHistory() +const handleCodeAutoChange = (value: boolean) => { + if (value) { + formData.value.deviceCode = undefined + } + formRef.value?.clearValidate('deviceCode') } -const handleResetRepair = async () => { - repairDateRange.value = undefined - await fetchRepairHistory() +const getQrcodeRefreshUrl = () => { + if (!formData.value.id || !formData.value.deviceCode) return '' + return `/mes/device-ledger/regenerate-code?id=${formData.value.id}&code=${encodeURIComponent(String(formData.value.deviceCode))}` } -const handleExportInspection = async () => { - if (!deviceId.value) return - try { - await message.exportConfirm() - inspectionExportLoading.value = true - const params: any = { deviceId: deviceId.value } - if (inspectionDateRange.value && inspectionDateRange.value.length === 2) { - params.startTime = inspectionDateRange.value[0] - params.endTime = inspectionDateRange.value[1] - } - const data = await TicketManagementApi.exportInspection(params) - download.excel(data, '点检履历.xls') - } catch { - } finally { - inspectionExportLoading.value = false +const buildPrintData = () => { + return { + id: formData.value.id, + deviceCode: formData.value.deviceCode, + deviceName: formData.value.deviceName, + deviceSpec: formData.value.deviceSpec, + deviceBrand: formData.value.deviceBrand, + deviceModel: formData.value.deviceModel, + deviceLocation: formData.value.deviceLocation, + remark: formData.value.remark, + qrcodeUrl: formData.value.qrcodeUrl } } -const handleExportMaintain = async () => { - if (!deviceId.value) return - try { - await message.exportConfirm() - maintainExportLoading.value = true - const params: any = { deviceId: deviceId.value } - if (maintainDateRange.value && maintainDateRange.value.length === 2) { - params.startTime = maintainDateRange.value[0] - params.endTime = maintainDateRange.value[1] - } - const data = await TicketManagementApi.exportMaintenance(params) - download.excel(data, '保养履历.xls') - } catch { - } finally { - maintainExportLoading.value = false +const handleQrcodeRefreshSuccess = async (data: any) => { + if (!formData.value.id) return + if (data?.qrcodeUrl) { + formData.value.qrcodeUrl = data.qrcodeUrl + return } -} - -const handleExportRepair = async () => { - if (!deviceId.value) return - try { - await message.exportConfirm() - repairExportLoading.value = true - const params: any = { deviceId: deviceId.value } - if (repairDateRange.value && repairDateRange.value.length === 2) { - params.startTime = repairDateRange.value[0] - params.endTime = repairDateRange.value[1] - } - const data = await DvRepairApi.exportRepairExcel(params) - download.excel(data, '维修履历.xls') - } catch { - } finally { - repairExportLoading.value = false + const detail = await DeviceLedgerApi.getDeviceLedger(formData.value.id) + formData.value.qrcodeUrl = detail?.qrcodeUrl + formData.value.deviceCode = detail?.deviceCode ?? formData.value.deviceCode +} + +const ensureOptionsLoaded = async () => { + const [deviceTypeRes, userRes, criticalRes, beijianRes] = await Promise.all([ + DeviceTypeApi.getDeviceTypeTree({ pageNo: 1, pageSize: 10 }), + getSimpleUserList(), + CriticalComponentApi.getCriticalComponentList(), + ProductApi.getComponentSimpleList() + ]) + deviceTypeTree.value = deviceTypeRes ?? [] + users.value = userRes ?? [] + criticalComponentOptions.value = buildCriticalComponentOptions(criticalRes ?? []) + beijianOptions.value = buildBeijianOptions(beijianRes ?? []) +} + +const bindFormData = (detail: DeviceLedgerVO) => { + const templateJson = (detail as any)?.templateJson + const parsedTemplateJson = + typeof templateJson === 'string' + ? (() => { + try { + return JSON.parse(templateJson) + } catch { + return undefined + } + })() + : templateJson + formData.value = { + ...initFormData(), + ...(detail as any), + templateJson: parsedTemplateJson, + isCode: (detail as any)?.isCode ?? false, + isScheduled: normalizeNumberish((detail as any)?.isScheduled) ?? 0, + ratedCapacity: normalizeNumberish((detail as any)?.ratedCapacity), + deviceType: normalizeNumberish((detail as any)?.deviceType), + deviceLine: normalizeNumberish((detail as any)?.deviceLine), + deviceManagerIds: parseIdsValue((detail as any)?.deviceManager), + productionDate: normalizeYmd((detail as any)?.productionDate), + factoryEntryDate: normalizeYmd((detail as any)?.factoryEntryDate), + componentIds: parseIdsValue((detail as any)?.componentId), + beijianIds: parseIdsValue((detail as any)?.beijianId), + qrcodeUrl: (detail as any)?.qrcodeUrl } + savedCriticalComponentOptions.value = buildCriticalComponentOptions((detail as any)?.componentList ?? []) + savedBeijianOptions.value = buildBeijianOptions((detail as any)?.beijianList ?? []) } +const criticalExportLoading = ref(false) const handleExportCriticalComponent = async () => { if (!deviceId.value) return try { await message.exportConfirm() criticalExportLoading.value = true const data = await CriticalComponentApi.exportDeviceComponent({ id: deviceId.value }) - download.excel(data, '关键件.xls') + download.excel(data, 'critical-component.xls') } catch { } finally { criticalExportLoading.value = false } } -const handleExportSpareBased = async () => { - if (!deviceId.value) return - try { - await message.exportConfirm() - spareExportLoading.value = true - const data = await DeviceLedgerApi.exportSpareBased({ id: deviceId.value }) - download.excel(data, '备件.xls') - } catch { - } finally { - spareExportLoading.value = false - } -} - -const getTypeTreeForNameMap = async () => { - const data = await DeviceTypeApi.getDeviceTypeTree({ pageNo: 1, pageSize: 10 }) - const treeChildren = JSON.parse(JSON.stringify(data ?? [])) - buildDeviceTypeNameMap(treeChildren) -} const getDetail = async () => { if (!deviceId.value) { @@ -678,27 +941,27 @@ const getDetail = async () => { } detailLoading.value = true try { - detailData.value = await DeviceLedgerApi.getDeviceLedger(deviceId.value) - await fetchInspectionHistory() - await fetchMaintainHistory() - await fetchRepairHistory() - const keys = repairGroups.value.map((g) => g.key) - repairActiveNames.value = keys.length ? [keys[0]] : [] + const detail = await DeviceLedgerApi.getDeviceLedger(deviceId.value) + detailData.value = detail + bindFormData(detail) } finally { detailLoading.value = false } } onMounted(async () => { - await getTypeTreeForNameMap() - await getDetail() + await ensureOptionsLoaded() + // await getDetail() + getList() + + bjGetList() }) -.device-ledger-history-image-error { - display: flex; - width: 100%; - height: 100%; - font-size: 12px; - color: var(--el-text-color-secondary); - background: var(--el-fill-color); - align-items: center; - justify-content: center; -} -.device-ledger-repair-collapse { - padding: 8px 8px 0; -} -.device-ledger-repair-title { - display: flex; - align-items: center; - justify-content: space-between; - width: 100%; -} -.device-ledger-repair-name { - font-weight: 600; - color: var(--el-text-color-primary); -} -.device-ledger-repair-meta { - font-size: 12px; - color: var(--el-text-color-secondary); -} -.device-ledger-history-item-text { - color: var(--el-text-color-regular); -} - From 2534340dda0583b3ab6558a75f6716f7ece7cfeb Mon Sep 17 00:00:00 2001 From: liutao <790864623@qq.com> Date: Thu, 28 May 2026 17:34:45 +0800 Subject: [PATCH 05/18] update --- .../mes/deviceledger/detail/editIndex.vue | 92 ++++++++++++++----- 1 file changed, 70 insertions(+), 22 deletions(-) diff --git a/src/views/mes/deviceledger/detail/editIndex.vue b/src/views/mes/deviceledger/detail/editIndex.vue index 30acd00b..926713b1 100644 --- a/src/views/mes/deviceledger/detail/editIndex.vue +++ b/src/views/mes/deviceledger/detail/editIndex.vue @@ -618,28 +618,6 @@ const setDefaultSelections = () => { }) } -// 设置默认选中的行 -const setBJDefaultSelections = () => { - // 等待DOM更新完成 - nextTick(() => { - if (!bjMultipleTableRef.value) return - bjMultipleTableRef.value.clearSelection() - const rawSubjectIds = toRaw(formData.value.beijianIds) - if (rawSubjectIds.length != 0) { - let row = { - id: undefined - } - bjMultipleTableRef.value.toggleRowSelection(row, true) - } - // 遍历数据,找到需要选中的行 - bjList.value.forEach(row => { - let id = row.id; - if (rawSubjectIds.includes(row.id)) { - bjMultipleTableRef.value.toggleRowSelection(row, true) - } - }) - }) -} const normalizeNumberish = (value: any): number | undefined => { if (value === null || value === undefined || value === '') return undefined if (typeof value === 'number') return Number.isFinite(value) ? value : undefined @@ -949,8 +927,78 @@ const getDetail = async () => { } } + +// 方法1:直接监听 data +watch(bjList, (newData, oldData) => { + // 可以在这里执行相关操作 + if (newData.length > 0) { + // 数据加载完成后的操作 + setBJDefaultSelections() + } +}, { deep: true }) + + +//const defaultSelectedIds = [144, 143,141] + +// 设置默认选中的行 +const setBJDefaultSelections = () => { + // 等待DOM更新完成 + nextTick(() => { + if (!bjMultipleTableRef.value) return + bjMultipleTableRef.value.clearSelection() + const rawSubjectIds = toRaw(formData.value.beijianIds) + if (rawSubjectIds.length != 0) { + let row = { + id: undefined + } + bjMultipleTableRef.value.toggleRowSelection(row, true) + } + // 遍历数据,找到需要选中的行 + bjList.value.forEach(row => { + let id = row.id; + if (rawSubjectIds.includes(row.id)) { + bjMultipleTableRef.value.toggleRowSelection(row, true) + } + }) + }) +} +async function initForm() { + // 修改时,设置数据 + if (deviceId.value) { + formLoading.value = true + try { + const detail = await DeviceLedgerApi.getDeviceLedger(deviceId.value) + const templateJson = (detail as any)?.templateJson + const parsedTemplateJson = typeof templateJson === 'string' + ? JSON.parse(templateJson) + : templateJson + formData.value = { + ...initFormData(), + ...(detail as any), + templateJson: parsedTemplateJson, + isCode: (detail as any)?.isCode ?? false, + isScheduled: normalizeNumberish((detail as any)?.isScheduled ?? (detail as any)?.isScheduled) ?? 0, + ratedCapacity: normalizeNumberish((detail as any)?.ratedCapacity), + deviceType: normalizeNumberish((detail as any)?.deviceType), + deviceLine: normalizeNumberish((detail as any)?.deviceLine), + deviceManagerIds: parseIdsValue((detail as any)?.deviceManager), + productionDate: normalizeYmd((detail as any)?.productionDate), + factoryEntryDate: normalizeYmd((detail as any)?.factoryEntryDate), + componentIds: parseIdsValue((detail as any)?.componentId), + beijianIds: parseIdsValue((detail as any)?.beijianId), + qrcodeUrl: (detail as any)?.qrcodeUrl, + } + savedCriticalComponentOptions.value = buildCriticalComponentOptions((detail as any)?.componentList ?? []) + savedBeijianOptions.value = buildBeijianOptions((detail as any)?.beijianList ?? []) + } finally { + formLoading.value = false + } + } +} + onMounted(async () => { await ensureOptionsLoaded() + initForm() // await getDetail() getList() From 5c81e130b0efe1e2baf9a2f50e8fab40a445b5bd Mon Sep 17 00:00:00 2001 From: liutao <790864623@qq.com> Date: Fri, 29 May 2026 08:22:34 +0800 Subject: [PATCH 06/18] update --- .../mes/deviceledger/detail/editIndex.vue | 199 +++++++++++++----- 1 file changed, 147 insertions(+), 52 deletions(-) diff --git a/src/views/mes/deviceledger/detail/editIndex.vue b/src/views/mes/deviceledger/detail/editIndex.vue index 926713b1..6bfb0854 100644 --- a/src/views/mes/deviceledger/detail/editIndex.vue +++ b/src/views/mes/deviceledger/detail/editIndex.vue @@ -156,10 +156,7 @@ - +
@@ -177,11 +174,20 @@ {{ t('action.export') }}
- + - + + + @@ -189,7 +195,11 @@ - + + +
+ {{ t('common.cancel') }} + {{ t('common.save') }}
- + { @@ -404,36 +424,32 @@ const bjResetQuery = () => { const handleSelectionChange = (rows: CriticalComponentVO[]) => { selectedIds.value = rows.map((r) => r.id).filter((id): id is number => typeof id === 'number') - // 获取当前页所有行的 id + // 获取当前页所有行�?id const currentPageIds = rows.map(item => item.id) // 从已选中的数组中移除当前页的数据 selectedRows.value = selectedRows.value.filter( item => !currentPageIds.includes(item.id) ) - // 添加当前页新选中的数据 - selectedRows.value.push(...rows) + // 添加当前页新选中的数�? selectedRows.value.push(...rows) } // 存储当前已选中的行 -const currentSelectedRows = ref([]) -// select 事件:row 是当前操作的行,selected 是操作后的状态 -const handleSelect = (selection, row) => { +const handleSelect = (selection: any[], row: any) => { // 判断是选中还是取消选中 const isSelected = selection.includes(row) if (isSelected) { - // console.log(`✅ 行被选中: ID=${row.id}, Name=${row.name}`) + // console.log(`�?行被选中: ID=${row.id}, Name=${row.name}`) ids.value.push(row.id) } else { ids.value = ids.value.filter( item => item !== row.id ) - // console.log(`❌ 行被取消选中: ID=${row.id}, Name=${row.name}`) + // console.log(`�?行被取消选中: ID=${row.id}, Name=${row.name}`) } - // 更新当前选中状态 - currentSelectedRows.value = selection + // 更新当前选中状�? currentSelectedRows.value = selection } @@ -451,6 +467,7 @@ const confirmCriticalComponentDialog = () => { //const selected = Array.from(new Set(criticalComponentDraft.value.map((v) => Number(v)).filter((v) => validMap.has(v)))) formData.value.componentIds = filterValidSelectedIds(ids.value, criticalComponentOptions.value) criticalComponentDialogVisible.value = false + syncCriticalComponentRows() //multipleTableRef.value.clearSelection() } const filterValidSelectedIds = (selectedIds: any[], options: SelectionOption[]) => { @@ -474,35 +491,31 @@ const confirmBeijianDialog = () => { const bjHandleSelectionChange = (rows: CriticalComponentVO[]) => { bjSelectedIds.value = rows.map((r) => r.id).filter((id): id is number => typeof id === 'number') - // 获取当前页所有行的 id + // 获取当前页所有行�?id const currentPageIds = rows.map(item => item.id) // 从已选中的数组中移除当前页的数据 bjSelectedRows.value = bjSelectedRows.value.filter( item => !currentPageIds.includes(item.id) ) - // 添加当前页新选中的数据 - bjSelectedRows.value.push(...rows) + // 添加当前页新选中的数�? bjSelectedRows.value.push(...rows) } // 存储当前已选中的行 -const bjCurrentSelectedRows = ref([]) -// select 事件:row 是当前操作的行,selected 是操作后的状态 -const bjHandleSelect = (selection, row) => { +const bjHandleSelect = (selection: any[], row: any) => { // 判断是选中还是取消选中 const isSelected = selection.includes(row) if (isSelected) { - // console.log(`✅ 行被选中: ID=${row.id}, Name=${row.name}`) + // console.log(`�?行被选中: ID=${row.id}, Name=${row.name}`) bjIds.value.push(row.id) } else { bjIds.value = bjIds.value.filter( item => item !== row.id ) - // console.log(`❌ 行被取消选中: ID=${row.id}, Name=${row.name}`) + // console.log(`�?行被取消选中: ID=${row.id}, Name=${row.name}`) } - // 更新当前选中状态 - bjCurrentSelectedRows.value = selection + // 更新当前选中状�? bjCurrentSelectedRows.value = selection } @@ -517,7 +530,6 @@ const getList = async () => { const data = await CriticalComponentApi.getCriticalComponentPage(queryParams) list.value = data.list total.value = data.total - // 数据加载后,重新设置选中状态 nextTick(() => { toggleSelection() }) @@ -534,7 +546,6 @@ const bjGetList = async () => { const data = await ProductApi.getProductPage(bjQueryParams) bjList.value = data.list bjTotal.value = data.total - // 数据加载后,重新设置选中状态 nextTick(() => { bjToggleSelection() }) @@ -542,13 +553,11 @@ const bjGetList = async () => { loading.value = false } } -// 切换选中状态 const toggleSelection = () => { if (!multipleTableRef.value || !selectedRows.value.length) return // 遍历当前页的数据 list.value.forEach(row => { - // 检查这一行是否在已选中数组中 const isSelected = selectedRows.value.some(item => item.id === row.id) if (isSelected) { @@ -561,13 +570,11 @@ const toggleSelection = () => { }) } -// 切换选中状态 const bjToggleSelection = () => { if (!bjMultipleTableRef.value || !bjSelectedRows.value.length) return // 遍历当前页的数据 bjList.value.forEach(row => { - // 检查这一行是否在已选中数组中 const isSelected = bjSelectedRows.value.some(item => item.id === row.id) if (isSelected) { @@ -606,13 +613,12 @@ const setDefaultSelections = () => { let row = { id: undefined } - multipleTableRef.value.toggleRowSelection(row, true) + multipleTableRef.value!.toggleRowSelection(row, true) } // 遍历数据,找到需要选中的行 list.value.forEach(row => { - let id = row.id; if (rawSubjectIds.includes(row.id)) { - multipleTableRef.value.toggleRowSelection(row, true) + multipleTableRef.value!.toggleRowSelection(row, true) } }) }) @@ -721,6 +727,7 @@ const criticalComponentOptions = ref([]) const beijianOptions = ref([]) const savedCriticalComponentOptions = ref([]) const savedBeijianOptions = ref([]) +const editableCriticalComponentRows = ref([]) const buildCriticalComponentOptions = (items: any[] = []): SelectionOption[] => (items ?? []) @@ -769,9 +776,11 @@ const criticalComponentDisplay = computed(() => const beijianDisplay = computed(() => formatSelectedSummary(formData.value.beijianIds ?? [], mergeSelectionOptions(beijianOptions.value, savedBeijianOptions.value)) ) -const criticalComponentTableData = computed(() => { +const buildCriticalComponentTableRows = () => { const detailList = (detailData.value as any)?.componentList - if (Array.isArray(detailList) && detailList.length) return detailList + if (Array.isArray(detailList) && detailList.length) { + return detailList.map((item: any) => ({ ...item, count: normalizeNumberish(item?.count) ?? 0 })) + } const selectedIds = new Set((formData.value.componentIds ?? []).map((id: any) => Number(id)).filter((id: number) => !Number.isNaN(id))) if (!selectedIds.size) return [] @@ -789,12 +798,17 @@ const criticalComponentTableData = computed(() => { id, code: nameParts.length ? code : '', name: nameParts.length ? nameParts.join('-') : label, + count: 0, description: '', remark: '', createTime: undefined } }) -}) +} + +const syncCriticalComponentRows = () => { + editableCriticalComponentRows.value = buildCriticalComponentTableRows() +} const clearCriticalComponent = () => { formData.value.componentIds = [] @@ -895,8 +909,65 @@ const bindFormData = (detail: DeviceLedgerVO) => { savedCriticalComponentOptions.value = buildCriticalComponentOptions((detail as any)?.componentList ?? []) savedBeijianOptions.value = buildBeijianOptions((detail as any)?.beijianList ?? []) } + syncCriticalComponentRows() const criticalExportLoading = ref(false) + +const normalizeFileUrlAsJsonArrayString = (value: any): string | undefined => { + if (value === null || value === undefined || value === '') return undefined + if (typeof value === 'string') return value.trim() || undefined + if (Array.isArray(value)) return JSON.stringify(value) + return JSON.stringify(value) +} + +const buildSubmitData = () => { + const componentRows = editableCriticalComponentRows.value.map((row: any) => ({ + ...row, + id: normalizeNumberish(row?.id), + count: normalizeNumberish(row?.count) ?? 0 + })).filter((row: any) => row.id !== undefined) + formData.value.componentIds = componentRows.map((row: any) => row.id) + + const data = { + ...(formData.value as any), + isScheduled: normalizeNumberish((formData.value as any).isScheduled) ?? 0, + ratedCapacity: normalizeNumberish((formData.value as any).ratedCapacity), + deviceType: normalizeNumberish(formData.value.deviceType), + deviceLine: normalizeNumberish((formData.value as any).deviceLine), + productionDate: normalizeYmd(formData.value.productionDate), + factoryEntryDate: normalizeYmd(formData.value.factoryEntryDate), + deviceManager: formData.value.deviceManagerIds?.length ? formData.value.deviceManagerIds.join(',') : undefined, + componentId: formData.value.componentIds?.length ? formData.value.componentIds.join(',') : undefined, + componentList: componentRows, + beijianId: formData.value.beijianIds?.length ? formData.value.beijianIds.join(',') : undefined, + fileUrl: normalizeFileUrlAsJsonArrayString((formData.value as any).fileUrl) + } as unknown as DeviceLedgerVO + + delete (data as any).deviceManagerIds + delete (data as any).componentIds + delete (data as any).beijianIds + return data +} + +const submitForm = async () => { + if (fileUploading.value) { + message.warning(t('common.loading')) + return + } + await formRef.value?.validate?.() + formLoading.value = true + try { + await DeviceLedgerApi.updateDeviceLedger(buildSubmitData()) + message.success(t('common.updateSuccess')) + await getDetail() + } finally { + formLoading.value = false + } +} + +const handleCancelEdit = async () => { + await getDetail() +} const handleExportCriticalComponent = async () => { if (!deviceId.value) return try { @@ -928,11 +999,8 @@ const getDetail = async () => { } -// 方法1:直接监听 data -watch(bjList, (newData, oldData) => { - // 可以在这里执行相关操作 +watch(bjList, (newData) => { if (newData.length > 0) { - // 数据加载完成后的操作 setBJDefaultSelections() } }, { deep: true }) @@ -951,13 +1019,12 @@ const setBJDefaultSelections = () => { let row = { id: undefined } - bjMultipleTableRef.value.toggleRowSelection(row, true) + bjMultipleTableRef.value!.toggleRowSelection(row, true) } // 遍历数据,找到需要选中的行 bjList.value.forEach(row => { - let id = row.id; if (rawSubjectIds.includes(row.id)) { - bjMultipleTableRef.value.toggleRowSelection(row, true) + bjMultipleTableRef.value!.toggleRowSelection(row, true) } }) }) @@ -968,6 +1035,7 @@ async function initForm() { formLoading.value = true try { const detail = await DeviceLedgerApi.getDeviceLedger(deviceId.value) + detailData.value = detail const templateJson = (detail as any)?.templateJson const parsedTemplateJson = typeof templateJson === 'string' ? JSON.parse(templateJson) @@ -990,6 +1058,7 @@ async function initForm() { } savedCriticalComponentOptions.value = buildCriticalComponentOptions((detail as any)?.componentList ?? []) savedBeijianOptions.value = buildBeijianOptions((detail as any)?.beijianList ?? []) + syncCriticalComponentRows() } finally { formLoading.value = false } @@ -1010,7 +1079,7 @@ onMounted(async () => { .device-ledger-detail-body { position: relative; max-height: 100vh; - padding-top: 2px; + padding: 2px 0 72px; } .device-ledger-detail-title { @@ -1062,6 +1131,22 @@ onMounted(async () => { text-overflow: ellipsis; white-space: nowrap; } +.device-ledger-action-bar { +/* position: sticky; + bottom: 0;*/ + position: fixed; + right: 24px; + bottom: 0; + left: 240px; + z-index: 10; + display: flex; + justify-content: flex-end; + gap: 12px; + padding: 12px 16px; + border-top: 1px solid var(--el-border-color-lighter); + background: var(--el-bg-color); + box-shadow: 0 -4px 12px rgb(0 0 0 / 6%); +} @@ -1069,3 +1154,13 @@ onMounted(async () => { + + + + + + + + + + From 97d450e8aa30eda5db7e082eac4608e7fb87e537 Mon Sep 17 00:00:00 2001 From: zhongwenkai <3478244299@qq.com> Date: Fri, 29 May 2026 15:26:53 +0800 Subject: [PATCH 07/18] =?UTF-8?q?style:=20=E4=BF=AE=E6=94=B9=E8=AE=BE?= =?UTF-8?q?=E5=A4=87=E7=AE=A1=E7=90=86=E4=B8=AD=E8=AE=BE=E5=A4=87=E5=85=B3?= =?UTF-8?q?=E9=94=AE=E4=BB=B6=E7=9A=84=E6=96=B0=E5=A2=9E/=E7=BC=96?= =?UTF-8?q?=E8=BE=91=E5=BC=B9=E6=A1=86=E6=A0=B7=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CriticalComponentForm.vue | 265 ++++++++----- src/views/mes/criticalComponent/index.vue | 354 ++++++++++-------- 2 files changed, 368 insertions(+), 251 deletions(-) diff --git a/src/views/mes/criticalComponent/CriticalComponentForm.vue b/src/views/mes/criticalComponent/CriticalComponentForm.vue index e9d7c164..c099d59c 100644 --- a/src/views/mes/criticalComponent/CriticalComponentForm.vue +++ b/src/views/mes/criticalComponent/CriticalComponentForm.vue @@ -1,104 +1,107 @@ - + + diff --git a/src/views/mes/criticalComponent/index.vue b/src/views/mes/criticalComponent/index.vue index e311b84d..8b68930e 100644 --- a/src/views/mes/criticalComponent/index.vue +++ b/src/views/mes/criticalComponent/index.vue @@ -1,154 +1,204 @@