优化
This commit is contained in:
@@ -1,9 +1,12 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, reactive, ref, watch } from 'vue'
|
||||
import { ChevronDown, ChevronUp, ClipboardList, Eye, RefreshCw, SlidersHorizontal } from '@lucide/vue'
|
||||
import { computed, nextTick, onMounted, reactive, ref, watch } from 'vue'
|
||||
import { ElMessage, type FormInstance, type FormRules } from 'element-plus'
|
||||
import { ChevronDown, ChevronUp, CircleCheck, ClipboardList, Eye, RefreshCw, SlidersHorizontal } from '@lucide/vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import {
|
||||
getPlatformUnifiedOrder,
|
||||
approvePlatformUnifiedOrder,
|
||||
assignPlatformUnifiedOrderCarrier,
|
||||
listPlatformUnifiedOrders,
|
||||
unifiedOrderStatusOptions,
|
||||
type UnifiedOrderDetailResource,
|
||||
@@ -12,9 +15,11 @@ import {
|
||||
} from '@/modules/unified-orders/api/unifiedOrders'
|
||||
import { useAccessPreviewStore } from '@/core/access/accessPreview'
|
||||
import { listPlatformTenants, type PlatformTenantResource } from '@/modules/iam'
|
||||
import { listPlatformPartners, type PlatformPartnerResource } from '@/modules/master-data'
|
||||
import { listAllPlatformWarehouses, type PlatformWarehouseSummaryResource } from '@/modules/warehouses'
|
||||
import { getApiErrorDetails, type ApiErrorDetails } from '@/shared/api/http'
|
||||
import ApiErrorMeta from '@/shared/components/ApiErrorMeta.vue'
|
||||
import AppDialog from '@/shared/components/AppDialog.vue'
|
||||
import AppDrawer from '@/shared/components/AppDrawer.vue'
|
||||
import DataTable, { type DataTableColumn } from '@/shared/components/DataTable.vue'
|
||||
import LoadErrorState from '@/shared/components/LoadErrorState.vue'
|
||||
@@ -62,8 +67,20 @@ const detail = ref<UnifiedOrderDetailResource | null>(null)
|
||||
const detailLoading = ref(false)
|
||||
const detailError = ref<ApiErrorDetails | null>(null)
|
||||
const detailOpen = ref(false)
|
||||
const approvalOpen = ref(false)
|
||||
const approvalLoadingId = ref('')
|
||||
const approvalSaving = ref(false)
|
||||
const carrierLoading = ref(false)
|
||||
const carrierLoadError = ref<ApiErrorDetails | null>(null)
|
||||
const carriers = ref<PlatformPartnerResource[]>([])
|
||||
const approvalOrder = ref<UnifiedOrderResource | null>(null)
|
||||
const carrierFormRef = ref<FormInstance>()
|
||||
const carrierForm = reactive({ carrierId: '' })
|
||||
const showAdvancedFilters = ref(false)
|
||||
const filters = ref<FilterModel>(emptyFilters())
|
||||
const carrierRules: FormRules = {
|
||||
carrierId: [{ required: true, message: '请选择承运商', trigger: 'change' }],
|
||||
}
|
||||
|
||||
const orderId = computed(() => typeof route.params.orderId === 'string' ? route.params.orderId : '')
|
||||
|
||||
@@ -208,10 +225,85 @@ const nextActionLabels: Record<string, string> = {
|
||||
}
|
||||
|
||||
function nextActionBoundary(row: UnifiedOrderResource) {
|
||||
if (row.nextAction === 'APPROVE_ORDER') return ''
|
||||
const label = row.nextAction ? nextActionLabels[row.nextAction] || row.nextAction : ''
|
||||
return label ? `待接入:${label}` : ''
|
||||
}
|
||||
|
||||
function canApprove(row: UnifiedOrderResource) {
|
||||
return row.mainStatus === 'PENDING_REVIEW' && row.nextAction === 'APPROVE_ORDER' && can('platform:order:approve')
|
||||
}
|
||||
|
||||
async function loadCarriers() {
|
||||
carrierLoading.value = true
|
||||
carrierLoadError.value = null
|
||||
try {
|
||||
const records: PlatformPartnerResource[] = []
|
||||
let currentPage = 1
|
||||
let pages = 1
|
||||
do {
|
||||
const result = await listPlatformPartners({ page: currentPage, size: 200, type: 'CARRIER', status: 'ACTIVE' })
|
||||
records.push(...result.records)
|
||||
pages = Math.max(1, result.pages)
|
||||
currentPage += 1
|
||||
} while (currentPage <= pages)
|
||||
carriers.value = records.filter((item) => item.status === 'ACTIVE' && item.types.includes('CARRIER'))
|
||||
} catch (error) {
|
||||
carriers.value = []
|
||||
carrierLoadError.value = getApiErrorDetails(error, '承运商列表加载失败,请稍后重试')
|
||||
} finally {
|
||||
carrierLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function openApproval(row: UnifiedOrderResource) {
|
||||
if (!canApprove(row)) return
|
||||
approvalOrder.value = row
|
||||
const currentSnapshot = detail.value?.summary.orderId === row.orderId ? detail.value.order.order : null
|
||||
carrierForm.carrierId = currentSnapshot?.carrierId == null ? '' : String(currentSnapshot.carrierId)
|
||||
approvalOpen.value = true
|
||||
await nextTick()
|
||||
carrierFormRef.value?.clearValidate()
|
||||
await loadCarriers()
|
||||
}
|
||||
|
||||
function closeApproval() {
|
||||
if (approvalSaving.value) return
|
||||
approvalOpen.value = false
|
||||
approvalOrder.value = null
|
||||
carrierForm.carrierId = ''
|
||||
}
|
||||
|
||||
async function assignCarrierAndApprove() {
|
||||
if (!approvalOrder.value) return
|
||||
const valid = await carrierFormRef.value?.validate().catch(() => false)
|
||||
if (!valid) return
|
||||
const carrier = carriers.value.find((item) => String(item.id) === carrierForm.carrierId)
|
||||
if (!carrier) {
|
||||
ElMessage.error('所选承运商已停用或不存在,请重新选择')
|
||||
return
|
||||
}
|
||||
const order = approvalOrder.value
|
||||
const orderId = String(order.orderId)
|
||||
const tenantId = String(order.tenantId)
|
||||
approvalSaving.value = true
|
||||
approvalLoadingId.value = orderId
|
||||
try {
|
||||
await assignPlatformUnifiedOrderCarrier(tenantId, orderId, String(carrier.id))
|
||||
await approvePlatformUnifiedOrder(tenantId, orderId)
|
||||
approvalOpen.value = false
|
||||
await loadOrders()
|
||||
if (detail.value?.summary.orderId === order.orderId) await loadDetail()
|
||||
approvalOrder.value = null
|
||||
ElMessage.success('承运商已指派,订单审核通过')
|
||||
} catch (error) {
|
||||
ElMessage.error(getApiErrorDetails(error, '承运商指派或订单审核失败,请稍后重试').message)
|
||||
} finally {
|
||||
approvalSaving.value = false
|
||||
approvalLoadingId.value = ''
|
||||
}
|
||||
}
|
||||
|
||||
async function loadCustomers() {
|
||||
customerLoading.value = true
|
||||
customerLoadError.value = null
|
||||
@@ -397,6 +489,7 @@ onMounted(() => {
|
||||
<template #actions="{ row }">
|
||||
<div class="row-actions">
|
||||
<el-button link type="primary" @click="openDetail(row as UnifiedOrderResource)"><Eye :size="15" />详情</el-button>
|
||||
<el-button v-if="canApprove(row as UnifiedOrderResource)" link type="success" :loading="approvalLoadingId === String((row as UnifiedOrderResource).orderId)" @click="openApproval(row as UnifiedOrderResource)"><CircleCheck :size="15" />审核通过</el-button>
|
||||
<el-button v-if="(row as UnifiedOrderResource).mainStatus === 'WAIT_MEASURE' && (row as UnifiedOrderResource).inboundId && can('platform:firstleg:inbound:view')" link type="primary" @click="openMeasurement(row as UnifiedOrderResource)">测量作业</el-button>
|
||||
<el-button v-if="workflowEligible(row as UnifiedOrderResource) && can('platform:firstleg:inbound:view')" link type="primary" @click="openWorkflow(row as UnifiedOrderResource)"><ClipboardList :size="15" />后半程作业</el-button>
|
||||
<span v-if="nextActionBoundary(row as UnifiedOrderResource)" class="action-boundary">{{ nextActionBoundary(row as UnifiedOrderResource) }}</span>
|
||||
@@ -418,13 +511,29 @@ onMounted(() => {
|
||||
<LoadErrorState v-if="detailError" title="订单详情暂时无法加载" :message="detailError.message" :loading="detailLoading" @retry="loadDetail"><template #details><ApiErrorMeta :details="detailError" /></template></LoadErrorState>
|
||||
<div v-else-if="detail" class="detail-content">
|
||||
<section class="detail-summary" aria-label="订单概览">
|
||||
<div class="detail-summary__header"><div><span class="eyebrow">订单主状态</span><h2>{{ detail.summary.orderNo || `订单 ${detail.summary.orderId}` }}</h2></div><div class="detail-summary__actions"><StatusBadge :status="statusLabel(detail.summary.mainStatus)" /><el-button v-if="workflowEligible(detail.summary) && can('platform:firstleg:inbound:view')" type="primary" plain @click="openWorkflow(detail.summary)"><ClipboardList :size="15" />后半程作业</el-button></div></div>
|
||||
<dl class="detail-grid"><div><dt>客户</dt><dd>{{ customerName(detail.summary) }}</dd></div><div><dt>入仓码</dt><dd>{{ detail.summary.inboundCode || '—' }}</dd></div><div><dt>平台收货仓</dt><dd>{{ detail.summary.receivingWarehouseName || '—' }}</dd></div><div><dt>目的仓</dt><dd>{{ detail.summary.destinationWarehouseName || '—' }}</dd></div><div><dt>下一步</dt><dd>{{ nextActionBoundary(detail.summary) || '—' }}</dd></div><div><dt>最近更新</dt><dd>{{ formatDate(detail.summary.updatedAt) }}</dd></div></dl>
|
||||
<div class="detail-summary__header"><div><span class="eyebrow">订单主状态</span><h2>{{ detail.summary.orderNo || `订单 ${detail.summary.orderId}` }}</h2></div><div class="detail-summary__actions"><StatusBadge :status="statusLabel(detail.summary.mainStatus)" /><el-button v-if="canApprove(detail.summary)" type="success" plain :loading="approvalLoadingId === String(detail.summary.orderId)" @click="openApproval(detail.summary)"><CircleCheck :size="15" />审核通过</el-button><el-button v-if="workflowEligible(detail.summary) && can('platform:firstleg:inbound:view')" type="primary" plain @click="openWorkflow(detail.summary)"><ClipboardList :size="15" />后半程作业</el-button></div></div>
|
||||
<dl class="detail-grid"><div><dt>客户</dt><dd>{{ customerName(detail.summary) }}</dd></div><div><dt>入仓码</dt><dd>{{ detail.summary.inboundCode || '—' }}</dd></div><div><dt>平台收货仓</dt><dd>{{ detail.summary.receivingWarehouseName || '—' }}</dd></div><div><dt>目的仓</dt><dd>{{ detail.summary.destinationWarehouseName || '—' }}</dd></div><div><dt>承运商</dt><dd>{{ detail.order.order?.carrierName || '待平台指派' }}</dd></div><div><dt>下一步</dt><dd>{{ canApprove(detail.summary) ? '选择承运商并审核' : nextActionBoundary(detail.summary) || '—' }}</dd></div><div><dt>最近更新</dt><dd>{{ formatDate(detail.summary.updatedAt) }}</dd></div></dl>
|
||||
</section>
|
||||
<section class="detail-section" aria-label="状态历史"><h3>状态历史</h3><el-empty v-if="detail.timeline.length === 0" description="暂无状态历史" :image-size="56" /><ol v-else class="history-list"><li v-for="(item, index) in detail.timeline" :key="`${item.source || 'event'}-${item.occurredAt || index}`"><StatusBadge :status="statusLabel(item.mainStatus)" /><div><strong>{{ item.action || item.eventType || '状态更新' }}</strong><p>{{ item.operatorName || '系统' }} · {{ formatDate(item.occurredAt) }}</p><p v-if="item.remark">{{ item.remark }}</p></div></li></ol></section>
|
||||
<section class="detail-section" aria-label="内部备注"><h3>内部备注</h3><el-empty v-if="detail.order.internalNotes.length === 0" description="暂无内部备注" :image-size="56" /><ul v-else class="notes-list"><li v-for="item in detail.order.internalNotes" :key="String(item.id)"><p>{{ item.content }}</p><span>操作人 {{ item.createdBy || '系统' }} · {{ formatDate(item.createdAt) }}</span></li></ul></section>
|
||||
</div>
|
||||
</AppDrawer>
|
||||
|
||||
<AppDialog v-model="approvalOpen" title="选择承运商并审核" confirm-text="指派并审核通过" confirm-type="success" :loading="carrierLoading" :confirm-loading="approvalSaving" :confirm-disabled="Boolean(carrierLoadError) || carriers.length === 0" destroy-on-close @confirm="assignCarrierAndApprove" @cancel="closeApproval">
|
||||
<el-alert title="订单审核通过后将进入履约流程;承运商名称由后端公共主数据生成快照。" type="info" :closable="false" show-icon />
|
||||
<el-alert v-if="carrierLoadError" class="carrier-load-error" type="error" :closable="false" show-icon>
|
||||
<template #title><div class="api-error-alert-title"><span>{{ carrierLoadError.message }}</span><ApiErrorMeta :details="carrierLoadError" /></div></template>
|
||||
<el-button link type="danger" :loading="carrierLoading" @click="loadCarriers">重新加载</el-button>
|
||||
</el-alert>
|
||||
<el-alert v-else-if="!carrierLoading && carriers.length === 0" class="carrier-load-error" title="暂无启用的承运商,请先到往来单位维护承运商主数据。" type="warning" :closable="false" show-icon />
|
||||
<el-form ref="carrierFormRef" :model="carrierForm" :rules="carrierRules" label-position="top" class="carrier-form" :disabled="carrierLoading || approvalSaving || Boolean(carrierLoadError)">
|
||||
<el-form-item label="承运商" prop="carrierId" required>
|
||||
<el-select v-model="carrierForm.carrierId" filterable :loading="carrierLoading" placeholder="请选择启用的承运商">
|
||||
<el-option v-for="carrier in carriers" :key="String(carrier.id)" :label="`${carrier.partnerName}(${carrier.partnerCode})`" :value="String(carrier.id)" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</AppDialog>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
@@ -446,6 +555,9 @@ onMounted(() => {
|
||||
.table-card { margin-top: 16px; overflow: hidden; border: 1px solid var(--cc-border); border-radius: 8px; background: #fff; }
|
||||
.row-actions { display: flex; align-items: center; gap: 4px; white-space: nowrap; }
|
||||
.row-actions :deep(.el-button) { margin-left: 0; }
|
||||
.carrier-form { margin-top: 18px; }
|
||||
.carrier-form :deep(.el-select) { width: 100%; }
|
||||
.carrier-load-error { margin-top: 14px; }
|
||||
.primary-cell { display: grid; min-width: 0; gap: 4px; }
|
||||
.primary-cell strong { overflow-wrap: anywhere; }
|
||||
.primary-cell span { color: var(--cc-muted); font-size: 12px; overflow-wrap: anywhere; }
|
||||
|
||||
Reference in New Issue
Block a user