import { ref, reactive, computed } from 'vue' import { useI18n } from 'vue-i18n' import { $baseMessage } from '@/utils' import { resolveOmsAssetUrl } from '@/api/oms/common' import { printLabelLookup, type PrintLabelBindItem, type PrintLabelLookupCandidate, type PrintLabelLookupResult, } from '@/api/operation' import { useOperationScanFocus } from '@/composables/useOperationScanFocus' export function normalizeFulfillBindLookup(raw: any): PrintLabelLookupResult { return { matched: raw?.matched ?? raw?.Matched, matchedNumberType: raw?.matchedNumberType ?? raw?.MatchedNumberType, warnings: raw?.warnings ?? raw?.Warnings ?? [], orderId: raw?.orderId ?? raw?.OrderId, serviceNo: raw?.serviceNo ?? raw?.ServiceNo, clientNo: raw?.clientNo ?? raw?.ClientNo, trackNo: raw?.trackNo ?? raw?.TrackNo, productCode: raw?.productCode ?? raw?.ProductCode, salesProductName: raw?.salesProductName ?? raw?.SalesProductName, destinationCountry: raw?.destinationCountry ?? raw?.DestinationCountry, destinationCountryName: raw?.destinationCountryName ?? raw?.DestinationCountryName, orderStatus: raw?.orderStatus ?? raw?.OrderStatus, orderStatusName: raw?.orderStatusName ?? raw?.OrderStatusName, warehouseId: raw?.warehouseId ?? raw?.WarehouseId, warehouseName: raw?.warehouseName ?? raw?.WarehouseName, bindStatus: raw?.bindStatus ?? raw?.BindStatus ?? 'none', bindQtyTotal: raw?.bindQtyTotal ?? raw?.BindQtyTotal ?? 0, bindLineCount: raw?.bindLineCount ?? raw?.BindLineCount ?? 0, bindItems: (raw?.bindItems ?? raw?.BindItems ?? []).map((item: any) => ({ bindType: item.bindType ?? item.BindType, skuCode: item.skuCode ?? item.SkuCode, skuName: item.skuName ?? item.SkuName, trackingNo: item.trackingNo ?? item.TrackingNo, productName: item.productName ?? item.ProductName, attributeText: item.attributeText ?? item.AttributeText, imageUrl: item.imageUrl ?? item.ImageUrl, binCode: item.binCode ?? item.BinCode, bindQty: item.bindQty ?? item.BindQty, status: item.status ?? item.Status, })), candidates: (raw?.candidates ?? raw?.Candidates ?? []).map((c: any) => ({ orderId: c.orderId ?? c.OrderId, serviceNo: c.serviceNo ?? c.ServiceNo, clientNo: c.clientNo ?? c.ClientNo, trackNo: c.trackNo ?? c.TrackNo, orderStatusName: c.orderStatusName ?? c.OrderStatusName, bindQtyTotal: c.bindQtyTotal ?? c.BindQtyTotal, matchedHint: c.matchedHint ?? c.MatchedHint, })), } } export function useFulfillBindLookup(pageKey: string, defaultNumberType = 'auto') { const { t } = useI18n() const scanInputRef = ref<{ focus?: () => void } | null>(null) const { keepScanFocus, maybeRefocusScan } = useOperationScanFocus(pageKey, scanInputRef) const formData = reactive({ scanNumber: '', numberType: defaultNumberType, }) const lookingUp = ref(false) const lookup = ref(null) const candidates = ref([]) const bindItems = computed(() => lookup.value?.bindItems || []) const hasNoneBindWarning = computed(() => (lookup.value?.warnings || []).includes('noneBind')) const canConfirmPick = computed( () => Boolean(lookup.value?.matched && lookup.value?.bindStatus === 'bound'), ) const matchedTypeLabel = computed(() => { const type = lookup.value?.matchedNumberType || '' const key = `operation.printLabel.numberType.${type === 'customerorder' ? 'customerOrder' : type}` const text = t(key) return text === key ? type : text }) const bindStatusLabel = computed(() => { const code = lookup.value?.bindStatus || 'none' const key = `order.myOrders.stockBind.status.${code}` const text = t(key) return text === key ? code : text }) const bindStatusColor = computed(() => { const code = lookup.value?.bindStatus if (code === 'deducted') return 'red' if (code === 'bound') return 'green' return 'gray' }) function itemImage(item: PrintLabelBindItem) { return resolveOmsAssetUrl(item.imageUrl || '') || '' } function bindPrimaryName(item: PrintLabelBindItem) { if (item.bindType === 'parcelItem') { return item.productName || item.skuName || item.trackingNo || '-' } return item.skuName || item.skuCode || '-' } function bindCode(item: PrintLabelBindItem) { return item.skuCode || '' } async function handleLookup(silent = false) { const number = formData.scanNumber.trim() if (!number) { if (!silent) { $baseMessage(t('operation.fulfillPack.message.numberRequired'), 'warning') } maybeRefocusScan() return false } lookingUp.value = true lookup.value = null candidates.value = [] try { const raw = await printLabelLookup({ number, numberType: formData.numberType || 'auto', }) const data = normalizeFulfillBindLookup(raw) if (data.candidates?.length && !data.matched) { candidates.value = data.candidates lookup.value = null if (!silent) { $baseMessage(t('operation.printLabel.preview.multiMatch'), 'warning') } return false } lookup.value = data candidates.value = [] if (formData.numberType === 'auto' && data.matchedNumberType) { const mt = data.matchedNumberType formData.numberType = mt === 'customerorder' ? 'customerOrder' : mt === 'auto' ? 'auto' : mt } if (!silent) { $baseMessage(t('operation.fulfillPack.message.lookupSuccess'), 'success') } return true } catch (e: any) { lookup.value = null candidates.value = [] if (!silent) { $baseMessage(e?.message || t('operation.fulfillPack.message.lookupFailed'), 'error') } return false } finally { lookingUp.value = false maybeRefocusScan() } } async function selectCandidate(c: PrintLabelLookupCandidate) { formData.scanNumber = c.serviceNo || c.clientNo || String(c.orderId) formData.numberType = c.serviceNo ? 'waybill' : 'customerOrder' candidates.value = [] await handleLookup() } async function lookupByWaybill(serviceNo: string) { formData.scanNumber = serviceNo formData.numberType = 'waybill' return handleLookup(true) } async function lookupByDomesticTracking(trackingNo: string) { formData.scanNumber = trackingNo formData.numberType = 'parcel' return handleLookup(true) } function handleReset() { formData.scanNumber = '' formData.numberType = defaultNumberType lookup.value = null candidates.value = [] maybeRefocusScan() } async function onScanEnter() { await handleLookup() } return { scanInputRef, keepScanFocus, formData, lookingUp, lookup, candidates, bindItems, hasNoneBindWarning, canConfirmPick, matchedTypeLabel, bindStatusLabel, bindStatusColor, itemImage, bindPrimaryName, bindCode, handleLookup, selectCandidate, lookupByWaybill, lookupByDomesticTracking, handleReset, onScanEnter, } }