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] =?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); -} -