@@ -1,12 +1,11 @@
< script setup lang = "ts" >
import { computed , nextTick , onMounted , reactive , ref , watch } from 'vue'
import { ElMessage , type FormInstance , type FormRules } from 'element-plus'
import { computed , nextTick , onMounted , ref , watch } from 'vue'
import { ElMessage } from 'element-plus'
import { ArrowLeft , Box , ChevronDown , ChevronUp , CircleCheck , Eye , History , ListChecks , MoreHorizontal , PackageOpen , RefreshCw , Ruler , SlidersHorizontal , StickyNote , WalletCards } from '@lucide/vue'
import { useRoute , useRouter } from 'vue-router'
import {
getPlatformUnifiedOrder ,
approvePlatformUnifiedOrder ,
assignPlatformUnifiedOrderCarrier ,
addPlatformUnifiedOrderNote ,
listPlatformUnifiedOrders ,
unifiedOrderStatusOptions ,
@@ -16,7 +15,6 @@ 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'
@@ -71,22 +69,13 @@ const activeDetailTab = ref('basic')
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 noteOpen = ref ( false )
const noteSaving = ref ( false )
const noteContent = ref ( '' )
const noteOrder = ref < UnifiedOrderResource | null > ( null )
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 : '' )
const detailTabs = [
@@ -270,6 +259,30 @@ function formatNumber(value: number | null | undefined, digits = 2) {
return value === null || value === undefined ? '—' : value . toLocaleString ( 'zh-CN' , { maximumFractionDigits : digits } )
}
function partyValue ( parties : Array < Record < string , unknown > > , type : 'SENDER' | 'CONSIGNEE' , key : string ) {
const party = parties . find ( ( item ) => String ( item . partyType || '' ) . toUpperCase ( ) === type )
const value = party ? . [ key ]
return value === null || value === undefined || value === '' ? '—' : String ( value )
}
function partyAddress ( parties : Array < Record < string , unknown > > , type : 'SENDER' | 'CONSIGNEE' ) {
const party = parties . find ( ( item ) => String ( item . partyType || '' ) . toUpperCase ( ) === type )
if ( ! party ) return '—'
return [ 'province' , 'city' , 'district' , 'addressLine' ] . map ( ( key ) => party [ key ] ) . filter ( ( value ) => typeof value === 'string' && value . trim ( ) ) . join ( ' ' ) || '—'
}
function cargoTotal ( cargoes : Array < Record < string , unknown > > , key : string ) {
const total = cargoes . reduce ( ( sum , cargo ) => {
const value = Number ( cargo [ key ] )
return Number . isFinite ( value ) ? sum + value : sum
} , 0 )
return total > 0 ? total : null
}
function displayBoolean ( value : boolean | null | undefined ) {
return value === null || value === undefined ? '—' : value ? '是' : '否'
}
function displayField ( row : UnifiedOrderResource , property : string | undefined ) {
if ( ! property ) return '—'
const source = row as unknown as Record < string , unknown >
@@ -297,7 +310,17 @@ function displayField(row: UnifiedOrderResource, property: string | undefined) {
}
function statusLabel ( value : UnifiedOrderMainStatus | string | null ) {
return unifiedOrderStatusOptions . find ( ( item ) => item . value === value ) ? . label || value || '状态更新'
return unifiedOrderStatusOptions . find ( ( item ) => item . value === value ) ? . label || orderStatusLabel ( value ) || '状态更新'
}
function historyActionLabel ( value : string | null | undefined ) {
return ( {
CREATE : '创建订单' , CREATED : '创建订单' , SUBMITTED : '提交订单' , APPROVED : '审核通过' ,
ORDER _APPROVED : '订单审核通过' , REJECTED : '审核驳回' , MEASURE _APPROVE : '测量审核通过' ,
MEASURE _REJECT : '测量审核驳回' , INBOUND _BEGIN : '开始入库' , INBOUND _COMPLETE : '入库完成' ,
UI _FIXTURE _MEASURE _APPROVE : '测量数据审核通过' , UI _FIXTURE _INBOUND _BEGIN : '开始入库' ,
STATUS _UPDATE : '状态更新' ,
} as Record < string , string > ) [ value || '' ] || ( value ? value . replaceAll ( '_' , ' ' ) : '状态更新' )
}
function measurementStatusLabel ( value : string | null | undefined ) {
@@ -334,70 +357,34 @@ function openBulkReviewPlaceholder() {
ElMessage . info ( '批量审核接口待接入,请先逐笔审核' )
}
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 ) {
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 ( ) {
async function approveOrder ( ) {
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 ( '承运商已指派, 订单审核通过' )
ElMessage . success ( '订单审核通过' )
} catch ( error ) {
ElMessage . error ( getApiErrorDetails ( error , '承运商指派或 订单审核失败,请稍后重试' ) . message )
ElMessage . error ( getApiErrorDetails ( error , '订单审核失败,请稍后重试' ) . message )
} finally {
approvalSaving . value = false
approvalLoadingId . value = ''
@@ -636,18 +623,66 @@ onMounted(() => {
< div > < dt > 公司 < / dt > < dd > { { customerName ( detail . summary ) } } < / dd > < / div >
< div > < dt > 运输方式 < / dt > < dd > { { detail . order . order ? . transportMode || '—' } } < / dd > < / div >
< div > < dt > 当前状态 < / dt > < dd > < StatusBadge :status = "statusLabel(detail.summary.mainStatus)" / > < / dd > < / div >
< div > < dt > 内部订单状态 < / dt > < dd > { { detail . summary . internalOrderStatus || '—' } } < / dd > < / div >
< div > < dt > 平台收货仓 < / dt > < dd > { { detail . summary . receivingWarehouseName || '—' } } < / dd > < / div >
< div > < dt > 收货仓地址 < / dt > < dd > { { detail . order . order ? . receivingWarehouseAddress || '—' } } < / dd > < / div >
< div > < dt > 目的仓 < / dt > < dd > { { detail . summary . destinationWarehouseName || '—' } } < / dd > < / div >
< div > < dt > 目的国家 < / dt > < dd > { { detail . order . order ? . destinationCountryCode || '—' } } < / dd > < / div >
< div > < dt > 目的仓地址 < / dt > < dd > { { detail . summary . destinationAddress || '—' } } < / dd > < / div >
< div > < dt > 目的仓邮编 < / dt > < dd > { { detail . summary . destinationPostalCode || '—' } } < / dd > < / div >
< div > < dt > 目的仓联系人 < / dt > < dd > { { detail . summary . destinationContact || '—' } } < / dd > < / div >
< div > < dt > 目的仓联系人电话 < / dt > < dd > { { detail . summary . destinationContactPhone || '—' } } < / dd > < / div >
< div > < dt > 渠道 < / dt > < dd > { { detail . summary . channelName || '—' } } < / dd > < / div >
< div > < dt > 承运商 < / dt > < dd > { { detail . order . order ? . carrierName || '待平台指派' } } < / dd > < / div >
< div > < dt > 业务类型 < / dt > < dd > { { detail . order . order ? . tradeTerm || '—' } } < / dd > < / div >
< div > < dt > 贸易方式 < / dt > < dd > { { detail . order . order ? . tradeTerm || detail . summary . businessType || '—' } } < / dd > < / div >
< div > < dt > 买单 / 退税 < / dt > < dd > { { detail . order . order ? . taxMode || '—' } } < / dd > < / div >
< div > < dt > 报关方式 < / dt > < dd > { { detail . summary . businessType || '—' } } < / dd > < / div >
< div > < dt > 费用审核状态 < / dt > < dd > { { detail . summary . feeReviewStatus || '—' } } < / dd > < / div >
< div > < dt > 是否扣货 < / dt > < dd > { { displayBoolean ( detail . summary . holdCargo ) } } < / dd > < / div >
< div > < dt > 扣货理由 < / dt > < dd > { { detail . summary . holdReason || '—' } } < / dd > < / div >
< div > < dt > 提货方式 < / dt > < dd > { { detail . order . order ? . pickupMethod || '—' } } < / dd > < / div >
< div > < dt > 提货时间 < / dt > < dd > { { formatDate ( detail . summary . pickupAt ) } } < / dd > < / div >
< div > < dt > 到仓时间 < / dt > < dd > { { detail . order . order ? . receivingTime || '—' } } < / dd > < / div >
< div > < dt > 货物到仓时间 < / dt > < dd > { { formatDate ( detail . summary . expectedArrivalAt || detail . summary . inboundCompletedAt ) } } < / dd > < / div >
< div > < dt > 预计出运 < / dt > < dd > { { formatDate ( detail . summary . expectedShipDate ) } } < / dd > < / div >
< div > < dt > 预计到达时间 < / dt > < dd > { { formatDate ( detail . summary . expectedArrivalAt ) } } < / dd > < / div >
< div > < dt > 集装箱号 < / dt > < dd > { { detail . summary . transport ? . containerNo || '—' } } < / dd > < / div >
< div > < dt > 柜号 < / dt > < dd > { { detail . summary . transport ? . cabinetNo || '—' } } < / dd > < / div >
< div > < dt > 船名 / 航次 < / dt > < dd > { { detail . summary . transport ? . vesselName || '—' } } / { { detail . summary . transport ? . voyageNo || '—' } } < / dd > < / div >
< div > < dt > 开船时间 < / dt > < dd > { { formatDate ( detail . summary . transport ? . sailingAt ) } } < / dd > < / div >
< div > < dt > 到港时间 < / dt > < dd > { { formatDate ( detail . summary . transport ? . portArrivalAt ) } } < / dd > < / div >
< div > < dt > 下一步 < / dt > < dd > { { canApprove ( detail . summary ) ? '选择承运商并审核' : nextActionBoundary ( detail . summary ) || '—' } } < / dd > < / div >
< div > < dt > 配载状态 < / dt > < dd > { { detail . summary . transport ? . loadingStatus || '—' } } < / dd > < / div >
< div > < dt > 目的仓类型 < / dt > < dd > { { detail . order . order ? . destinationType || '—' } } < / dd > < / div >
< div > < dt > 是否整柜 < / dt > < dd > { { detail . order . order ? . containerMode || '—' } } < / dd > < / div >
< div > < dt > 商检验 < / dt > < dd > { { displayBoolean ( detail . order . order ? . customsInspection ) } } < / dd > < / div >
< div > < dt > 总箱数 < / dt > < dd > { { detail . summary . totalPackageCount ? ? '—' } } 箱 < / dd > < / div >
< div > < dt > 总托盘数 < / dt > < dd > { { detail . summary . totalPalletCount ? ? '—' } } < / dd > < / div >
< div > < dt > 总毛重 < / dt > < dd > { { formatNumber ( cargoTotal ( detail . order . cargoes , 'grossWeightKg' ) ? ? detail . summary . measurement ? . actualGrossWeightKg ) } } kg < / dd > < / div >
< div > < dt > 总净重 < / dt > < dd > { { formatNumber ( cargoTotal ( detail . order . cargoes , 'netWeightKg' ) ) } } kg < / dd > < / div >
< div > < dt > 总体积 < / dt > < dd > { { formatNumber ( cargoTotal ( detail . order . cargoes , 'volumeCbm' ) ? ? detail . summary . measurement ? . actualVolumeCbm , 3 ) } } m³ < / dd > < / div >
< div > < dt > 计费毛重 < / dt > < dd > { { formatNumber ( detail . summary . measurement ? . chargeableWeightKg ) } } kg < / dd > < / div >
< div > < dt > 计费体积 < / dt > < dd > { { formatNumber ( detail . summary . chargeableVolumeCbm , 3 ) } } m³ < / dd > < / div >
< div > < dt > 总材积重 < / dt > < dd > { { formatNumber ( detail . summary . volumetricWeightKg ) } } kg < / dd > < / div >
< div > < dt > 总材积量 < / dt > < dd > { { formatNumber ( detail . summary . volumetricWeightKg ) } } < / dd > < / div >
< div > < dt > 库位位置 < / dt > < dd > { { detail . summary . location || '—' } } < / dd > < / div >
< div > < dt > 创建时间 < / dt > < dd > { { formatDate ( detail . summary . createdAt ) } } < / dd > < / div >
< div > < dt > 入库完成时间 < / dt > < dd > { { formatDate ( detail . summary . inboundCompletedAt ) } } < / dd > < / div >
< div > < dt > 测量完成时间 < / dt > < dd > { { formatDate ( detail . summary . measurementCompletedAt || detail . summary . measurement ? . completedAt ) } } < / dd > < / div >
< div class = "wide" > < dt > 提货联系人 < / dt > < dd > { { partyValue ( detail . order . parties , 'SENDER' , 'contactName' ) } } · { { detail . summary . pickupContactPhone || partyValue ( detail . order . parties , 'SENDER' , 'phone' ) } } · { { detail . summary . pickupContactEmail || partyValue ( detail . order . parties , 'SENDER' , 'email' ) } } < / dd > < / div >
< div class = "wide" > < dt > 提货地址 < / dt > < dd > { { detail . summary . pickupAddress || partyAddress ( detail . order . parties , 'SENDER' ) } } · { { detail . summary . pickupPostalCode || partyValue ( detail . order . parties , 'SENDER' , 'postalCode' ) } } < / dd > < / div >
< div class = "wide" > < dt > 目的地联系人 < / dt > < dd > { { partyValue ( detail . order . parties , 'CONSIGNEE' , 'contactName' ) } } · { { partyValue ( detail . order . parties , 'CONSIGNEE' , 'phone' ) } } · { { partyValue ( detail . order . parties , 'CONSIGNEE' , 'email' ) } } < / dd > < / div >
< div > < dt > 目的仓联系人邮箱 < / dt > < dd > { { partyValue ( detail . order . parties , 'CONSIGNEE' , 'email' ) } } < / dd > < / div >
< div class = "wide" > < dt > 目的地地址 < / dt > < dd > { { partyAddress ( detail . order . parties , 'CONSIGNEE' ) } } · { { partyValue ( detail . order . parties , 'CONSIGNEE' , 'postalCode' ) } } < / dd > < / div >
< div class = "wide" > < dt > 备注 < / dt > < dd > { { detail . order . order ? . customerRemark || '—' } } < / dd > < / div >
< div class = "wide" > < dt > 关联文件 < / dt > < dd > { { detail . summary . associatedFiles ? . join ( '、' ) || '—' } } < / dd > < / div >
< div class = "wide" > < dt > 唛头文件 < / dt > < dd > { { detail . summary . markFiles ? . join ( '、' ) || '—' } } < / dd > < / div >
< div class = "wide" > < dt > PC 端下单文件 < / dt > < dd > { { detail . summary . pcOrderFiles ? . join ( '、' ) || '—' } } < / dd > < / div >
< div > < dt > 快递公司 < / dt > < dd > { { detail . summary . expressCompany || detail . order . order ? . expressCompany || '—' } } < / dd > < / div >
< div > < dt > 快递单号 < / dt > < dd > { { detail . summary . expressTrackingNo || detail . order . order ? . expressTrackingNo || '—' } } < / dd > < / div >
< div class = "wide" > < dt > 客服内部备注 < / dt > < dd > { { detail . summary . internalRemark || '—' } } < / dd > < / div >
< div > < dt > 创建人 < / dt > < dd > { { detail . summary . createdBy || '—' } } < / dd > < / div >
< div > < dt > 下一步 < / dt > < dd > { { canApprove ( detail . summary ) ? '审核订单' : nextActionBoundary ( detail . summary ) || '—' } } < / dd > < / div >
< / dl >
< / template >
@@ -667,7 +702,7 @@ onMounted(() => {
< template v-else-if = "activeDetailTab === 'history'" >
< div class = "panel-title" > < div > < h2 > 状态历史 < / h2 > < p > 共 { { detail . timeline . length } } 条 < / p > < / div > < History :size = "19" / > < / div >
< el-empty v-if = "detail.timeline.length === 0" description="暂无状态历史" :image-size="64" / >
< ol v-else class = "history-list" aria -label = " 状态历史 " > < li v-for = "(item, index) in detail.timeline" :key="`${item.source || 'event'}-${item.occurredAt || index}`"><span class="history-marker" aria-hidden="true" /><div><div class="history-line"><strong>{{ item.action || item.eventType || '状态更新' }}</strong><StatusBadge :status="statusLabel(item.mainStatus)" /></div><p>{{ item.operatorName || '系统' }} · {{ formatDate(item.occurredAt) }}</p><p v-if="item.remark" class="history-remark" > {{ item.remark }} < / p > < / div > < / li > < / ol >
< ol v-else class = "history-list" aria -label = " 状态历史 " > < li v-for = "(item, index) in detail.timeline" :key="`${item.source || 'event'}-${item.occurredAt || index}`"><span class="history-marker" aria-hidden="true" /><div><div class="history-line"><strong>{{ historyActionLabel( item.action || item.eventType) }}</strong><StatusBadge :status="statusLabel(item.mainStatus)" /></div><p>{{ item.operatorName || '系统' }} · {{ formatDate(item.occurredAt) }}</p><p v-if="item.remark" class="history-remark" > {{ item.remark }} < / p > < / div > < / li > < / ol >
< / template >
< template v-else-if = "activeDetailTab === 'measurement'" >
@@ -759,20 +794,8 @@ onMounted(() => {
< el -input v-model = "noteContent" type="textarea" :rows="5" maxlength="2000" show-word-limit placeholder="请输入仅平台内部可见的备注" aria-label="内部备注内容" / >
< / AppDialog >
< 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="assignCarrierAndA pprove" @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 v-model = "approvalOpen" title="审核订单 " confirm-text="审核通过" confirm-type="success" :confirm-loading="approvalSaving" destroy-on-close @confirm="approveOrder " @cancel="closeApproval" >
< el -alert title = "确认订单资料无误后审核通过,订单将进入待测量阶段 。" type = "info" :closable = "false" show -icon / >
< / AppDialog >
< / section >
< / template >
@@ -796,9 +819,6 @@ onMounted(() => {
. table - card { margin - top : 16 px ; overflow : hidden ; border : 1 px solid var ( -- cc - border ) ; border - radius : 8 px ; background : # fff ; }
. row - actions { display : flex ; align - items : center ; gap : 4 px ; white - space : nowrap ; }
. row - actions : deep ( . el - button ) { margin - left : 0 ; }
. carrier - form { margin - top : 18 px ; }
. carrier - form : deep ( . el - select ) { width : 100 % ; }
. carrier - load - error { margin - top : 14 px ; }
. primary - cell { display : grid ; min - width : 0 ; gap : 4 px ; }
. primary - cell strong { overflow - wrap : anywhere ; }
. primary - cell span { color : var ( -- cc - muted ) ; font - size : 12 px ; overflow - wrap : anywhere ; }
@@ -828,15 +848,16 @@ onMounted(() => {
. detail - tabs button : hover { background : # fafbff ; color : # 344054 ; }
. detail - tabs button . active { background : # f7f8ff ; color : var ( -- cc - primary ) ; }
. detail - tabs button . active : : after { background : var ( -- cc - primary ) ; }
. detail - panel { min - width : 0 ; min - height : 390 px ; padding : 22 px ; border : 1 px solid var ( -- cc - border ) ; border - radius : 0 0 8 px 8 px ; background : # fff ; }
. panel - title { display : flex ; align - items : flex - start ; justify - content : space - between ; gap : 16 px ; margin - bottom : 18 px ; padding - bottom : 14 px ; border - bottom : 1 px solid var ( -- cc - border ) ; }
. detail - panel { min - width : 0 ; min - height : 390 px ; padding : 16 px ; border : 1 px solid var ( -- cc - border ) ; border - radius : 0 0 8 px 8 px ; background : # fff ; }
. panel - title { display : flex ; align - items : flex - start ; justify - content : space - between ; gap : 16 px ; margin - bottom : 10 px ; padding - bottom : 8 px ; border - bottom : 1 px solid var ( -- cc - border ) ; }
. panel - title h2 { margin : 0 ; color : # 344054 ; font - size : 16 px ; }
. panel - title p { margin : 5 px 0 0 ; color : var ( -- cc - muted ) ; font - size : 11 px ; }
. panel - title > svg { color : var ( -- cc - primary ) ; }
. order - info - grid { display : grid ; grid - template - columns : repeat ( 3 , minmax ( 0 , 1 fr ) ) ; gap : 1 px ; margin : 0 ; overflow : hidden ; border : 1 px solid # edf0f4 ; background : # edf0f4 ; }
. order - info - grid > div , . measurement - grid > div { min - width : 0 ; padding : 14 px 16 px ; background : # fff ; }
. order - info - grid dt , . measurement - grid dt { margin - bottom : 6 px ; color : var ( -- cc - muted ) ; font - size : 11 px ; }
. order - info - grid dd , . measurement - grid dd { margin : 0 ; color : # 25324 a ; font - size : 13 px ; font - weight : 650 ; line - height : 1.5 ; overflow - wrap : anywhere ; }
. order - info - grid { display : grid ; grid - template - columns : repeat ( 3 , minmax ( 0 , 1 fr ) ) ; gap : 0 ; margin : 0 ; overflow : hidden ; border : 1 px solid # edf0f4 ; background : # fff ; }
. order - info - grid > div , . measurement - grid > div { display : flex ; min - width : 0 ; min - height : 38 px ; align - items : baseline ; gap : 8 px ; padding : 8 px 12 px ; border - right : 1 px solid # edf0f4 ; border - bottom : 1 px solid # edf0f4 ; background : # fff ; }
. order - info - grid > div . wide { grid - column : span 2 ; }
. order - info - grid dt , . measurement - grid dt { flex : 0 0 auto ; margin : 0 ; color : var ( -- cc - muted ) ; font - size : 11 px ; white - space : nowrap ; }
. order - info - grid dd , . measurement - grid dd { min - width : 0 ; margin : 0 ; color : # 25324 a ; font - size : 12 px ; font - weight : 650 ; line - height : 1.35 ; overflow - wrap : anywhere ; }
. detail - table - wrap { overflow - x : auto ; }
. detail - table - wrap : deep ( . el - table ) { min - width : 720 px ; -- el - table - header - bg - color : # f8fafc ; }
. measurement - grid { display : grid ; grid - template - columns : repeat ( 4 , minmax ( 0 , 1 fr ) ) ; gap : 1 px ; margin : 0 ; border : 1 px solid var ( -- cc - border ) ; background : var ( -- cc - border ) ; }
@@ -854,6 +875,6 @@ onMounted(() => {
. notes - list li > p { margin : 0 ; color : var ( -- cc - text ) ; font - size : 13 px ; }
. notes - list span { display : block ; margin - top : 5 px ; color : var ( -- cc - muted ) ; font - size : 12 px ; }
@ media ( max - width : 960 px ) { . detail - overview { grid - template - columns : repeat ( 2 , minmax ( 0 , 1 fr ) ) ; } . detail - overview article : nth - child ( 2 ) { border - right : 0 ; } . detail - overview article : nth - child ( - n + 2 ) { border - bottom : 1 px solid var ( -- cc - border ) ; } . order - info - grid { grid - template - columns : repeat ( 2 , minmax ( 0 , 1 fr ) ) ; } . measurement - grid { grid - template - columns : repeat ( 2 , minmax ( 0 , 1 fr ) ) ; } }
@ media ( max - width : 720 px ) { . page - intro { align - items : stretch ; flex - direction : column ; } . page - intro : deep ( . el - button ) { width : 100 % ; min - height : 44 px ; } . status - tab { min - height : 46 px ; padding : 0 14 px ; } . table - actions : deep ( . el - button ) { width : 100 % ; min - height : 40 px ; } . row - actions { flex - wrap : wrap ; } . order - detail - header { align - items : stretch ; flex - direction : column ; } . order - detail - heading { display : grid ; grid - template - columns : auto 36 px minmax ( 0 , 1 fr ) auto ; } . detail - header - actions { display : grid ; grid - template - columns : 1 fr ; } . detail - header - actions : deep ( . el - button ) { min - height : 40 px ; } . detail - overview , . order - info - grid , . measurement - grid { grid - template - columns : 1 fr ; } . detail - overview article { border - right : 0 ; border - bottom : 1 px solid var ( -- cc - border ) ; } . detail - overview article : last - child { border - bottom : 0 ; } . detail - tabs { grid - template - columns : repeat ( 6 , 132 px ) ; } . detail - panel { padding : 16 px ; } }
@ media ( max - width : 720 px ) { . page - intro { align - items : stretch ; flex - direction : column ; } . page - intro : deep ( . el - button ) { width : 100 % ; min - height : 44 px ; } . status - tab { min - height : 46 px ; padding : 0 14 px ; } . table - actions : deep ( . el - button ) { width : 100 % ; min - height : 40 px ; } . row - actions { flex - wrap : wrap ; } . order - detail - header { align - items : stretch ; flex - direction : column ; } . order - detail - heading { display : grid ; grid - template - columns : auto 36 px minmax ( 0 , 1 fr ) auto ; } . detail - header - actions { display : grid ; grid - template - columns : 1 fr ; } . detail - header - actions : deep ( . el - button ) { min - height : 40 px ; } . detail - overview , . order - info - grid , . measurement - grid { grid - template - columns : 1 fr ; } . detail - overview article { border - right : 0 ; border - bottom : 1 px solid var ( -- cc - border ) ; } . detail - overview article : last - child { border - bottom : 0 ; } . order - info - grid > div . wide { grid - column : auto ; } . detail - tabs { grid - template - columns : repeat ( 6 , 132 px ) ; } . detail - panel { padding : 16 px ; } }
@ media ( max - width : 420 px ) { . order - detail - heading { grid - template - columns : auto minmax ( 0 , 1 fr ) auto ; } . order - mark { display : none ; } }
< / style >