import type { Plan, PlanProduct, TransportArrangeResult, TransportInventoryLink, TransportSiteLink } from '@/api/pl' import { planApi } from '@/api/pl' import DbPoundSlipQrcodeDialog from '@/components/business/db/DbPoundSlipQrcodeDialog.vue' import { useDictStore } from '@/store/modules/dict' import { pickBxData } from '@/utils/http/bx' import { formatPlanProductListText } from '@/utils/pl-display' import { buildDriverSummaryFromPlan, buildDriverStopBlocksFromPlan, buildPoundQrcodeLinksFromInventoryRecords } from '@/utils/pl-driver-summary' import { buildSiteSummaryFromInventoryLinks, buildSiteStopBlocksFromPlan } from '@/utils/db-site-summary' import { copyTextToClipboard } from '@/utils/clipboard' import { isInMiniProgramWebView } from '@/utils/mp-webview' export function usePlPlanArrangeActions() { const dictStore = useDictStore() const plan = ref | null>(null) const products = ref([]) const driverUrl = ref('') const licensePlate = ref('') const siteRecords = ref([]) const inventoryRecords = ref([]) const qrcodeVisible = ref(false) const selectedInventoryId = ref('') const selectedInventoryUnit = ref('') const qrcodeDialogRef = ref | null>(null) const poundQrcodePickVisible = ref(false) const poundQrcodePickRecords = ref([]) const showArrangeActions = computed(() => Boolean( plan.value?.transportStatus === 'arranged' && plan.value?.followStatus !== 'cancelled' && (driverUrl.value || siteRecords.value.length || inventoryRecords.value.length) ) ) const hasSiteInventoryRecords = computed(() => siteRecords.value.length > 0) const hasSiteLinks = computed(() => siteRecords.value.some((item) => Boolean(item.siteUrl?.trim())) ) /** 勾选「生成地磅单」后才会关联 db_purchase_plan,磅单二维码仅针对这类记录 */ const dibangInventoryRecords = computed(() => inventoryRecords.value.filter((item) => Boolean(item.dbPurchasePlanId?.trim())) ) const hasDibangInventoryRecords = computed(() => dibangInventoryRecords.value.length > 0) function getUnitLabel(unit: string) { return dictStore.getLabel('crm_product_unit', unit) || unit } function resetArrangeActions() { plan.value = null products.value = [] driverUrl.value = '' licensePlate.value = '' siteRecords.value = [] inventoryRecords.value = [] qrcodeVisible.value = false selectedInventoryId.value = '' selectedInventoryUnit.value = '' poundQrcodePickVisible.value = false poundQrcodePickRecords.value = [] } function applyArrangeResult( result: TransportArrangeResult, contextPlan?: Partial, contextProducts?: PlanProduct[] ) { if (contextPlan) plan.value = contextPlan if (contextProducts) products.value = contextProducts driverUrl.value = result.driverUrl || '' licensePlate.value = result.licensePlate || '' siteRecords.value = result.siteRecords || [] inventoryRecords.value = result.inventoryRecords || [] } async function loadArrangeActions(planId: string, existingPlan?: Plan | null) { resetArrangeActions() const data = existingPlan || pickBxData(await planApi.get(planId)) if (!data) return plan.value = data products.value = Array.isArray(data.products) ? data.products : [] if (data.transportStatus !== 'arranged' || data.followStatus === 'cancelled') { return } const infoRes = await planApi.getArrangeInfo(planId) const info = pickBxData<{ driverUrl?: string driver_url?: string licensePlate?: string license_plate?: string siteRecords?: TransportSiteLink[] site_records?: TransportSiteLink[] inventoryRecords?: TransportInventoryLink[] inventory_records?: TransportInventoryLink[] }>(infoRes) driverUrl.value = info?.driverUrl || info?.driver_url || '' licensePlate.value = info?.licensePlate || info?.license_plate || data.licensePlate || '' siteRecords.value = info?.siteRecords || info?.site_records || [] inventoryRecords.value = info?.inventoryRecords || info?.inventory_records || [] } function buildPlanSummaryText(options?: { startPoint?: string endPoint?: string pickupAddress?: string pickupLng?: number | null pickupLat?: number | null destinationAddress?: string destinationLng?: number | null destinationLat?: number | null pickupContact?: string pickupNotes?: string deliveryContact?: string destinationNotes?: string licensePlate?: string driverUrl?: string poundQrcodeLinks?: { label: string; url: string }[] }) { return buildDriverSummaryFromPlan({ plan: plan.value || undefined, planNo: plan.value?.planNo, licensePlate: options?.licensePlate || licensePlate.value || plan.value?.licensePlate, pickupTime: plan.value?.pickupTime, planDate: plan.value?.planDate, personnelSummary: plan.value?.personnelSummary, driverUrl: options?.driverUrl || driverUrl.value, products: products.value, getUnitLabel, stopBlocks: buildDriverStopBlocksFromPlan(plan.value, getUnitLabel), ...options, poundQrcodeLinks: options?.poundQrcodeLinks ?? buildPoundQrcodeLinksFromInventoryRecords(dibangInventoryRecords.value) }) } async function copyDriverUrl(url?: string) { const target = url || driverUrl.value if (!target) return const ok = await copyTextToClipboard(target) if (ok) ElMessage.success('已复制司机链接') else ElMessage.error('复制失败') } function resolveSiteRecordProductText(record: TransportSiteLink) { const product = products.value.find((item) => item.id === record.planProductId) if (product) { return formatPlanProductListText(product, getUnitLabel, { usePlannedQuantity: true }) } return record.productName || '—' } function buildSiteSummaryText(options?: { licensePlate?: string }) { return buildSiteSummaryFromInventoryLinks({ planNo: plan.value?.planNo, licensePlate: options?.licensePlate || licensePlate.value || plan.value?.licensePlate, pickupTime: plan.value?.pickupTime, planDate: plan.value?.planDate, records: siteRecords.value.map((record) => ({ ...record, productText: resolveSiteRecordProductText(record) })), stopBlocks: buildSiteStopBlocksFromPlan(plan.value, getUnitLabel) }) } async function copySiteSummary(options?: { licensePlate?: string }) { if (!hasSiteLinks.value) { ElMessage.warning('暂无现场链接') return } const ok = await copyTextToClipboard(buildSiteSummaryText(options)) if (ok) ElMessage.success('已复制给现场') else ElMessage.error('复制失败') } async function copyPlanSummary(options?: { startPoint?: string endPoint?: string pickupAddress?: string pickupLng?: number | null pickupLat?: number | null destinationAddress?: string destinationLng?: number | null destinationLat?: number | null pickupContact?: string pickupNotes?: string deliveryContact?: string destinationNotes?: string licensePlate?: string driverUrl?: string poundQrcodeLinks?: { label: string; url: string }[] }) { const ok = await copyTextToClipboard(buildPlanSummaryText(options)) if (ok) ElMessage.success('已复制给司机') else ElMessage.error('复制失败') } function resolveInventoryUnitLabel(record: TransportInventoryLink) { const product = products.value.find((item) => item.id === record.planProductId) const unitRaw = product?.unit || '' return unitRaw ? getUnitLabel(unitRaw) : '吨' } async function copyPoundSlipQrcodeForRecord( record: TransportInventoryLink, options?: { preview?: boolean } ) { selectedInventoryId.value = record.dbInventoryId selectedInventoryUnit.value = resolveInventoryUnitLabel(record) qrcodeVisible.value = true await nextTick() if (options?.preview) { return } await qrcodeDialogRef.value?.copyQrcodeImage() } async function copyPoundSlipQrcode() { const records = dibangInventoryRecords.value if (!records.length) { ElMessage.warning('未勾选生成地磅单,暂无磅单二维码') return } // 小程序 webview 内与出入库一致:弹窗预览供截图(不自动复制图片) const previewOnly = isInMiniProgramWebView() if (records.length === 1) { await copyPoundSlipQrcodeForRecord(records[0], { preview: previewOnly }) return } poundQrcodePickRecords.value = records poundQrcodePickVisible.value = true } async function handlePoundQrcodePickCopy(record: TransportInventoryLink) { await copyPoundSlipQrcodeForRecord(record, { preview: isInMiniProgramWebView() }) } async function handlePoundQrcodePickPreview(record: TransportInventoryLink) { await copyPoundSlipQrcodeForRecord(record, { preview: true }) } return { plan, products, driverUrl, licensePlate, siteRecords, inventoryRecords, qrcodeVisible, selectedInventoryId, selectedInventoryUnit, qrcodeDialogRef, poundQrcodePickVisible, poundQrcodePickRecords, showArrangeActions, hasSiteInventoryRecords, hasSiteLinks, dibangInventoryRecords, hasDibangInventoryRecords, resetArrangeActions, applyArrangeResult, loadArrangeActions, buildPlanSummaryText, buildSiteSummaryText, copyDriverUrl, copySiteSummary, copyPlanSummary, copyPoundSlipQrcode, copyPoundSlipQrcodeForRecord, handlePoundQrcodePickCopy, handlePoundQrcodePickPreview } }