295 lines
9.7 KiB
TypeScript
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
|
|
}
|
|
}
|