2026-08-10 15:36:53 +08:00
< script setup lang = "ts" >
import { computed } from 'vue'
2026-08-28 16:54:02 +08:00
import { ArrowLeft , Check , Pencil } from '@lucide/vue'
2026-08-10 15:36:53 +08:00
import ApiErrorMeta from '@/shared/components/ApiErrorMeta.vue'
import DataTable , { type DataTableColumn } from '@/shared/components/DataTable.vue'
2026-08-10 16:24:54 +08:00
import DetailPageLayout from '@/shared/components/DetailPageLayout.vue'
2026-08-10 15:36:53 +08:00
import LoadErrorState from '@/shared/components/LoadErrorState.vue'
import StatusBadge from '@/shared/components/StatusBadge.vue'
2026-08-11 19:35:28 +08:00
import { loadingListStatusLabel } from '../status'
2026-08-10 15:36:53 +08:00
import type { ApiErrorDetails } from '@/shared/api/http'
2026-09-01 11:10:22 +08:00
import type { LoadingDetail , LoadingHistory , LoadingItem , LoadingListMode , LoadingListSummary , PreloadSummary } from '../api/platformLoadingLists'
2026-08-10 15:36:53 +08:00
const props = defineProps < {
mode : LoadingListMode
loading : boolean
detail : LoadingDetail | null
error : ApiErrorDetails | null
canEdit : boolean
2026-08-31 12:12:51 +08:00
canEditTransport ?: boolean
transportNote ?: string
2026-08-15 17:04:00 +08:00
confirmSaving ?: boolean
2026-08-10 15:36:53 +08:00
} > ()
2026-08-28 16:54:02 +08:00
const emit = defineEmits < { back : []; retry : []; edit : []; editWarehouseRemark : [ item : LoadingItem ]; confirmPreload : [] } > ()
2026-08-10 15:36:53 +08:00
2026-08-18 17:40:40 +08:00
const actionColumn : DataTableColumn = { key : 'actions' , label : '操作' , fixed : 'left' , width : 132 , slotName : 'actions' , hideable : false , movable : false }
2026-08-18 16:39:44 +08:00
const baseItemColumns : DataTableColumn [] = [
2026-08-18 16:09:43 +08:00
{ key : 'sequence' , prop : 'loadSequence' , label : '装柜顺序' , width : 92 },
2026-08-18 16:39:44 +08:00
{ key : 'inboundCode' , prop : 'inboundCode' , label : '入仓码' , minWidth : 170 , className : 'identifier-column' , showOverflowTooltip : true },
{ key : 'orderNo' , prop : 'orderNo' , label : '订单号' , minWidth : 170 , className : 'identifier-column' , showOverflowTooltip : true },
2026-09-01 15:24:37 +08:00
{ key : 'containerNo' , prop : 'containerNo' , label : '集装箱号' , minWidth : 165 , className : 'identifier-column' , showOverflowTooltip : true , formatter : ( row : LoadingItem ) => row . containerNo || '待补录' },
2026-08-18 16:39:44 +08:00
{ key : 'skuCode' , prop : 'skuCode' , label : 'SKU' , minWidth : 150 , className : 'identifier-column' , showOverflowTooltip : true },
{ key : 'destinationWarehouse' , prop : 'destinationWarehouse' , label : '目的仓' , minWidth : 150 , className : 'identifier-column' , showOverflowTooltip : true },
2026-08-17 19:52:04 +08:00
{ key : 'customerName' , prop : 'customerName' , label : '客户' , minWidth : 150 },
2026-09-08 17:38:09 +08:00
{ key : 'taxRefundFlag' , label : '是否退税' , width : 96 , align : 'center' , slotName : 'taxRefundFlag' },
2026-08-17 19:52:04 +08:00
{ key : 'chineseName' , prop : 'chineseName' , label : '中文品名' , minWidth : 160 },
{ key : 'englishName' , prop : 'englishName' , label : '英文品名' , minWidth : 170 },
{ key : 'netWeightKg' , label : '净重(kg)' , width : 110 , align : 'right' , slotName : 'netWeight' },
2026-08-10 15:36:53 +08:00
{ key : 'packageCount' , prop : 'packageCount' , label : '箱数' , width : 86 , align : 'right' },
{ key : 'palletCount' , prop : 'palletCount' , label : '托盘数' , width : 86 , align : 'right' },
{ key : 'volumeCbm' , label : '体积(m³)' , width : 110 , align : 'right' , slotName : 'volume' },
2026-08-17 19:52:04 +08:00
{ key : 'grossWeightKg' , label : '毛重(kg)' , width : 110 , align : 'right' , slotName : 'grossWeight' },
{ key : 'dimensionalWeightKg' , label : '材积重(kg)' , width : 115 , align : 'right' , slotName : 'dimensionalWeight' },
{ key : 'productQuantity' , prop : 'productQuantity' , label : '产品数' , width : 96 , align : 'right' },
{ key : 'unitsPerPackage' , prop : 'unitsPerPackage' , label : '每箱产品数' , width : 110 , align : 'right' },
2026-08-10 15:36:53 +08:00
{ key : 'fbaTrackingNo' , prop : 'fbaTrackingNo' , label : 'FBA 追踪编码' , minWidth : 155 },
{ key : 'shipmentNo' , prop : 'shipmentNo' , label : '货件号' , minWidth : 140 },
{ key : 'shippingMark' , prop : 'shippingMark' , label : '唛头' , minWidth : 130 },
2026-08-18 16:09:43 +08:00
{ key : 'customerRemark' , prop : 'customerRemark' , label : '备注(客户)' , minWidth : 190 , slotName : 'customerRemark' },
{ key : 'internalRemark' , prop : 'internalRemark' , label : '备注(内部)' , minWidth : 190 , slotName : 'internalRemark' },
{ key : 'warehouseRemark' , prop : 'warehouseRemark' , label : '备注(仓库)' , minWidth : 210 , slotName : 'warehouseRemark' },
2026-08-10 15:36:53 +08:00
{ key : 'frozenAt' , label : '冻结时间' , minWidth : 150 , slotName : 'frozenAt' },
]
const summary = computed < LoadingListSummary | null >(() => props . detail ? . summary || null )
const preloadSummary = computed (() => props . mode === 'preload' ? summary . value as PreloadSummary | null : null )
2026-08-18 16:39:44 +08:00
const canEditItemRemark = computed (() => props . mode === 'preload' && props . canEdit && [ 'EDITING' , 'LOADING' ]. includes ( summary . value ? . status || '' ))
const itemColumns = computed (() => canEditItemRemark . value ? [ actionColumn , ... baseItemColumns ] : baseItemColumns )
2026-09-01 11:10:22 +08:00
const volatileSnapshotFields = new Set ([ 'requestHash' , 'version' , 'updatedAt' , 'operatorId' , 'operatorName' , 'occurredAt' ])
function stableSnapshotValue ( value : unknown ) : unknown {
if ( Array . isArray ( value )) return value . map ( stableSnapshotValue )
if ( ! value || typeof value !== 'object' ) return value
return Object . fromEntries ( Object . entries ( value as Record < string , unknown >)
. filter (([ key ]) => ! volatileSnapshotFields . has ( key ))
. sort (([ left ], [ right ]) => left . localeCompare ( right ))
. map (([ key , nested ]) => [ key , stableSnapshotValue ( nested )]))
}
function normalizedSnapshot ( value : string | null ) {
if ( ! value ) return null
try { return stableSnapshotValue ( JSON . parse ( value ) as unknown ) } catch { return value }
}
function isPureStatusNoop ( history : LoadingHistory ) {
return history . actionType === 'STATUS_UPDATE'
&& history . fromStatus === history . toStatus
&& JSON . stringify ( normalizedSnapshot ( history . beforeSnapshot )) === JSON . stringify ( normalizedSnapshot ( history . afterSnapshot ))
}
function historyEventKey ( history : LoadingHistory ) {
if ( history . operationId ) return `operation: ${ history . operationId } `
return history . historyId ? `history: ${ history . historyId } ` : null
}
const visibleHistories = computed (() => {
const seen = new Set < string >()
return ( props . detail ? . histories || []). filter (( history ) => {
if ( isPureStatusNoop ( history )) return false
const eventKey = historyEventKey ( history )
if ( ! eventKey ) return true
if ( seen . has ( eventKey )) return false
seen . add ( eventKey )
return true
})
})
2026-08-15 17:04:00 +08:00
const blockingReasonLabels : Record < string , string > = {
LOADING_PLAN_NOT_EDITING : '装柜计划已不在可确认的编辑状态' ,
PRELOAD_LINK_MISSING : '装柜明细尚未完整关联入库预装票据' ,
OCEAN_BOOKING_NOT_LINKED : '尚未关联真实订舱' ,
OCEAN_BOOKING_NOT_RELEASED : '关联订舱尚未放舱' ,
PDA_TASK_ALREADY_CREATED : 'PDA 任务已创建,无需重复确认' ,
PRELOAD_ALREADY_CONFIRMED : '整份预装计划已经确认' ,
}
const confirmationStatusLabels : Record < string , string > = {
PENDING : '待确认' , PARTIALLY_CONFIRMED : '部分票据已确认' , CONFIRMED : '已确认' ,
}
const oceanBookingStatusLabels : Record < string , string > = {
CONFIRMED : '已确认' , CHANGE_PENDING : '改船处理中' , RELEASED : '已放舱' , CANCELLED : '已取消' ,
}
const pdaTaskStatusLabels : Record < string , string > = {
PENDING : '待领取' , CLAIMED : '已领取' , LOADING : '作业中' , AWAITING_REVIEW : '待人工复核' , COMPLETED : '已完成' ,
}
const blockingReasonText = computed (() => ( preloadSummary . value ? . readiness . blockingReasons || [])
. map (( reason ) => blockingReasonLabels [ reason ] || '存在未满足的确认条件' )
. join ( '; ' ))
2026-08-10 15:36:53 +08:00
const title = computed (() => {
if ( ! summary . value ) return props . mode === 'preload' ? '预装清单详情' : '装柜清单详情'
const number = props . mode === 'preload'
? ( summary . value as PreloadSummary ). preloadNo
: 'loadingListNo' in summary . value ? summary . value . loadingListNo : ''
return ` ${ props . mode === 'preload' ? '预装清单' : '装柜清单' } · ${ number } `
})
function actionLabel ( action : string ) {
return ({
CREATE : '创建计划' , ASSIGN : '分配货物' , ADJUST : '调整货物' , DELETE : '移除货物' ,
RECOMMEND : '智能排柜' , PUSH_PRELOAD : '推送预装' , UPDATE_TRANSPORT : '修改运输信息' ,
2026-08-15 17:04:00 +08:00
CONFIRM_PRELOAD : '确认预装' ,
2026-08-11 11:52:30 +08:00
START_LOADING : '开始拣货' , COMPLETE : '完成装柜' ,
2026-08-12 09:25:55 +08:00
} as Record < string , string >)[ action ] || '状态更新'
2026-08-10 15:36:53 +08:00
}
function formatDateTime ( value ?: string | null ) {
return value ? value . replace ( 'T' , ' ' ). slice ( 0 , 16 ) : '—'
}
function formatNumber ( value ?: number | null , digits = 3 ) {
if ( value === null || value === undefined || ! Number . isFinite ( Number ( value ))) return '—'
return Number ( value ). toLocaleString ( 'zh-CN' , { maximumFractionDigits : digits })
}
</ script >
< template >
2026-08-12 16:31:46 +08:00
< section v-loading = "loading" class="loading-detail-content" :aria-label="title" :aria-busy="loading" >
2026-08-18 17:40:40 +08:00
< section class = "detail-overview" aria -label =" 清单概览 " >
2026-08-18 16:39:44 +08:00
< header class = "detail-page-header" >
< el-button link type = "primary" class = "detail-page-header__back" @click ="emit('back')" >< ArrowLeft :size = "16" /> 返回列表 </ el-button >
< div class = "detail-page-header__identity" >
< h1 >{{ title }}</ h1 >
2026-08-28 16:54:02 +08:00
< p >{{ mode === 'preload' ? '核对运输信息、确认预装并维护逐条仓库备注' : '查看已完成装柜明细和柜次费用' }}</ p >
2026-08-18 16:39:44 +08:00
</ div >
< div v-if = "detail && summary" class="detail-page-header__actions" >
< template v-if = "canEditItemRemark" >
2026-09-01 15:24:37 +08:00
< el-button v-if = "canEditTransport" @click="emit('edit')"><Pencil :size="16" />{{ summary . containerNo ? '纠正集装箱号 / 航次' : '补录集装箱号 / 航次' }}</ el-button >
2026-08-18 17:40:40 +08:00
< el-button type = "primary" :loading = "confirmSaving" :disabled = "!preloadSummary?.readiness.canConfirm" @click ="emit('confirmPreload')" >< Check :size = "16" /> 确认预装 </ el-button >
2026-08-18 16:39:44 +08:00
</ template >
</ div >
</ header >
< section v-if = "detail && summary" class="detail-summary" aria-label="清单基本信息" >
2026-08-15 17:04:00 +08:00
< div >< span > 柜次号 </ span >< strong >{{ summary . cabinetNo || '—' }}</ strong ></ div >
2026-09-01 15:24:37 +08:00
< div >< span > 集装箱号 </ span >< strong >{{ summary . containerNo || '待补录' }}</ strong ></ div >
2026-08-10 15:36:53 +08:00
< div >< span > 仓库 </ span >< strong >{{ summary . warehouseName || '—' }}</ strong ></ div >
2026-08-11 19:35:28 +08:00
< div >< span > 状态 </ span >< StatusBadge :status = "loadingListStatusLabel(summary.status)" /></ div >
2026-08-10 15:36:53 +08:00
< template v-if = "preloadSummary" >
< div >< span > 船名 / 航次 </ span >< strong >{{ preloadSummary . vesselName || '—' }} / {{ preloadSummary . voyageNo || '—' }}</ strong ></ div >
< div >< span > 箱型 </ span >< strong >{{ preloadSummary . containerType || '—' }}</ strong ></ div >
2026-08-15 17:04:00 +08:00
< div >< span > 预装确认 </ span >< StatusBadge : status = "confirmationStatusLabels[preloadSummary.readiness.confirmationStatus] || '未知状态'" /></ div >
< div >< span > 订舱状态 </ span >< strong >{{ preloadSummary . readiness . oceanBookingStatus ? ( oceanBookingStatusLabels [ preloadSummary . readiness . oceanBookingStatus ] || '未知状态' ) : '未关联' }}</ strong ></ div >
< div >< span > PDA 状态 </ span >< strong >{{ preloadSummary . readiness . pdaTaskStatus ? ( pdaTaskStatusLabels [ preloadSummary . readiness . pdaTaskStatus ] || '未知状态' ) : ( preloadSummary . readiness . readyForPda ? '可下发' : '未就绪' ) }}</ strong ></ div >
2026-08-12 10:59:49 +08:00
< div class = "detail-summary__wide" >< span > 客户 </ span >< strong >{{ preloadSummary . customers . map (( item ) => item . customerName || '客户信息缺失' ). join ( '、' ) || '客户信息缺失' }}</ strong ></ div >
2026-08-10 15:36:53 +08:00
</ template >
< div >< span > 创建时间 </ span >< strong >{{ formatDateTime ( summary . createdAt ) }}</ strong ></ div >
</ section >
2026-08-18 16:39:44 +08:00
</ section >
< LoadErrorState v-if = "error" title="清单详情加载失败" :message="error.message" @retry="emit('retry')" >
< template # details >< ApiErrorMeta :details = "error" /></ template >
</ LoadErrorState >
2026-08-10 15:36:53 +08:00
2026-08-18 16:39:44 +08:00
< div v-else-if = "detail && summary" class="detail-layout" >
< DetailPageLayout :ariaLabel = "title" >
2026-08-18 17:40:40 +08:00
< div v-if = "preloadSummary && preloadSummary.readiness.confirmationStatus !== 'CONFIRMED' && !preloadSummary.readiness.canConfirm" class="readiness-note" role="status" >
< strong > 暂不能确认预装 : </ strong >
< span >{{ blockingReasonText || '请刷新详情获取最新确认条件' }}</ span >
</ div >
2026-08-31 12:12:51 +08:00
< div v-if = "preloadSummary" class="readiness-note transport-note" role="status" >< strong > 真实集装箱号 : </ strong >< span >{{ transportNote }}</ span ></ div >
2026-08-10 15:36:53 +08:00
2026-08-18 17:40:40 +08:00
< section class = "detail-section" >
2026-08-10 15:36:53 +08:00
< DataTable
:data = "detail.items"
:columns = "itemColumns"
2026-08-12 18:16:44 +08:00
stripe
2026-08-10 15:36:53 +08:00
preference -key =" loading -list-detail-items "
row -key =" itemId "
empty -text =" 暂无装柜货物明细 "
aria -label =" 装柜货物明细 "
>
2026-08-18 16:39:44 +08:00
< template # toolbar -start >< div class = "detail-section__heading" >< div >< h2 > 装柜货物明细 < span >{{ detail . items . length }} 条 </ span ></ h2 >< p > 仓库备注将随装柜完成资料下发海外仓 </ p ></ div ></ div ></ template >
2026-08-18 16:09:43 +08:00
< template # actions = "{ row }" >
2026-08-18 16:39:44 +08:00
< el-button size = "small" type = "primary" plain : aria -label ="` 编辑 $ { row.inboundCode || row.orderNo } 的仓库备注 `" @click ="emit('editWarehouseRemark', row as LoadingItem)" >< Pencil :size = "14" /> 编辑备注 </ el-button >
2026-08-18 16:09:43 +08:00
</ template >
2026-08-10 15:36:53 +08:00
< template # grossWeight = "{ row }" >{{ formatNumber ( row . grossWeightKg ) }}</ template >
< template # netWeight = "{ row }" >{{ formatNumber ( row . netWeightKg ) }}</ template >
2026-09-08 17:38:09 +08:00
< template # taxRefundFlag = "{ row }" >{{ row . taxRefundFlag === true ? '是' : row . taxRefundFlag === false ? '否' : '—' }}</ template >
2026-08-10 15:36:53 +08:00
< template # volume = "{ row }" >{{ formatNumber ( row . volumeCbm ) }}</ template >
2026-08-17 19:52:04 +08:00
< template # dimensionalWeight = "{ row }" >{{ formatNumber ( row . dimensionalWeightKg ) }}</ template >
2026-08-18 16:09:43 +08:00
< template # customerRemark = "{ row }" >< div class = "remark-cell" >< span >{{ row . customerRemark || '—' }}</ span >< small > 来源 : 客户下单 </ small ></ div ></ template >
< template # internalRemark = "{ row }" >< div class = "remark-cell" >< span >{{ row . internalRemark || '—' }}</ span >< small > 来源 : 订单内部备注 </ small ></ div ></ template >
< template # warehouseRemark = "{ row }" >< div class = "remark-cell" >< span >{{ row . warehouseRemark || '—' }}</ span >< small >{{ mode === 'preload' ? '由仓库维护' : '装柜完成时冻结' }}</ small ></ div ></ template >
2026-08-10 15:36:53 +08:00
< template # frozenAt = "{ row }" >{{ formatDateTime ( row . frozenAt ) }}</ template >
</ DataTable >
</ section >
2026-08-18 17:40:40 +08:00
< section class = "history-section" aria -label =" 操作历史 " >
2026-09-01 11:10:22 +08:00
< header >< h2 > 操作历史 </ h2 >< span >{{ visibleHistories . length }} 条 </ span ></ header >
< el-empty v-if = "visibleHistories.length === 0" description="暂无操作历史" :image-size="72" />
2026-08-10 15:36:53 +08:00
< el-timeline v-else >
2026-09-01 11:10:22 +08:00
< el-timeline-item v-for = "history in visibleHistories" :key="historyEventKey(history) || history.historyId" :timestamp="formatDateTime(history.occurredAt)" placement="top" >
2026-08-10 15:36:53 +08:00
< div class = "history-entry" >
< strong >{{ actionLabel ( history . actionType ) }}</ strong >
2026-08-11 19:35:28 +08:00
< span v-if = "history.fromStatus || history.toStatus" >{{ loadingListStatusLabel ( history.fromStatus ) }} → {{ loadingListStatusLabel ( history.toStatus ) }}</ span >
2026-08-12 10:59:49 +08:00
< p >{{ history . operatorName || '操作人信息缺失' }}< template v-if = "history.operationReason" > · {{ history.operationReason }}</ template ></ p >
2026-08-10 15:36:53 +08:00
</ div >
</ el -timeline-item >
</ el-timeline >
</ section >
2026-08-10 16:24:54 +08:00
</ DetailPageLayout >
2026-08-10 15:36:53 +08:00
</ div >
2026-08-12 16:31:46 +08:00
</ section >
2026-08-10 15:36:53 +08:00
</ template >
< style scoped >
2026-08-18 17:40:40 +08:00
. loading - detail - content { display : grid ; min - width : 0 ; gap : 16 px ; padding : 16 px ; border : 1 px solid var ( -- cc - border ); border - radius : 8 px ; background : # fff ; }
. detail - layout { display : grid ; min - width : 0 ; }
. detail - overview { min - width : 0 ; }
. detail - page - header { display : grid ; grid - template - columns : auto minmax ( 0 , 1 fr ) auto ; align - items : center ; gap : 16 px ; padding : 8 px 0 12 px ; }
2026-08-18 16:39:44 +08:00
. detail - page - header__back { margin - right : 2 px ; }
. detail - page - header__identity { min - width : 0 ; }
. detail - page - header__identity h1 { margin : 0 ; color : var ( -- cc - text ); font - size : 18 px ; line - height : 1.35 ; }
. detail - page - header__identity p { margin : 3 px 0 0 ; color : var ( -- cc - muted ); font - size : 12 px ; }
. detail - page - header__actions { display : flex ; align - items : center ; justify - content : flex - end ; gap : 8 px ; }
. detail - page - header__actions : deep (. el - button + . el - button ) { margin - left : 0 ; }
2026-08-18 17:40:40 +08:00
. detail - summary { display : grid ; grid - template - columns : repeat ( 4 , minmax ( 0 , 1 fr )); gap : 6 px 28 px ; padding : 10 px 0 2 px ; border - top : 1 px solid var ( -- cc - border ); }
. detail - summary > div { display : flex ; min - width : 0 ; min - height : 26 px ; align - items : center ; gap : 6 px ; }
. detail - summary > div > span : first - child { flex : 0 0 auto ; color : var ( -- cc - muted ); font - size : 12 px ; white - space : nowrap ; }
. detail - summary > div > span : first - child :: after { content : ": " ; }
. detail - summary strong { min - width : 0 ; color : var ( -- cc - text ); font - size : 13 px ; overflow - wrap : anywhere ; }
2026-08-18 16:39:44 +08:00
. detail - summary : deep (. status - badge ) { width : fit - content ; }
2026-08-10 15:36:53 +08:00
. detail - summary small { color : var ( -- cc - muted ); font - size : 11 px ; }
2026-08-18 17:40:40 +08:00
. detail - summary__wide { grid - column : auto ; }
. detail - layout : deep (. detail - page - layout__content ) { display : grid ; min - width : 0 ; gap : 20 px ; }
. readiness - note { display : flex ; min - width : 0 ; align - items : center ; padding : 2 px 4 px ; color : var ( -- cc - text ); font - size : 12 px ; }
. readiness - note strong { flex : 0 0 auto ; color : var ( -- el - color - warning - dark - 2 ); }
. readiness - note span { min - width : 0 ; color : var ( -- cc - muted ); overflow : hidden ; text - overflow : ellipsis ; white - space : nowrap ; }
. detail - section , . history - section { min - width : 0 ; overflow : hidden ; padding : 16 px 0 0 ; border - top : 1 px solid var ( -- cc - border ); }
2026-08-18 16:39:44 +08:00
. detail - section__heading h2 { margin : 0 ; font - size : 15 px ; }
. detail - section__heading h2 span { margin - left : 6 px ; color : var ( -- cc - muted ); font - size : 12 px ; font - weight : 400 ; }
. detail - section__heading p { margin : 2 px 0 0 ; color : var ( -- cc - muted ); font - size : 11 px ; line - height : 1.35 ; }
. detail - section : deep (. data - table__toolbar ) { min - height : 52 px ; padding : 7 px 12 px ; }
2026-08-10 15:36:53 +08:00
. detail - section : deep (. el - table ) { width : 100 % ; }
2026-08-18 16:39:44 +08:00
. detail - section : deep (. el - table th . el - table__cell ) { padding : 7 px 0 ; }
. detail - section : deep (. el - table td . el - table__cell ) { padding : 6 px 0 ; }
. detail - section : deep (. identifier - column . cell ) { overflow : hidden ; text - overflow : ellipsis ; white - space : nowrap ; }
2026-08-10 15:36:53 +08:00
. detail - section small { display : block ; margin - top : 3 px ; color : var ( -- cc - muted ); }
2026-08-18 16:09:43 +08:00
. remark - cell { min - width : 0 ; line - height : 1.45 ; overflow - wrap : anywhere ; }
2026-08-10 15:36:53 +08:00
. history - section header { display : flex ; align - items : baseline ; gap : 10 px ; margin - bottom : 18 px ; }
. history - section h2 { margin : 0 ; font - size : 16 px ; }
. history - section header span { color : var ( -- cc - muted ); font - size : 12 px ; }
. history - entry { display : flex ; flex - wrap : wrap ; align - items : center ; gap : 8 px ; }
. history - entry > span { color : var ( -- cc - muted ); font - size : 12 px ; }
. history - entry p { width : 100 % ; margin : 5 px 0 0 ; color : var ( -- cc - muted ); font - size : 12 px ; }
</ style >