From 691e0cec978789c4ed4102d162b8017f78498111 Mon Sep 17 00:00:00 2001 From: ck-chenkang Date: Thu, 18 Jun 2026 16:16:10 +0800 Subject: [PATCH] =?UTF-8?q?feat:=E4=BF=AE=E6=94=B9=E8=AE=BE=E5=A4=87?= =?UTF-8?q?=E7=BB=B4=E4=BF=AE-=E8=AE=BE=E5=A4=87=E5=92=8C=E5=85=B3?= =?UTF-8?q?=E9=94=AE=E4=BB=B6=E7=9A=84=E9=80=89=E6=8B=A9=E6=96=B9=E5=BC=8F?= =?UTF-8?q?=EF=BC=8C=E5=A2=9E=E5=8A=A0=E5=BC=B9=E7=AA=97=E4=BB=A5=E5=8F=8A?= =?UTF-8?q?=E8=A1=A8=E5=8D=95=E9=BB=98=E8=AE=A4=E5=BD=93=E5=A4=A9=E6=97=A5?= =?UTF-8?q?=E6=9C=9F=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pages/equipmentMaintenance/form.vue | 410 ++++++++++++++++-- 1 file changed, 380 insertions(+), 30 deletions(-) diff --git a/src/pages_function/pages/equipmentMaintenance/form.vue b/src/pages_function/pages/equipmentMaintenance/form.vue index 4d4fa4c..2f6de46 100644 --- a/src/pages_function/pages/equipmentMaintenance/form.vue +++ b/src/pages_function/pages/equipmentMaintenance/form.vue @@ -108,16 +108,24 @@ - - {{ selectedDeviceLabel }} - + + {{ selectedDeviceLabel }} + + {{ t('equipmentMaintenance.component') }} - - {{ selectedComponentLabel }} - + + {{ selectedComponentLabel }} + + @@ -232,6 +240,86 @@ {{ t('dashboard.back') }} {{ submitButtonText }} + + + + + {{ t('equipmentMaintenance.placeholderDevice') }} + × + + + + + + × + + + + + + + {{ item.raw?.deviceCode || String(item.value) }} + {{ item.raw?.deviceName || '-' }} + + {{ deviceMetaText(item) }} + + + + {{ t('equipmentMaintenance.deviceNotFound') }} + + + + + + + + {{ t('equipmentMaintenance.placeholderComponent') }} + × + + + + + + × + + + + + + + {{ item.raw?.code || item.raw?.componentCode || '-' }} + {{ item.raw?.name || item.raw?.componentName || '-' }} + + {{ componentMetaText(item) }} + + + + {{ t('equipmentMaintenance.deviceNotFound') }} + + + @@ -261,6 +349,10 @@ const users = ref([]) const deviceOptions = ref([]) const componentOptions = ref([]) const loading = ref(false) +const devicePickerRef = ref(null) +const deviceSearchKeyword = ref('') +const componentPickerRef = ref(null) +const componentSearchKeyword = ref('') const formData = reactive({ id: undefined, @@ -275,9 +367,9 @@ const formData = reactive({ machineryBrand: '', machinerySpec: '', machineryTypeId: 1, - requireDate: '', - finishDate: '', - confirmDate: '', + requireDate: todayPickerDate(), + finishDate: todayPickerDate(), + confirmDate: todayPickerDate(), repairStatus: '0', repairResult: '0', acceptedBy: '', @@ -318,7 +410,6 @@ const statusLabel = computed(() => { return t('equipmentMaintenance.statusPending') }) const userLabels = computed(() => users.value.map((item) => item.nickname || item.name || String(item.id || ''))) -const deviceLabels = computed(() => deviceOptions.value.map((item) => item.label)) const faultLevelOptions = computed(() => { const dicts = dictStore.getDict(DICT_TYPE.FAILURE_LEVEL) || [] return dicts @@ -333,14 +424,32 @@ const confirmByIndex = computed(() => findUserIndex(formData.confirmBy)) const acceptedByLabel = computed(() => resolveUserLabel(formData.acceptedBy, t('equipmentMaintenance.placeholderAcceptedBy'))) const confirmByLabel = computed(() => resolveUserLabel(formData.confirmBy, t('equipmentMaintenance.placeholderConfirmBy'))) const showComponentSelect = computed(() => Number(formData.machineryTypeId || 1) === 2) -const deviceIndex = computed(() => { - const index = deviceOptions.value.findIndex((item) => String(item.value) === String(formData.deviceId || '')) - return index >= 0 ? index : 0 -}) const selectedDeviceLabel = computed(() => { const current = deviceOptions.value.find((item) => String(item.value) === String(formData.deviceId || '')) return current?.label || t('equipmentMaintenance.placeholderDevice') }) +const filteredDeviceOptions = computed(() => { + const keyword = deviceSearchKeyword.value.trim().toLowerCase() + if (!keyword) return deviceOptions.value + const keywordId = extractEquipmentKeywordId(keyword) + return deviceOptions.value.filter((item) => { + const raw = item.raw || {} + const fields = [ + item.label, + item.value, + raw.id, + raw.deviceId, + raw.machineryId, + raw.machineryid, + raw.deviceCode, + raw.deviceName, + raw.deviceBrand, + raw.deviceSpec + ] + return fields.some((field) => String(field || '').toLowerCase().includes(keyword)) + || (keywordId && matchesDeviceOptionId(item, keywordId)) + }) +}) const filteredComponentOptions = computed(() => { const source = Array.isArray(componentOptions.value) ? componentOptions.value : [] const currentDeviceId = String(formData.deviceId || '').trim() @@ -348,10 +457,22 @@ const filteredComponentOptions = computed(() => { const matched = source.filter((item) => matchesComponentDevice(item?.raw, currentDeviceId)) return matched.length ? matched : source }) -const componentLabels = computed(() => filteredComponentOptions.value.map((item) => item.label)) -const componentIndex = computed(() => { - const index = filteredComponentOptions.value.findIndex((item) => String(item.value) === String(formData.componentId || '')) - return index >= 0 ? index : 0 +const searchableComponentOptions = computed(() => { + const keyword = componentSearchKeyword.value.trim().toLowerCase() + if (!keyword) return filteredComponentOptions.value + return filteredComponentOptions.value.filter((item) => { + const raw = item.raw || {} + const fields = [ + item.label, + raw.code, + raw.componentCode, + raw.name, + raw.componentName, + raw.deviceName, + raw.deviceCode + ] + return fields.some((field) => String(field || '').toLowerCase().includes(keyword)) + }) }) const selectedComponentLabel = computed(() => { const current = filteredComponentOptions.value.find((item) => String(item.value) === String(formData.componentId || '')) @@ -433,9 +554,9 @@ async function fetchDetail(id) { formData.machineryBrand = inputValue(detail?.machineryBrand) formData.machinerySpec = inputValue(detail?.machinerySpec) formData.machineryTypeId = Number(detail?.machineryTypeId || 1) || 1 - formData.requireDate = formatPickerDate(detail?.requireDate) - formData.finishDate = formatPickerDate(detail?.finishDate) - formData.confirmDate = formatPickerDate(detail?.confirmDate) + formData.requireDate = formatPickerDate(detail?.requireDate) || todayPickerDate() + formData.finishDate = formatPickerDate(detail?.finishDate) || todayPickerDate() + formData.confirmDate = formatPickerDate(detail?.confirmDate) || todayPickerDate() formData.repairStatus = normalizedRepairStatus formData.repairResult = normalizedRepairStatus formData.acceptedBy = normalizeUserId(detail?.acceptedBy) @@ -513,17 +634,61 @@ function onUserChange(field, event) { formData[field] = current ? String(current.id) : '' } -function onDeviceChange(event) { - const index = Number(event?.detail?.value || 0) - const current = deviceOptions.value[index] - if (!current) return +function openDevicePicker() { + if (readonlyBase.value) return + devicePickerRef.value?.open?.() +} + +function closeDevicePicker() { + devicePickerRef.value?.close?.() +} + +function clearDeviceSearch() { + deviceSearchKeyword.value = '' +} + +function selectDeviceOption(current) { + if (!current || readonlyBase.value) return applyDeviceOption(current) + clearDeviceSearch() + closeDevicePicker() } -function onComponentChange(event) { - const index = Number(event?.detail?.value || 0) - const current = filteredComponentOptions.value[index] - formData.componentId = current ? current.value : undefined +function deviceMetaText(item) { + const raw = item?.raw || {} + return [raw.deviceBrand, raw.deviceSpec].map(inputValue).filter(Boolean).join(' / ') +} + +function openComponentPicker() { + if (readonlyBase.value) return + componentPickerRef.value?.open?.() +} + +function closeComponentPicker() { + componentPickerRef.value?.close?.() +} + +function clearComponentSearch() { + componentSearchKeyword.value = '' +} + +function selectComponentOption(current) { + if (!current || readonlyBase.value) return + formData.componentId = current.value + clearComponentSearch() + closeComponentPicker() +} + +function componentMetaText(item) { + const raw = item?.raw || {} + return [ + raw.deviceName, + raw.deviceCode, + raw.spec, + raw.componentSpec, + raw.model, + raw.componentModel + ].map(inputValue).filter(Boolean).join(' / ') } function selectMachineryType(value) { @@ -552,7 +717,7 @@ async function handleDeviceScan() { uni.showToast({ title: t('equipmentMaintenance.scanEquipmentRequired'), icon: 'none' }) return } - const matched = deviceOptions.value.find((item) => String(item.value) === String(scan.id)) + const matched = deviceOptions.value.find((item) => matchesDeviceOptionId(item, scan.id)) if (!matched) { uni.showToast({ title: t('equipmentMaintenance.deviceNotFound'), icon: 'none' }) return @@ -587,6 +752,28 @@ function parseEquipmentScanResult(result) { } } +function extractEquipmentKeywordId(keyword) { + const match = String(keyword || '').trim().match(/-(\d+)$/) + return match ? match[1] : '' +} + +function getDeviceOptionIds(item) { + const raw = item?.raw || {} + return [ + item?.value, + raw.id, + raw.deviceId, + raw.machineryId, + raw.machineryid + ].filter((value) => value !== undefined && value !== null && value !== '') +} + +function matchesDeviceOptionId(item, id) { + const target = String(id || '').trim() + if (!target) return false + return getDeviceOptionIds(item).some((value) => String(value) === target) +} + function applyDeviceOption(current) { formData.deviceId = current.value formData.machineryId = current.value @@ -671,6 +858,10 @@ function datePickerValue(value) { return value || formatPickerDate(Date.now()) } +function todayPickerDate() { + return formatPickerDate(Date.now()) +} + function formatPickerDate(value) { if (value === undefined || value === null || value === '') return '' const date = parseDate(value) @@ -1002,6 +1193,23 @@ function goBack() { color: #9ca3af; } +.picker-field { + justify-content: space-between; + gap: 12rpx; +} + +.picker-field.is-disabled { + color: #64748b; +} + +.picker-field-text { + flex: 1; + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + .code-row { display: flex; align-items: center; @@ -1161,4 +1369,146 @@ function goBack() { background: #1f4b79; color: #ffffff; } + +.device-picker-popup { + height: 72vh; + background: #ffffff; + border-radius: 22rpx 22rpx 0 0; + overflow: hidden; +} + +.device-picker-header { + position: relative; + height: 92rpx; + display: flex; + align-items: center; + justify-content: center; + border-bottom: 1rpx solid #eef2f7; +} + +.device-picker-title { + font-size: 30rpx; + font-weight: 700; + color: #111827; +} + +.device-picker-close { + position: absolute; + right: 24rpx; + top: 14rpx; + width: 56rpx; + height: 56rpx; + display: flex; + align-items: center; + justify-content: center; + color: #64748b; + font-size: 42rpx; + line-height: 1; +} + +.device-picker-search { + padding: 18rpx 24rpx 14rpx; +} + +.device-search-box { + height: 74rpx; + padding: 0 20rpx; + border-radius: 14rpx; + background: #f8fafc; + display: flex; + align-items: center; + gap: 12rpx; +} + +.device-search-input { + flex: 1; + height: 74rpx; + min-width: 0; + font-size: 28rpx; + color: #111827; +} + +.device-search-clear { + width: 44rpx; + height: 44rpx; + border-radius: 50%; + background: #e2e8f0; + color: #64748b; + display: flex; + align-items: center; + justify-content: center; + font-size: 30rpx; + line-height: 1; +} + +.device-picker-scroll { + height: calc(72vh - 184rpx); + padding-bottom: calc(16rpx + env(safe-area-inset-bottom)); + box-sizing: border-box; +} + +.device-picker-item { + margin: 0 24rpx 14rpx; + padding: 20rpx; + border-radius: 16rpx; + background: #f8fafc; + border: 2rpx solid transparent; + display: flex; + align-items: center; + justify-content: space-between; + gap: 16rpx; +} + +.device-picker-item.is-selected { + background: #eef6ff; + border-color: #bfdbfe; +} + +.device-item-main { + flex: 1; + min-width: 0; +} + +.device-item-title { + display: flex; + align-items: center; + gap: 12rpx; + min-width: 0; +} + +.device-item-code { + flex-shrink: 0; + max-width: 52%; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + font-size: 28rpx; + font-weight: 700; + color: #111827; +} + +.device-item-name { + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + font-size: 28rpx; + color: #334155; +} + +.device-item-meta { + margin-top: 8rpx; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + font-size: 24rpx; + color: #64748b; +} + +.device-picker-empty { + padding: 72rpx 24rpx; + text-align: center; + color: #94a3b8; + font-size: 28rpx; +}