|
|
@@ -1,18 +1,160 @@
|
|
|
<template>
|
|
|
<MpWebviewShell>
|
|
|
- <p class="mp-wv-placeholder">计划详情加载中…</p>
|
|
|
+ <div v-if="loading" class="mp-wv-empty">加载中…</div>
|
|
|
+ <div v-else-if="errorText" class="mp-wv-empty">{{ errorText }}</div>
|
|
|
+ <template v-else-if="plan">
|
|
|
+ <header class="mp-wv-header">
|
|
|
+ <div class="mp-wv-header__row">
|
|
|
+ <button class="mp-wv-header__back" type="button" @click="goList">← 列表</button>
|
|
|
+ <el-tag :type="statusTagType" size="small">{{ statusLabel }}</el-tag>
|
|
|
+ </div>
|
|
|
+ <h1>{{ plan.planNo || '计划详情' }}</h1>
|
|
|
+ <div class="mp-wv-card__sub" style="color: rgba(255, 255, 255, 0.9)">
|
|
|
+ {{ typeLabel }} · 计划日期 {{ plan.planDate || '—' }}
|
|
|
+ </div>
|
|
|
+ </header>
|
|
|
+
|
|
|
+ <section class="mp-wv-card">
|
|
|
+ <div class="mp-wv-card__meta">送货时间:{{ deliveryTimeText }}</div>
|
|
|
+ <div class="mp-wv-card__meta">车牌:{{ plate }}</div>
|
|
|
+ <div class="mp-wv-card__meta">人员:{{ people }}</div>
|
|
|
+ <div class="mp-wv-card__meta">跟进人:{{ plan.followUserName || '—' }}</div>
|
|
|
+ <div class="mp-wv-card__product">{{ product }}</div>
|
|
|
+ </section>
|
|
|
+
|
|
|
+ <div class="mp-wv-toolbar" style="flex-wrap: wrap">
|
|
|
+ <button v-if="canArrange" class="mp-wv-btn mp-wv-btn--primary" type="button" @click="goArrange">
|
|
|
+ {{ arrangeLabel }}
|
|
|
+ </button>
|
|
|
+ <button v-if="canEdit" class="mp-wv-btn" type="button" @click="goEdit">编辑计划</button>
|
|
|
+ <button class="mp-wv-btn" type="button" @click="goCopy">复制计划</button>
|
|
|
+ <button v-if="canWait" class="mp-wv-btn" type="button" :disabled="acting" @click="doWaitNotice">
|
|
|
+ 等客户通知
|
|
|
+ </button>
|
|
|
+ <button class="mp-wv-btn" type="button" @click="goPrint">打印单据</button>
|
|
|
+ <button v-if="canVoid" class="mp-wv-btn" type="button" :disabled="acting" @click="doVoid">
|
|
|
+ 作废
|
|
|
+ </button>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
</MpWebviewShell>
|
|
|
</template>
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
+ import { computed, onMounted, ref } from 'vue'
|
|
|
+ import { useRoute, useRouter } from 'vue-router'
|
|
|
+ import { ElMessage, ElMessageBox } from 'element-plus'
|
|
|
import MpWebviewShell from '@/layouts/MpWebviewShell.vue'
|
|
|
-</script>
|
|
|
+ import { planApi, type Plan } from '@/api/pl'
|
|
|
+ import { buildPlanCard } from '@/utils/mp-pl-plan-card'
|
|
|
+ import {
|
|
|
+ arrangeTransportLabel,
|
|
|
+ canArrangeTransport,
|
|
|
+ canEditPlan,
|
|
|
+ canVoidPlan,
|
|
|
+ canWaitNotice
|
|
|
+ } from '@/utils/pl-plan-action-guards'
|
|
|
+ import '@/styles/mp-webview.scss'
|
|
|
+
|
|
|
+ const route = useRoute()
|
|
|
+ const router = useRouter()
|
|
|
+ const loading = ref(true)
|
|
|
+ const acting = ref(false)
|
|
|
+ const errorText = ref('')
|
|
|
+ const plan = ref<Plan | null>(null)
|
|
|
+
|
|
|
+ const card = computed(() => (plan.value ? buildPlanCard(plan.value) : null))
|
|
|
+ const statusLabel = computed(() => card.value?.statusLabel || '—')
|
|
|
+ const statusTagType = computed(() => card.value?.statusTagType || 'info')
|
|
|
+ const typeLabel = computed(() => card.value?.typeLabel || '—')
|
|
|
+ const plate = computed(() => card.value?.plate || '—')
|
|
|
+ const people = computed(() => card.value?.people || '—')
|
|
|
+ const product = computed(() => card.value?.product || '—')
|
|
|
+ const deliveryTimeText = computed(() => {
|
|
|
+ const times = plan.value?.deliveryTimes || []
|
|
|
+ if (times.length) return times.join('、')
|
|
|
+ return plan.value?.deliveryTime || '—'
|
|
|
+ })
|
|
|
+ const canArrange = computed(() => (plan.value ? canArrangeTransport(plan.value) : false))
|
|
|
+ const canEdit = computed(() => (plan.value ? canEditPlan(plan.value) : false))
|
|
|
+ const canVoid = computed(() => (plan.value ? canVoidPlan(plan.value) : false))
|
|
|
+ const canWait = computed(() => (plan.value ? canWaitNotice(plan.value) : false))
|
|
|
+ const arrangeLabel = computed(() =>
|
|
|
+ plan.value ? arrangeTransportLabel(plan.value) : '安排运输'
|
|
|
+ )
|
|
|
+
|
|
|
+ async function loadDetail() {
|
|
|
+ const id = String(route.params.id || '')
|
|
|
+ if (!id) {
|
|
|
+ errorText.value = '缺少计划 ID'
|
|
|
+ loading.value = false
|
|
|
+ return
|
|
|
+ }
|
|
|
+ loading.value = true
|
|
|
+ try {
|
|
|
+ const res = await planApi.get(id)
|
|
|
+ plan.value = ((res as { data?: Plan }).data ?? res) as Plan
|
|
|
+ errorText.value = ''
|
|
|
+ } catch (error) {
|
|
|
+ errorText.value = error instanceof Error ? error.message : '加载失败'
|
|
|
+ } finally {
|
|
|
+ loading.value = false
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ function goList() {
|
|
|
+ void router.push('/m/pl/plan')
|
|
|
+ }
|
|
|
+
|
|
|
+ function goEdit() {
|
|
|
+ if (!plan.value) return
|
|
|
+ void router.push(`/m/pl/plan/${plan.value.id}/edit`)
|
|
|
+ }
|
|
|
|
|
|
-<style scoped>
|
|
|
- .mp-wv-placeholder {
|
|
|
- margin: 24px 0 0;
|
|
|
- font-size: 14px;
|
|
|
- color: #6b7b74;
|
|
|
- text-align: center;
|
|
|
+ function goArrange() {
|
|
|
+ if (!plan.value) return
|
|
|
+ void router.push(`/m/pl/plan/${plan.value.id}/arrange`)
|
|
|
}
|
|
|
-</style>
|
|
|
+
|
|
|
+ function goCopy() {
|
|
|
+ if (!plan.value) return
|
|
|
+ void router.push({ path: '/m/pl/plan/new', query: { copyFrom: plan.value.id } })
|
|
|
+ }
|
|
|
+
|
|
|
+ function goPrint() {
|
|
|
+ if (!plan.value) return
|
|
|
+ void router.push(`/m/pl/plan/${plan.value.id}/print`)
|
|
|
+ }
|
|
|
+
|
|
|
+ async function doWaitNotice() {
|
|
|
+ if (!plan.value) return
|
|
|
+ await ElMessageBox.confirm('确认将该计划标记为等客户通知?', '等客户通知', {
|
|
|
+ type: 'warning'
|
|
|
+ })
|
|
|
+ acting.value = true
|
|
|
+ try {
|
|
|
+ const res = await planApi.waitNotice(plan.value.id)
|
|
|
+ plan.value = ((res as { data?: Plan }).data ?? res) as Plan
|
|
|
+ ElMessage.success('已标记为等客户通知')
|
|
|
+ } finally {
|
|
|
+ acting.value = false
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ async function doVoid() {
|
|
|
+ if (!plan.value) return
|
|
|
+ await ElMessageBox.confirm('确认作废该运输计划?此操作不可恢复。', '作废', { type: 'warning' })
|
|
|
+ acting.value = true
|
|
|
+ try {
|
|
|
+ const res = await planApi.cancel(plan.value.id)
|
|
|
+ plan.value = ((res as { data?: Plan }).data ?? res) as Plan
|
|
|
+ ElMessage.success('已作废')
|
|
|
+ } finally {
|
|
|
+ acting.value = false
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ onMounted(() => {
|
|
|
+ void loadDetail()
|
|
|
+ })
|
|
|
+</script>
|