Files
shanxia2.0/miniprogram/pages/pl/plan/detail.vue
T
2026-07-18 09:14:02 +08:00

191 lines
7.0 KiB
Vue

<script setup>
import { ref, computed } from 'vue'
import { onLoad, onShow } from '@dcloudio/uni-app'
import { getPlan, getPlanArrangeInfo, archivePlan } from '@/api/pl'
import {
dash,
planTypeLabel,
followStatusLabel,
transportStatusLabel,
archiveStatusLabel,
planCardSub,
formatQty
} from '@/utils/plLabels'
import { PAGE_PATHS } from '@/config/api'
const id = ref('')
const readonly = ref(false)
const detail = ref(null)
const arrange = ref(null)
const acting = ref(false)
const phaseFollow = computed(() => followStatusLabel(detail.value?.followStatus))
const phaseTransport = computed(() => transportStatusLabel(detail.value?.transportStatus))
const phaseArchive = computed(() => archiveStatusLabel(detail.value?.archiveStatus))
const products = computed(() =>
Array.isArray(detail.value?.products) ? detail.value.products : []
)
const canArrange = computed(
() =>
!readonly.value &&
detail.value?.transportStatus === 'pending' &&
detail.value?.followStatus !== 'cancelled' &&
detail.value?.followStatus !== 'completed'
)
const canArchive = computed(
() =>
!readonly.value &&
detail.value?.archiveStatus !== 'archived' &&
detail.value?.transportStatus === 'arranged' &&
detail.value?.followStatus !== 'cancelled'
)
const goBack = () => {
uni.navigateBack({
fail: () =>
uni.navigateTo({
url: readonly.value ? PAGE_PATHS.plArchiveList : PAGE_PATHS.plPlanList
})
})
}
const goArrange = () =>
uni.navigateTo({ url: `${PAGE_PATHS.plPlanArrange}?id=${id.value}` })
const load = async () => {
if (!id.value) return
try {
detail.value = await getPlan(id.value)
if (detail.value?.transportStatus === 'arranged') {
try {
arrange.value = await getPlanArrangeInfo(id.value)
} catch {
arrange.value = null
}
} else {
arrange.value = null
}
} catch (e) {
uni.showToast({ title: e.message || '加载失败', icon: 'none' })
}
}
const onArchive = () => {
const list = products.value
if (!list.length) {
uni.showToast({ title: '无产品明细,无法归档', icon: 'none' })
return
}
uni.showModal({
title: '确认归档',
content: '将按计划数量写入实际数量并归档。复杂调整请在 Web 端操作。',
success: async (r) => {
if (!r.confirm) return
acting.value = true
try {
await archivePlan(id.value, {
products: list.map((p) => ({
id: p.id,
actualQty: Number(p.actualQty != null ? p.actualQty : p.quantity) || 0
}))
})
uni.showToast({ title: '已归档', icon: 'success' })
load()
} catch (e) {
uni.showToast({ title: e.message || '归档失败', icon: 'none' })
} finally {
acting.value = false
}
}
})
}
onLoad((q) => {
id.value = q?.id || ''
readonly.value = q?.readonly === '1' || q?.mode === 'archive'
})
onShow(() => load())
</script>
<template>
<view class="mp-page mp-page--sub mp-page--with-bottom">
<view class="mp-header mp-header--pl">
<text class="mp-subpage-back" @click="goBack">← 返回</text>
<text class="mp-header__title" style="margin-top: 16rpx">
{{ readonly ? '归档详情' : '计划详情' }}
</text>
</view>
<view v-if="detail" class="mp-detail-hero">
<view class="mp-detail-hero__top">
<view>
<text class="mp-detail-hero__title">{{ dash(detail.planNo) }}</text>
<text class="mp-detail-hero__sub">{{ planCardSub(detail) }}</text>
</view>
<text class="mp-tag" :class="'mp-tag--' + phaseTransport.tag">{{ phaseTransport.text }}</text>
</view>
<view class="mp-detail-hero__meta">
<text class="mp-detail-hero__chip">{{ planTypeLabel(detail.planType) }}</text>
<text class="mp-detail-hero__chip">{{ phaseFollow.text }}</text>
<text class="mp-detail-hero__chip">{{ phaseArchive.text }}</text>
</view>
</view>
<view class="mp-block">
<text class="mp-block__title">基本信息</text>
<view class="mp-block__panel">
<view class="mp-kv"><text class="mp-kv__label">计划日期</text><text class="mp-kv__value">{{ dash(detail?.planDate) }}</text></view>
<view class="mp-kv"><text class="mp-kv__label">客户</text><text class="mp-kv__value">{{ dash(detail?.customerName) }}</text></view>
<view class="mp-kv"><text class="mp-kv__label">供应商</text><text class="mp-kv__value">{{ dash(detail?.supplierName) }}</text></view>
<view class="mp-kv"><text class="mp-kv__label">取货地址</text><text class="mp-kv__value">{{ dash(detail?.pickupAddress) }}</text></view>
<view class="mp-kv"><text class="mp-kv__label">送货地址</text><text class="mp-kv__value">{{ dash(detail?.destinationAddress) }}</text></view>
<view class="mp-kv"><text class="mp-kv__label">联系人</text><text class="mp-kv__value">{{ dash(detail?.contactName) }} {{ dash(detail?.contactPhone) }}</text></view>
<view class="mp-kv"><text class="mp-kv__label">跟进人</text><text class="mp-kv__value">{{ dash(detail?.followUserName) }}</text></view>
<view class="mp-kv"><text class="mp-kv__label">备注</text><text class="mp-kv__value">{{ dash(detail?.remark) }}</text></view>
</view>
</view>
<view class="mp-block">
<text class="mp-block__title">产品明细</text>
<mp-empty v-if="!products.length" compact title="暂无明细" />
<view v-else class="mp-block__panel">
<view v-for="p in products" :key="p.id" class="mp-kv">
<text class="mp-kv__label">{{ dash(p.productName) }}</text>
<text class="mp-kv__value">{{ formatQty(p.quantity, p.unit) }}</text>
</view>
</view>
</view>
<view v-if="detail?.transportStatus === 'arranged'" class="mp-block">
<text class="mp-block__title">运输安排</text>
<view class="mp-block__panel">
<view class="mp-kv">
<text class="mp-kv__label">车牌</text>
<text class="mp-kv__value">{{ dash(arrange?.licensePlate || detail.licensePlate) }}</text>
</view>
<view class="mp-kv">
<text class="mp-kv__label">司机</text>
<text class="mp-kv__value">{{ dash(detail.driverName) }}</text>
</view>
<view class="mp-kv">
<text class="mp-kv__label">承运商</text>
<text class="mp-kv__value">{{ dash(detail.carrierName) }}</text>
</view>
<view v-if="arrange?.driverUrl" class="mp-kv">
<text class="mp-kv__label">司机扫码</text>
<text class="mp-kv__value">已生成链接</text>
</view>
</view>
</view>
<view v-if="canArrange || canArchive" class="mp-bottom-bar">
<button v-if="canArrange" class="mp-btn mp-btn--primary mp-btn--block" :disabled="acting" @click="goArrange">
安排运输
</button>
<button v-if="canArchive" class="mp-btn mp-btn--primary mp-btn--block" :disabled="acting" @click="onArchive">
归档
</button>
</view>
</view>
</template>