2026-07-31 13:43:16 +08:00
< script setup lang = "ts" >
2026-08-01 12:17:33 +08:00
import { computed , nextTick , onMounted , reactive , ref , watch } from 'vue'
2026-07-31 13:43:16 +08:00
import { ElMessage , ElMessageBox } from 'element-plus'
2026-08-01 12:17:33 +08:00
import type { FormInstance , FormRules } from 'element-plus'
import { Eye , FilePenLine , PackageCheck } from '@lucide/vue'
2026-07-31 13:43:16 +08:00
import {
acceptPlatformFirstLegOrder ,
addPlatformFirstLegOrderNote ,
approvePlatformFirstLegOrder ,
cancelPlatformFirstLegOrder ,
completePlatformFirstLegOrder ,
getPlatformFirstLegOrder ,
listPlatformFirstLegOrders ,
startPlatformFirstLegOrder ,
updatePlatformFirstLegOrderExpress ,
type PlatformFirstLegOrderDetailResource ,
type PlatformFirstLegOrderResource ,
type PlatformFirstLegOrderStatus ,
} from '@/api/platformFirstLegOrders'
import { getApiErrorDetails , type ApiErrorDetails } from '@/api/http'
import ApiErrorMeta from '@/components/ApiErrorMeta.vue'
import StatusBadge from '@/components/StatusBadge.vue'
2026-08-01 12:17:33 +08:00
import DataTable , { type DataTableColumn } from '@/components/common/DataTable.vue'
import QueryToolbar , { type QueryField , type QueryModel } from '@/components/common/QueryToolbar.vue'
2026-07-31 13:43:16 +08:00
import { useAccessPreviewStore } from '@/stores/accessPreview'
2026-08-01 12:17:33 +08:00
const PHASE_ONE_TENANT_ID = '10001'
2026-07-31 13:43:16 +08:00
const access = useAccessPreviewStore ()
2026-08-01 12:17:33 +08:00
const loadedTenantId = ref ( PHASE_ONE_TENANT_ID )
2026-07-31 13:43:16 +08:00
const orders = ref < PlatformFirstLegOrderResource [] >([])
const total = ref ( 0 )
const page = ref ( 1 )
2026-08-01 12:17:33 +08:00
const pageSize = ref ( 20 )
2026-07-31 13:43:16 +08:00
const loading = ref ( false )
const actionLoadingId = ref ( '' )
const loadError = ref < ApiErrorDetails | null >( null )
const detailOpen = ref ( false )
const detailLoading = ref ( false )
const detail = ref < PlatformFirstLegOrderDetailResource | null >( null )
const noteDialogOpen = ref ( false )
const expressDialogOpen = ref ( false )
const selectedOrder = ref < PlatformFirstLegOrderResource | null >( null )
2026-08-01 12:17:33 +08:00
const noteForm = reactive ({ content : '' })
2026-07-31 13:43:16 +08:00
const noteSaving = ref ( false )
const expressSaving = ref ( false )
2026-08-01 12:17:33 +08:00
const expressFormRef = ref < FormInstance >()
const noteFormRef = ref < FormInstance >()
2026-07-31 13:43:16 +08:00
const expressForm = reactive ({ expressCompany : '' , expressTrackingNo : '' })
2026-08-01 12:17:33 +08:00
const filters = ref < QueryModel & {
keyword : string
status : PlatformFirstLegOrderStatus | ''
dateRange : string []
}>({
2026-07-31 13:43:16 +08:00
keyword : '' ,
status : '' ,
dateRange : [],
})
2026-08-01 12:17:33 +08:00
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' }],
}
const noteRules : FormRules = {
content : [{ required : true , message : '请填写内部备注' , trigger : 'blur' }],
}
const tenantReady = computed (() => loadedTenantId . value === PHASE_ONE_TENANT_ID )
2026-07-31 13:43:16 +08:00
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 ''
}
2026-08-01 12:17:33 +08:00
async function loadOrders () {
const targetTenantId = PHASE_ONE_TENANT_ID
2026-07-31 13:43:16 +08:00
loadError . value = null
loading . value = true
try {
const result = await listPlatformFirstLegOrders ( targetTenantId , {
2026-08-01 12:17:33 +08:00
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 ,
2026-07-31 13:43:16 +08:00
page : page . value ,
2026-08-01 12:17:33 +08:00
size : pageSize . value ,
2026-07-31 13:43:16 +08:00
})
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
}
}
2026-08-01 12:17:33 +08:00
function searchOrders () {
if ( page . value !== 1 ) {
page . value = 1
return
}
2026-07-31 13:43:16 +08:00
void loadOrders ()
}
2026-08-01 12:17:33 +08:00
watch ([ page , pageSize ], () => {
void loadOrders ()
})
2026-07-31 13:43:16 +08:00
async function openDetail ( order : PlatformFirstLegOrderResource ) {
if ( ! tenantReady . value ) return
selectedOrder . value = order
detail . value = null
detailOpen . value = true
detailLoading . value = true
try {
detail . value = await getPlatformFirstLegOrder ( loadedTenantId . value , String ( order . id ))
} catch ( error ) {
const details = getApiErrorDetails ( error , '订单详情加载失败' )
ElMessage . error ( details . message )
} finally {
detailLoading . value = false
}
}
async function refreshAfterAction ( orderId : string ) {
await loadOrders ()
if ( detailOpen . value && selectedOrder . value ? . id === orderId ) {
detail . value = await getPlatformFirstLegOrder ( loadedTenantId . value , orderId )
}
}
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
2026-08-01 12:17:33 +08:00
void nextTick (() => expressFormRef . value ? . clearValidate ())
2026-07-31 13:43:16 +08:00
}
async function saveExpress () {
if ( ! selectedOrder . value || ! tenantReady . value ) return
2026-08-01 12:17:33 +08:00
const valid = await expressFormRef . value ? . validate (). catch (() => false )
if ( ! valid ) return
2026-07-31 13:43:16 +08:00
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 ( getApiErrorDetails ( error , '履约快递信息保存失败' ). message )
} finally {
expressSaving . value = false
}
}
function openNote ( order : PlatformFirstLegOrderResource ) {
selectedOrder . value = order
2026-08-01 12:17:33 +08:00
noteForm . content = ''
2026-07-31 13:43:16 +08:00
noteDialogOpen . value = true
2026-08-01 12:17:33 +08:00
void nextTick (() => noteFormRef . value ? . clearValidate ())
2026-07-31 13:43:16 +08:00
}
async function saveNote () {
if ( ! selectedOrder . value || ! tenantReady . value ) return
2026-08-01 12:17:33 +08:00
const valid = await noteFormRef . value ? . validate (). catch (() => false )
if ( ! valid ) return
2026-07-31 13:43:16 +08:00
noteSaving . value = true
try {
2026-08-01 12:17:33 +08:00
await addPlatformFirstLegOrderNote ( loadedTenantId . value , String ( selectedOrder . value . id ), noteForm . content . trim ())
2026-07-31 13:43:16 +08:00
noteDialogOpen . value = false
await refreshAfterAction ( String ( selectedOrder . value . id ))
ElMessage . success ( '内部备注已保存' )
} catch ( error ) {
ElMessage . error ( getApiErrorDetails ( error , '内部备注保存失败' ). message )
} finally {
noteSaving . value = false
}
}
onMounted (() => {
2026-08-01 12:17:33 +08:00
void loadOrders ()
2026-07-31 13:43:16 +08:00
})
</ script >
< template >
< div class = "business-page" >
2026-08-01 12:17:33 +08:00
< QueryToolbar
v-model = "filters"
ariaLabel = "头程订单筛选"
:fields = "queryFields"
search -text =" 查询订单 "
:search-on-reset = "true"
@search ="searchOrders"
/>
2026-07-31 13:43:16 +08:00
< 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 >
2026-08-01 12:17:33 +08:00
< el-button link type = "danger" @click ="loadOrders" > 重新加载 </ el -button >
2026-07-31 13:43:16 +08:00
</ el-alert >
< section class = "surface-panel table-panel" >
2026-08-01 12:17:33 +08:00
< 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 >
2026-07-31 13:43:16 +08:00
</ section >
< el-drawer v-model = "detailOpen" title="订单详情" size="760px" class="business-drawer" >
< div v-loading = "detailLoading" >
< 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 >< 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 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="订单详情暂不可用" />
</ div >
< template # footer >< div class = "drawer-footer" >< el-button @click ="detailOpen = false" > 关闭 </ el -button ></ div ></ template >
</ el-drawer >
< el-dialog v-model = "expressDialogOpen" title="维护履约快递" width="520px" >
< el-alert title = "当前后端契约只接收快递公司名称和快递单号;快递主数据关联待后端补充。" type = "info" :closable = "false" show -icon />
2026-08-01 12:17:33 +08:00
< 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 >
2026-07-31 13:43:16 +08:00
< 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" >
2026-08-01 12:17:33 +08:00
< 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 >
2026-07-31 13:43:16 +08:00
< 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 >
. party - grid p { margin : 4 px 0 0 ; color : var ( -- cc - muted ); font - size : 12 px ; line - height : 1.6 ; overflow - wrap : anywhere ; }
</ style >