@@ -0,0 +1,493 @@
< script setup lang = "ts" >
import { computed , nextTick , reactive , ref , watch } from 'vue'
import { ElMessage , ElMessageBox } from 'element-plus'
import type { FormInstance , FormRules } from 'element-plus'
import { ArrowLeft , Eye , FilePenLine , PackageCheck } from '@lucide/vue'
import { useRoute , useRouter } from 'vue-router'
import {
acceptPlatformFirstLegOrder ,
addPlatformFirstLegOrderNote ,
approvePlatformFirstLegOrder ,
cancelPlatformFirstLegOrder ,
completePlatformFirstLegOrder ,
getPlatformFirstLegOrder ,
listPlatformFirstLegOrders ,
startPlatformFirstLegOrder ,
updatePlatformFirstLegOrderExpress ,
type PlatformFirstLegOrderDetailResource ,
type PlatformFirstLegOrderResource ,
type PlatformFirstLegOrderStatus ,
} from '@/modules/first-leg-orders/api/platformFirstLegOrders'
import { ApiResponseError , getApiErrorDetails , type ApiErrorDetails } from '@/shared/api/http'
import ApiErrorMeta from '@/shared/components/ApiErrorMeta.vue'
import StatusBadge from '@/shared/components/StatusBadge.vue'
import DataTable , { type DataTableColumn } from '@/shared/components/DataTable.vue'
import QueryToolbar , { type QueryField , type QueryModel } from '@/shared/components/QueryToolbar.vue'
import { useAccessPreviewStore } from '@/core/access/accessPreview'
const PHASE _ONE _TENANT _ID = '10001'
const route = useRoute ( )
const router = useRouter ( )
const access = useAccessPreviewStore ( )
const loadedTenantId = ref ( PHASE _ONE _TENANT _ID )
const orders = ref < PlatformFirstLegOrderResource [ ] > ( [ ] )
const total = ref ( 0 )
const page = ref ( 1 )
const pageSize = ref ( 20 )
const loading = ref ( false )
const actionLoadingId = ref ( '' )
const loadError = ref < ApiErrorDetails | null > ( null )
const detailLoading = ref ( false )
const detail = ref < PlatformFirstLegOrderDetailResource | null > ( null )
const noteDialogOpen = ref ( false )
const expressDialogOpen = ref ( false )
const selectedOrder = ref < PlatformFirstLegOrderResource | null > ( null )
const noteForm = reactive ( { content : '' } )
const noteSaving = ref ( false )
const expressSaving = ref ( false )
const expressFormRef = ref < FormInstance > ( )
const noteFormRef = ref < FormInstance > ( )
const expressForm = reactive ( { expressCompany : '' , expressTrackingNo : '' } )
const filters = ref < QueryModel & {
keyword : string
status : PlatformFirstLegOrderStatus | ' '
dateRange : string [ ]
} > ( {
keyword : '' ,
status : '' ,
dateRange : [ ] ,
} )
const queryFields : QueryField [ ] = [
{ key : 'keyword' , type : 'input' , placeholder : '订单号或客户名称' , props : { 'aria-label' : '订单关键词' } } ,
{
key : 'status' ,
type : 'select' ,
placeholder : '全部状态' ,
props : { 'aria-label' : '订单状态' } ,
options : [
{ value : 'DRAFT' , label : '草稿' } ,
{ value : 'SUBMITTED' , label : '待审核' } ,
{ value : 'APPROVED' , label : '已审核' } ,
{ value : 'ACCEPTED' , label : '已受理' } ,
{ value : 'PROCESSING' , label : '处理中' } ,
{ value : 'COMPLETED' , label : '已完成' } ,
{ value : 'CANCELLED' , label : '已取消' } ,
{ value : 'REJECTED' , label : '已驳回(旧契约)' } ,
] ,
} ,
{ key : 'dateRange' , type : 'daterange' , props : { 'aria-label' : '订单创建日期' } } ,
]
const orderColumns : DataTableColumn [ ] = [
{ key : 'actions' , label : '操作' , minWidth : 200 , fixed : 'left' , slotName : 'actions' } ,
{ key : 'orderInfo' , label : '订单信息' , minWidth : 210 , slotName : 'orderInfo' } ,
{ key : 'customer' , label : '客户' , minWidth : 190 , showOverflowTooltip : true , slotName : 'customer' } ,
{ key : 'transport' , label : '运输方案' , minWidth : 180 , slotName : 'transport' } ,
{ key : 'destination' , label : '目的地' , minWidth : 150 , slotName : 'destination' } ,
{ key : 'receivingWarehouse' , label : '平台收货仓' , minWidth : 180 , slotName : 'receivingWarehouse' } ,
{ key : 'express' , label : '履约快递' , minWidth : 160 , slotName : 'express' } ,
{ key : 'status' , label : '状态' , width : 120 , slotName : 'status' } ,
]
const expressRules : FormRules = {
expressCompany : [ { required : true , message : '请填写快递公司' , trigger : 'blur' } ] ,
expressTrackingNo : [
{ required : true , message : '请填写快递单号' , trigger : 'blur' } ,
{ pattern : /^[A-Za-z0-9-]{4,128}$/ , message : '快递单号格式不正确' , trigger : 'blur' } ,
] ,
}
const noteRules : FormRules = {
content : [ { required : true , message : '请填写内部备注' , trigger : 'blur' } ] ,
}
const tenantReady = computed ( ( ) => loadedTenantId . value === PHASE _ONE _TENANT _ID )
const detailOrderId = computed ( ( ) => {
const orderId = route . query . orderId
return typeof orderId === 'string' && orderId ? orderId : ''
} )
const isDetailPage = computed ( ( ) => Boolean ( detailOrderId . value ) )
const statusOptions : Array < { value : PlatformFirstLegOrderStatus ; label : string } > = [
{ value : 'DRAFT' , label : '草稿' } ,
{ value : 'SUBMITTED' , label : '待审核' } ,
{ value : 'APPROVED' , label : '已审核' } ,
{ value : 'ACCEPTED' , label : '已受理' } ,
{ value : 'PROCESSING' , label : '处理中' } ,
{ value : 'COMPLETED' , label : '已完成' } ,
{ value : 'CANCELLED' , label : '已取消' } ,
{ value : 'REJECTED' , label : '已驳回(旧契约)' } ,
]
function statusLabel ( status : PlatformFirstLegOrderStatus ) {
return statusOptions . find ( ( item ) => item . value === status ) ? . label || status
}
function transportLabel ( order : PlatformFirstLegOrderResource ) {
if ( order . transportMode === 'SEA' ) return order . containerMode === 'FULL_CONTAINER' ? '海运整箱 FCL' : '海运拼箱 LCL'
if ( order . transportMode === 'AIR' ) return '空运'
if ( order . transportMode === 'RAIL' ) return '铁路运输'
return '陆运'
}
function formattedDate ( value : string | null | undefined ) {
return value ? value . replace ( 'T' , ' ' ) : '-'
}
function can ( action : string ) {
return access . can ( action )
}
function canCancel ( order : PlatformFirstLegOrderResource ) {
return can ( 'platform:order:cancel' ) && [ 'DRAFT' , 'SUBMITTED' , 'APPROVED' , 'ACCEPTED' , 'REJECTED' ] . includes ( order . status )
}
function actionLabel ( order : PlatformFirstLegOrderResource ) {
if ( order . status === 'SUBMITTED' && can ( 'platform:order:approve' ) ) return '审核通过'
if ( order . status === 'APPROVED' && can ( 'platform:order:accept' ) ) return '受理订单'
if ( order . status === 'ACCEPTED' && can ( 'platform:order:process' ) ) return '开始处理'
if ( order . status === 'PROCESSING' && can ( 'platform:order:complete' ) ) return '完成订单'
return ''
}
async function loadOrders ( ) {
const targetTenantId = PHASE _ONE _TENANT _ID
loadError . value = null
loading . value = true
try {
const result = await listPlatformFirstLegOrders ( targetTenantId , {
keyword : filters . value . keyword . trim ( ) || undefined ,
status : filters . value . status || undefined ,
createdFrom : filters . value . dateRange . length === 2 ? ` ${ filters . value . dateRange [ 0 ] } T00:00:00 ` : undefined ,
createdTo : filters . value . dateRange . length === 2 ? ` ${ filters . value . dateRange [ 1 ] } T23:59:59 ` : undefined ,
page : page . value ,
size : pageSize . value ,
} )
orders . value = result . records
total . value = result . total
loadedTenantId . value = targetTenantId
} catch ( error ) {
orders . value = [ ]
total . value = 0
loadError . value = getApiErrorDetails ( error , '订单列表加载失败,请检查 Gateway、租户范围和订单权限' )
} finally {
loading . value = false
}
}
function searchOrders ( ) {
if ( page . value !== 1 ) {
page . value = 1
return
}
void loadOrders ( )
}
watch ( [ page , pageSize ] , ( ) => {
void loadOrders ( )
} )
async function openDetail ( order : PlatformFirstLegOrderResource ) {
if ( ! tenantReady . value ) return
selectedOrder . value = order
await router . push ( { path : route . path , query : { ... route . query , orderId : String ( order . id ) } } )
}
async function loadDetail ( orderId : string ) {
if ( ! tenantReady . value ) return
detail . value = null
detailLoading . value = true
try {
detail . value = await getPlatformFirstLegOrder ( loadedTenantId . value , orderId )
selectedOrder . value = detail . value . order
} catch ( error ) {
const details = getApiErrorDetails ( error , '订单详情加载失败' )
ElMessage . error ( details . message )
} finally {
detailLoading . value = false
}
}
async function refreshAfterAction ( orderId : string ) {
await loadOrders ( )
if ( detailOrderId . value === orderId ) {
await loadDetail ( orderId )
}
}
function closeDetailPage ( ) {
const { orderId : _orderId , ... query } = route . query
void router . replace ( { path : route . path , query } )
}
function getFieldValidationMessage ( error : unknown , field : string , fallback : string ) {
const details = getApiErrorDetails ( error , fallback )
if ( error instanceof ApiResponseError && error . details && typeof error . details === 'object' ) {
const fieldMessage = ( error . details as Record < string , unknown > ) [ field ]
if ( typeof fieldMessage === 'string' && fieldMessage ) {
return ` ${ details . message } : ${ fieldMessage } `
}
}
return details . message
}
async function runLifecycleAction ( order : PlatformFirstLegOrderResource ) {
if ( ! tenantReady . value ) return
const label = actionLabel ( order )
if ( ! label ) return
try {
await ElMessageBox . confirm ( ` 确认 ${ label } “ ${ order . orderNo || order . id } ”吗?该操作会写入订单状态历史。 ` , label , {
confirmButtonText : ` 确认 ${ label } ` ,
cancelButtonText : '取消' ,
type : 'warning' ,
} )
actionLoadingId . value = String ( order . id )
if ( order . status === 'SUBMITTED' ) await approvePlatformFirstLegOrder ( loadedTenantId . value , String ( order . id ) )
if ( order . status === 'APPROVED' ) await acceptPlatformFirstLegOrder ( loadedTenantId . value , String ( order . id ) )
if ( order . status === 'ACCEPTED' ) await startPlatformFirstLegOrder ( loadedTenantId . value , String ( order . id ) )
if ( order . status === 'PROCESSING' ) await completePlatformFirstLegOrder ( loadedTenantId . value , String ( order . id ) )
await refreshAfterAction ( String ( order . id ) )
ElMessage . success ( ` ${ label } 已完成 ` )
} catch ( error ) {
if ( error === 'cancel' || error === 'close' ) return
ElMessage . error ( getApiErrorDetails ( error , ` ${ label } 失败,请稍后重试 ` ) . message )
} finally {
actionLoadingId . value = ''
}
}
async function cancelOrder ( order : PlatformFirstLegOrderResource ) {
if ( ! tenantReady . value ) return
try {
const result = await ElMessageBox . prompt ( '请填写取消原因。后端要求该字段必填,并将写入订单状态历史。' , '取消订单' , {
confirmButtonText : '确认取消' ,
cancelButtonText : '返回' ,
inputPattern : /\S+/ ,
inputErrorMessage : '请填写取消原因' ,
inputPlaceholder : '例如:客户取消本次运输安排' ,
} )
actionLoadingId . value = String ( order . id )
await cancelPlatformFirstLegOrder ( loadedTenantId . value , String ( order . id ) , result . value . trim ( ) )
await refreshAfterAction ( String ( order . id ) )
ElMessage . success ( '订单已取消' )
} catch ( error ) {
if ( error === 'cancel' || error === 'close' ) return
ElMessage . error ( getApiErrorDetails ( error , '取消订单失败,请稍后重试' ) . message )
} finally {
actionLoadingId . value = ''
}
}
function openExpress ( order : PlatformFirstLegOrderResource ) {
selectedOrder . value = order
expressForm . expressCompany = order . expressCompany || ''
expressForm . expressTrackingNo = order . expressTrackingNo || ''
expressDialogOpen . value = true
void nextTick ( ( ) => expressFormRef . value ? . clearValidate ( ) )
}
async function saveExpress ( ) {
if ( ! selectedOrder . value || ! tenantReady . value ) return
const valid = await expressFormRef . value ? . validate ( ) . catch ( ( ) => false )
if ( ! valid ) return
expressSaving . value = true
try {
await updatePlatformFirstLegOrderExpress ( loadedTenantId . value , String ( selectedOrder . value . id ) , {
expressCompany : expressForm . expressCompany . trim ( ) ,
expressTrackingNo : expressForm . expressTrackingNo . trim ( ) ,
} )
expressDialogOpen . value = false
await refreshAfterAction ( String ( selectedOrder . value . id ) )
ElMessage . success ( '履约快递信息已保存' )
} catch ( error ) {
ElMessage . error ( getFieldValidationMessage ( error , 'expressTrackingNo' , '履约快递信息保存失败' ) )
} finally {
expressSaving . value = false
}
}
function openNote ( order : PlatformFirstLegOrderResource ) {
selectedOrder . value = order
noteForm . content = ''
noteDialogOpen . value = true
void nextTick ( ( ) => noteFormRef . value ? . clearValidate ( ) )
}
async function saveNote ( ) {
if ( ! selectedOrder . value || ! tenantReady . value ) return
const valid = await noteFormRef . value ? . validate ( ) . catch ( ( ) => false )
if ( ! valid ) return
noteSaving . value = true
try {
await addPlatformFirstLegOrderNote ( loadedTenantId . value , String ( selectedOrder . value . id ) , noteForm . content . trim ( ) )
noteDialogOpen . value = false
await refreshAfterAction ( String ( selectedOrder . value . id ) )
ElMessage . success ( '内部备注已保存' )
} catch ( error ) {
ElMessage . error ( getApiErrorDetails ( error , '内部备注保存失败' ) . message )
} finally {
noteSaving . value = false
}
}
watch ( detailOrderId , ( orderId ) => {
if ( orderId ) {
void loadDetail ( orderId )
return
}
detail . value = null
selectedOrder . value = null
void loadOrders ( )
} , { immediate : true } )
< / script >
< template >
< div class = "business-page" >
< template v-if = "isDetailPage" >
< section class = "surface-panel detail-page" v-loading = "detailLoading" >
< header class = "detail-page-header" >
< div >
< h1 > 订单详情 < / h1 >
< p > 查看订单信息 、 履约快递 、 内部备注和状态历史 。 < / p >
< / div >
< el-button @click ="closeDetailPage" > < ArrowLeft :size = "16" / > 返回订单列表 < / el-button >
< / header >
< template v-if = "detail" >
< div class = "order-detail-head" >
< div >
< span > { { transportLabel ( detail . order ) } } < / span >
< h2 > { { detail . order . orderNo || detail . order . id } } < / h2 >
< p > { { detail . order . customerName } } · { { detail . order . referenceId || '无客户参考号' } } < / p >
< / div >
< StatusBadge :status = "statusLabel(detail.order.status)" / >
< / div >
< div class = "detail-section" >
< h3 > 订单概要 < / h3 >
< el-descriptions :column = "2" border >
< el-descriptions-item label = "目标仓 / 国家" > { { detail . order . destinationWarehouseName || '-' } } · { { detail . order . destinationCountryCode || '-' } } < / el-descriptions-item >
< el-descriptions-item label = "平台收货仓" > { { detail . order . receivingWarehouseName || '-' } } < / el-descriptions-item >
< el-descriptions-item label = "收货地址" :span = "2" > { { detail . order . receivingWarehouseAddress || '-' } } < / el-descriptions-item >
< el-descriptions-item label = "渠道" > { { detail . order . channelName || '-' } } ( { { detail . order . channelCode || '-' } } ) < / el-descriptions-item >
< el-descriptions-item label = "预计出运" > { { detail . order . expectedShipDate || '-' } } < / el-descriptions-item >
< el-descriptions-item label = "客户备注" :span = "2" > { { detail . order . customerRemark || '-' } } < / el-descriptions-item >
< / el-descriptions >
< / div >
< div class = "detail-section" >
< h3 > 货物明细 < / h3 >
< div class = "detail-table-scroll" >
< el-table :data = "detail.cargoes" size = "small" empty -text = " 暂无货物明细 " >
< el-table-column prop = "itemNo" label = "#" width = "52" / >
< el-table-column prop = "cargoName" label = "品名" min -width = " 150 " / >
< el-table-column prop = "packageType" label = "包装" min -width = " 100 " / >
< el-table-column prop = "packageCount" label = "件/箱" width = "86" / >
< el-table-column label = "毛重 / 体积" min -width = " 150 " > < template # default = "{ row }" > { { row . grossWeightKg } } kg / { { row . volumeCbm } } m³ < / template > < / el-table-column >
< / el-table >
< / div >
< / div >
< div class = "detail-section" >
< h3 > 收发货方 < / h3 >
< div class = "party-grid" >
< div v-for = "party in detail.parties" :key="party.id" >
< span > { { party . partyType === 'SENDER' ? '发货方' : '收货方' } } < / span >
< strong > { { party . companyName } } < / strong >
< p > { { party . contactName } } · { { party . phone } } < / p >
< p > { { [ party . province , party . city , party . district , party . addressLine ] . filter ( Boolean ) . join ( ' ' ) } } < / p >
< / div >
< / div >
< / div >
< div class = "detail-section" >
< h3 > 履约快递 < / h3 >
< el-descriptions :column = "2" border >
< el-descriptions-item label = "快递公司" > { { detail . order . expressCompany || '-' } } < / el-descriptions-item >
< el-descriptions-item label = "快递单号" > { { detail . order . expressTrackingNo || '-' } } < / el-descriptions-item >
< / el-descriptions >
< / div >
< div class = "detail-section" >
< h3 > 内部备注 < / h3 >
< el-empty v-if = "!detail.internalNotes.length" description="暂无内部备注" :image-size="52" / >
< el-timeline v-else > < el -timeline -item v-for = "note in detail.internalNotes" :key="note.id" :timestamp="formattedDate(note.createdAt)" placement="top" > < p > { { note . content } } < / p > < / el-timeline-item > < / el-timeline >
< / div >
< div class = "detail-section" >
< h3 > 状态历史 < / h3 >
< el-timeline > < el-timeline-item v-for = "item in detail.statusHistory" :key="item.id" :timestamp="`${formattedDate(item.occurredAt)} · ${item.operatorName || '系统'}`" placement="top" > < strong > { { statusLabel ( item . toStatus ) } } < / strong > < p > { { item . reason || item . action || '状态已更新。' } } < / p > < / el-timeline-item > < / el-timeline >
< / div >
< / template >
< el-empty v-else-if = "!detailLoading" description="订单详情暂不可用"><el-button type="primary" @click="closeDetailPage" > 返回订单列表 < / el -button > < / el-empty >
< / section >
< / template >
< template v-else >
< QueryToolbar
v-model = "filters"
ariaLabel = "头程订单筛选"
:fields = "queryFields"
search -text = " 查询 "
:search-on-reset = "true"
@search ="searchOrders"
/ >
< el-alert v-if = "loadError" type="error" :closable="false" show-icon role="alert" >
< template # title > < div class = "api-error-alert-title" > < span > { { loadError . message } } < / span > < ApiErrorMeta :details = "loadError" / > < / div > < / template >
< el-button link type = "danger" @click ="loadOrders" > 重新加载 < / el -button >
< / el-alert >
< section class = "surface-panel table-panel" >
< DataTable
v -model :page = "page"
v -model :page-size = "pageSize"
:data = "orders"
:columns = "orderColumns"
:loading = "loading"
:total = "total"
: page -sizes = " [ 20 , 50 , 100 ] "
empty -text = " 当前没有符合条件的头程订单 "
row -key = " id "
>
< template # actions = "{ row }" > < div class = "table-actions" > < el-button link type = "primary" @click ="openDetail(row as PlatformFirstLegOrderResource)" > < Eye :size = "15" / > 详情 < / el-button > < el-button v-if = "actionLabel(row as PlatformFirstLegOrderResource)" link type="success" :loading="actionLoadingId === String((row as PlatformFirstLegOrderResource).id)" @click="runLifecycleAction(row as PlatformFirstLegOrderResource)">{{ actionLabel(row as PlatformFirstLegOrderResource) }}</el-button><el-button v-if="canCancel(row as PlatformFirstLegOrderResource)" link type="danger" :loading="actionLoadingId === String((row as PlatformFirstLegOrderResource).id)" @click="cancelOrder(row as PlatformFirstLegOrderResource)">取消</el-button><el-button v-if="can('platform:order:fulfillment') && !['DRAFT', 'REJECTED', 'CANCELLED'].includes((row as PlatformFirstLegOrderResource).status)" link type="primary" @click="openExpress(row as PlatformFirstLegOrderResource)"><PackageCheck :size="15" />快递</el-button><el-button v-if="can('platform:order:note')" link type="primary" @click="openNote(row as PlatformFirstLegOrderResource)"><FilePenLine :size="15" / > 备注 < / el-button > < / div > < / template >
< template # orderInfo = "{ row }" > < div class = "primary-cell" > < strong > { { ( row as PlatformFirstLegOrderResource ) . orderNo || '-' } } < / strong > < span > { { ( row as PlatformFirstLegOrderResource ) . referenceId || '无客户参考号' } } · { { formattedDate ( ( row as PlatformFirstLegOrderResource ) . createdAt ) } } < / span > < / div > < / template >
< template # customer = "{ row }" > < div class = "primary-cell" > < strong > { { ( row as PlatformFirstLegOrderResource ) . customerName } } < / strong > < span > 租户 { { ( row as PlatformFirstLegOrderResource ) . tenantId } } < / span > < / div > < / template >
< template # transport = "{ row }" > < div class = "primary-cell" > < strong > { { transportLabel ( row as PlatformFirstLegOrderResource ) } } < / strong > < span > { { ( row as PlatformFirstLegOrderResource ) . channelName || '未填写渠道' } } < / span > < / div > < / template >
< template # destination = "{ row }" > < div class = "primary-cell" > < strong > { { ( row as PlatformFirstLegOrderResource ) . destinationWarehouseName || '-' } } < / strong > < span > { { ( row as PlatformFirstLegOrderResource ) . destinationCountryCode || '-' } } < / span > < / div > < / template >
< template # receivingWarehouse = "{ row }" > < div class = "primary-cell" > < strong > { { ( row as PlatformFirstLegOrderResource ) . receivingWarehouseName || '-' } } < / strong > < span > { { ( row as PlatformFirstLegOrderResource ) . receivingWarehouseAddress || '-' } } < / span > < / div > < / template >
< template # express = "{ row }" > < div class = "primary-cell" > < strong > { { ( row as PlatformFirstLegOrderResource ) . expressCompany || '未维护' } } < / strong > < span > { { ( row as PlatformFirstLegOrderResource ) . expressTrackingNo || '—' } } < / span > < / div > < / template >
< template # status = "{ row }" > < StatusBadge : status = "statusLabel((row as PlatformFirstLegOrderResource).status)" / > < / template >
< template # empty > < el-empty description = "当前没有符合条件的头程订单" / > < / template >
< / DataTable >
< / section >
< / template >
< el-dialog v-model = "expressDialogOpen" title="维护履约快递" width="520px" >
< el -alert title = "当前后端契约只接收快递公司名称和快递单号;快递主数据关联待后端补充。" type = "info" :closable = "false" show -icon / >
< el-form ref = "expressFormRef" :model = "expressForm" :rules = "expressRules" label -position = " top " class = "delivery-form" > < el-form-item label = "快递公司" prop = "expressCompany" required > < el-input v -model .trim = " expressForm.expressCompany " maxlength = "64" placeholder = "例如 顺丰速运" / > < / el-form-item > < el-form-item label = "快递单号" prop = "expressTrackingNo" required > < el-input v -model .trim = " expressForm.expressTrackingNo " maxlength = "128" placeholder = "请输入完整快递单号" / > < / el-form-item > < / el-form >
< template # footer > < el-button :disabled = "expressSaving" @click ="expressDialogOpen = false" > 取消 < / el -button > < el-button type = "primary" :loading = "expressSaving" @click ="saveExpress" > 保存快递信息 < / el -button > < / template >
< / el-dialog >
< el-dialog v-model = "noteDialogOpen" title="添加内部备注" width="520px" >
< el -form ref = "noteFormRef" :model = "noteForm" :rules = "noteRules" label -position = " top " > < el-form-item label = "内部备注" prop = "content" required > < el-input v -model .trim = " noteForm.content " maxlength = "2000" type = "textarea" :rows = "4" show -word -limit placeholder = "仅平台内部可见" / > < / el-form-item > < / el-form >
< template # footer > < el-button :disabled = "noteSaving" @click ="noteDialogOpen = false" > 取消 < / el -button > < el-button type = "primary" :loading = "noteSaving" @click ="saveNote" > 保存备注 < / el -button > < / template >
< / el-dialog >
< / div >
< / template >
< style scoped >
. detail - page { overflow : hidden ; }
. detail - page - header { display : flex ; min - height : 88 px ; align - items : center ; justify - content : space - between ; gap : 18 px ; padding : 20 px 24 px ; border - bottom : 1 px solid var ( -- cc - border ) ; }
. detail - page - header h1 { margin : 0 ; font - size : 20 px ; line - height : 1.35 ; }
. detail - page - header p { margin : 5 px 0 0 ; color : var ( -- cc - muted ) ; font - size : 12 px ; line - height : 1.5 ; }
. detail - table - scroll { overflow - x : auto ; }
. party - grid p { margin : 4 px 0 0 ; color : var ( -- cc - muted ) ; font - size : 12 px ; line - height : 1.6 ; overflow - wrap : anywhere ; }
@ media ( max - width : 720 px ) {
. detail - page - header { align - items : flex - start ; flex - direction : column ; padding : 16 px ; }
. detail - page - header . el - button { width : 100 % ; min - height : 40 px ; }
. detail - table - scroll : deep ( . el - table ) { min - width : 560 px ; }
}
< / style >