Files
shanxia2.0/web-admin/src/composables/usePlPlanArrangeActions.ts
T
2026-10-02 09:28:00 +08:00

295 lines
9.7 KiB
TypeScript

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<Partial<Plan> | null>(null)
const products = ref<PlanProduct[]>([])
const driverUrl = ref('')
const licensePlate = ref('')
const siteRecords = ref<TransportSiteLink[]>([])
const inventoryRecords = ref<TransportInventoryLink[]>([])
const qrcodeVisible = ref(false)
const selectedInventoryId = ref('')
const selectedInventoryUnit = ref('')
const qrcodeDialogRef = ref<InstanceType<typeof DbPoundSlipQrcodeDialog> | null>(null)
const poundQrcodePickVisible = ref(false)
const poundQrcodePickRecords = ref<TransportInventoryLink[]>([])
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<Plan>,
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<Plan>(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
}
}