This commit is contained in:
2026-09-14 17:54:21 +08:00
parent 7e1b9b4cee
commit 25825685c3
40 changed files with 850 additions and 705 deletions
@@ -144,7 +144,7 @@ watch([page, pageSize], ([nextPage, nextSize], [previousPage, previousSize]) =>
</DataTable>
</template>
<AppDrawer class="customer-audit-drawer" v-model="detailOpen" title="操作日志详情" size="min(920px, 96vw)" :loading="detailLoading" :show-footer="false">
<AppDrawer class="customer-audit-drawer admin-density-drawer" v-model="detailOpen" title="操作日志详情" size="min(920px, 96vw)" :loading="detailLoading" :show-footer="false">
<LoadErrorState v-if="detailError" title="详情加载失败" :message="detailError.message" :loading="detailLoading"><template #details><ApiErrorMeta :details="detailError" /></template></LoadErrorState>
<template v-else-if="detail">
<section class="audit-detail-hero" :class="detail.success ? 'is-success' : 'is-failed'">
@@ -172,13 +172,13 @@ watch([page, pageSize], ([nextPage, nextSize], [previousPage, previousSize]) =>
</template>
<style scoped>
:global(.customer-audit-drawer .el-drawer__header) { margin-bottom: 0; padding: 12px 16px; border-bottom: 1px solid var(--cc-border); }
:global(.customer-audit-drawer .el-drawer__body) { padding: 12px 16px; }
.customer-audit-panel { min-width:0; }.panel-title { display:flex; align-items:flex-start; justify-content:space-between; gap:16px; margin-bottom:8px; padding-bottom:8px; border-bottom:1px solid var(--cc-border); }.panel-title h2 { margin:0; color:#344054; font-size:14px; }.panel-title p { margin:5px 0 0; color:var(--cc-muted); font-size:12px; }.panel-title > svg { color:var(--cc-primary); }.audit-query { margin-bottom:12px; }.module-empty { display:flex; min-height:64px; align-items:center; justify-content:center; flex-direction:column; gap:7px; color:#98a2b3; text-align:center; }.module-empty strong { color:#475467; font-size:13px; }.module-empty p { margin:0; color:var(--cc-muted); font-size:12px; }
.cell-clamp { display:-webkit-box; overflow:hidden; -webkit-box-orient:vertical; -webkit-line-clamp:2; line-height:1.45; overflow-wrap:anywhere; }.event-label { display:inline-flex; max-width:100%; padding:3px 9px; overflow:hidden; border:1px solid #dbeafe; border-radius:999px; background:#eff6ff; color:#1d4ed8; font-size:12px; line-height:1.35; text-overflow:ellipsis; white-space:nowrap; }.service-label { display:block; overflow:hidden; color:#475467; text-overflow:ellipsis; white-space:nowrap; }
.audit-detail-hero { display:flex; align-items:flex-start; gap:13px; padding:8px 10px; border:1px solid #d1fadf; border-radius:0; background:#f6fef9; }.audit-detail-hero.is-failed { border-color:#fee4e2; background:#fff7f6; }.result-icon { display:grid; width:38px; height:38px; flex:0 0 auto; place-items:center; border-radius:50%; background:#dcfae6; color:#079455; }.is-failed .result-icon { background:#fee4e2; color:#d92d20; }.hero-copy { min-width:0; flex:1; }.hero-copy > span { color:#667085; font-size:12px; }.hero-copy h3 { margin:3px 0 4px; color:#101828; font-size:14px; }.hero-copy p { margin:0; color:#475467; font-size:13px; line-height:1.55; }
.audit-detail-grid { display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); margin-top:16px; overflow:hidden; border:1px solid #e4e7ec; border-radius:0; background:#fff; }.audit-detail-grid > div { display:flex; min-width:0; min-height:36px; flex-direction:column; justify-content:center; gap:5px; padding:6px 10px; border-right:1px solid #eaecf0; border-bottom:1px solid #eaecf0; }.audit-detail-grid > div:nth-child(3n) { border-right:0; }.audit-detail-grid > div.wide { grid-column:1/-1; border-right:0; }.audit-detail-grid > div:last-child { border-bottom:0; }.audit-detail-grid span { color:#667085; font-size:12px; }.audit-detail-grid strong { overflow-wrap:anywhere; color:#1d2939; font-size:14px; font-weight:600; }.audit-detail-grid small { overflow-wrap:anywhere; color:#98a2b3; font-size:12px; }.request-cell > div { display:flex; min-width:0; align-items:center; gap:9px; }.request-cell code { min-width:0; overflow-wrap:anywhere; color:#344054; font:12px/1.55 ui-monospace,SFMono-Regular,Consolas,monospace; }.failure-alert { margin-top:16px; }
.snapshot-grid { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:16px; margin-top:18px; }.snapshot-grid section { min-width:0; padding:10px; border:1px solid #e4e7ec; border-radius:0; background:#fff; }.snapshot-grid h3 { margin:0; color:#344054; font-size:14px; }.snapshot-grid p { margin:4px 0 10px; color:#98a2b3; font-size:12px; }.snapshot-grid pre { min-height:160px; max-height:360px; margin:0; padding:14px; overflow:auto; border:1px solid #eaecf0; border-radius:8px; background:#f8fafc; color:#344054; font:12px/1.65 ui-monospace,SFMono-Regular,Consolas,monospace; white-space:pre-wrap; overflow-wrap:anywhere; }
.customer-audit-panel { min-width:0; }.panel-title { display:flex; align-items:flex-start; justify-content:space-between; gap:var(--admin-space-6, 16px); margin-bottom:var(--admin-space-3, 8px); padding-bottom:var(--admin-space-3, 8px); border-bottom:1px solid var(--cc-border); }.panel-title h2 { margin:0; color:#344054; font-size:var(--admin-font-section, 14px); }.panel-title p { margin:5px 0 0; color:var(--cc-muted); font-size:var(--admin-font-caption, 12px); }.panel-title > svg { color:var(--cc-primary); }.audit-query { margin-bottom:var(--admin-space-5, 12px); }.module-empty { display:flex; min-height:var(--admin-empty-height, 64px); align-items:center; justify-content:center; flex-direction:column; gap:7px; color:#98a2b3; text-align:center; }.module-empty strong { color:#475467; font-size:var(--admin-font-body, 13px); }.module-empty p { margin:0; color:var(--cc-muted); font-size:var(--admin-font-caption, 12px); }
.cell-clamp { display:-webkit-box; overflow:hidden; -webkit-box-orient:vertical; -webkit-line-clamp:2; line-height:1.45; overflow-wrap:anywhere; }.event-label { display:inline-flex; max-width:100%; padding:3px 9px; overflow:hidden; border:1px solid #dbeafe; border-radius:999px; background:#eff6ff; color:#1d4ed8; font-size:var(--admin-font-caption, 12px); line-height:1.35; text-overflow:ellipsis; white-space:nowrap; }.service-label { display:block; overflow:hidden; color:#475467; text-overflow:ellipsis; white-space:nowrap; }
.audit-detail-hero { display:flex; align-items:flex-start; gap:13px; padding:var(--admin-space-3, 8px) var(--admin-space-4, 10px); border:1px solid #d1fadf; border-radius:0; background:#f6fef9; }.audit-detail-hero.is-failed { border-color:#fee4e2; background:#fff7f6; }.result-icon { display:grid; width:38px; height:var(--admin-tab-height, 38px); flex:0 0 auto; place-items:center; border-radius:50%; background:#dcfae6; color:#079455; }.is-failed .result-icon { background:#fee4e2; color:#d92d20; }.hero-copy { min-width:0; flex:1; }.hero-copy > span { color:#667085; font-size:var(--admin-font-caption, 12px); }.hero-copy h3 { margin:3px 0 var(--admin-space-1, 4px); color:#101828; font-size:var(--admin-font-section, 14px); }.hero-copy p { margin:0; color:#475467; font-size:var(--admin-font-body, 13px); line-height:1.55; }
.audit-detail-grid { display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); margin-top:var(--admin-space-6, 16px); overflow:hidden; border:1px solid #e4e7ec; border-radius:0; background:var(--admin-surface, #fff); }.audit-detail-grid > div { display:flex; min-width:0; min-height:var(--admin-row-height, 36px); flex-direction:column; justify-content:center; gap:5px; padding:var(--admin-space-2, 6px) var(--admin-space-4, 10px); border-right:1px solid #eaecf0; border-bottom:1px solid #eaecf0; }.audit-detail-grid > div:nth-child(3n) { border-right:0; }.audit-detail-grid > div.wide { grid-column:1/-1; border-right:0; }.audit-detail-grid > div:last-child { border-bottom:0; }.audit-detail-grid span { color:#667085; font-size:var(--admin-font-caption, 12px); }.audit-detail-grid strong { overflow-wrap:anywhere; color:#1d2939; font-size:var(--admin-font-section, 14px); font-weight:600; }.audit-detail-grid small { overflow-wrap:anywhere; color:#98a2b3; font-size:var(--admin-font-caption, 12px); }.request-cell > div { display:flex; min-width:0; align-items:center; gap:9px; }.request-cell code { min-width:0; overflow-wrap:anywhere; color:#344054; font:12px/1.55 ui-monospace,SFMono-Regular,Consolas,monospace; }.failure-alert { margin-top:var(--admin-space-6, 16px); }
.snapshot-grid { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:var(--admin-space-6, 16px); margin-top:18px; }.snapshot-grid section { min-width:0; padding:var(--admin-space-4, 10px); border:1px solid #e4e7ec; border-radius:0; background:var(--admin-surface, #fff); }.snapshot-grid h3 { margin:0; color:#344054; font-size:var(--admin-font-section, 14px); }.snapshot-grid p { margin:var(--admin-space-1, 4px) 0 var(--admin-space-4, 10px); color:#98a2b3; font-size:var(--admin-font-caption, 12px); }.snapshot-grid pre { min-height:160px; max-height:360px; margin:0; padding:14px; overflow:auto; border:1px solid #eaecf0; border-radius:8px; background:var(--admin-label-bg, #f8fafc); color:#344054; font:12px/1.65 ui-monospace,SFMono-Regular,Consolas,monospace; white-space:pre-wrap; overflow-wrap:anywhere; }
@media(max-width:720px){.audit-detail-grid{grid-template-columns:1fr}.audit-detail-grid>div,.audit-detail-grid>div:nth-child(3n){border-right:0}.audit-detail-grid>div.wide{grid-column:auto}.snapshot-grid{grid-template-columns:1fr}.audit-detail-hero{align-items:flex-start}.audit-detail-hero>.el-tag{flex:0 0 auto}}
</style>
@@ -81,15 +81,15 @@ onMounted(load)
</template>
<div v-else class="fee-empty">当前客户暂无海外仓费用配置</div>
<AppDialog class="admin-density-dialog customer-density-dialog" v-model="visible" title="编辑海外仓费用" width="min(1120px, calc(100vw - 28px))" :confirm-loading="saving" :show-cancel="false" @confirm="save">
<el-form ref="formRef" :model="form" label-position="top"><el-form-item label="币种"><el-select v-model="form.currency" style="width:180px"><el-option v-for="currency in currencies" :key="currency" :label="currency" :value="currency" /></el-select></el-form-item><div class="edit-groups"><div v-for="(group, groupIndex) in [storageTiers, returnTiers]" :key="groupIndex"><div class="edit-group-heading"><h3>{{ groupIndex === 0 ? '仓租(CBM/天)' : '退货仓储费(托/天)' }} · {{ group.length }}档</h3><el-button type="primary" plain size="small" :disabled="form.tiers.length >= 100" @click="addTier(groupIndex === 0 ? 'STORAGE' : 'RETURN_STORAGE')">新增阶梯</el-button></div><table><thead><tr><th>操作</th><th>开始天数</th><th>结束天数</th><th>价格</th></tr></thead><tbody><tr v-for="tier in group" :key="tier.id"><td><el-button size="small" plain type="danger" :disabled="group.length <= 1" @click="removeTier(tier)">删除</el-button></td><td><el-input-number v-model="tier.startDay" :min="1" :max="36500" :controls="false" placeholder="请输入开始天数" /></td><td><el-input-number v-if="tier.endDay !== null" v-model="tier.endDay" :min="1" :max="36500" :controls="false" placeholder="请输入结束天数" /><span v-else>以上</span></td><td><el-input-number v-model="tier.unitPrice" :min="0" :precision="6" :controls="false" placeholder="请输入价格" /></td></tr></tbody></table></div></div></el-form>
<el-form ref="formRef" :model="form" label-position="top"><el-form-item label="币种"><el-select v-model="form.currency" style="width:180px"><el-option v-for="currency in currencies" :key="currency" :label="currency" :value="currency" /></el-select></el-form-item><div class="edit-groups"><div v-for="(group, groupIndex) in [storageTiers, returnTiers]" :key="groupIndex"><div class="edit-group-heading"><h3>{{ groupIndex === 0 ? '仓租(CBM/天)' : '退货仓储费(托/天)' }} · {{ group.length }}档</h3><el-button class="density-action" data-tone="primary" type="primary" plain size="small" :disabled="form.tiers.length >= 100" @click="addTier(groupIndex === 0 ? 'STORAGE' : 'RETURN_STORAGE')">新增阶梯</el-button></div><table><thead><tr><th>操作</th><th>开始天数</th><th>结束天数</th><th>价格</th></tr></thead><tbody><tr v-for="tier in group" :key="tier.id"><td><el-button class="density-action" data-tone="danger" size="small" plain type="danger" :disabled="group.length <= 1" @click="removeTier(tier)">删除</el-button></td><td><el-input-number v-model="tier.startDay" :min="1" :max="36500" :controls="false" placeholder="请输入开始天数" /></td><td><el-input-number v-if="tier.endDay !== null" v-model="tier.endDay" :min="1" :max="36500" :controls="false" placeholder="请输入结束天数" /><span v-else>以上</span></td><td><el-input-number v-model="tier.unitPrice" :min="0" :precision="6" :controls="false" placeholder="请输入价格" /></td></tr></tbody></table></div></div></el-form>
<template #footer><div class="dialog-actions"><el-button :disabled="saving" @click="closeEditor">取消</el-button><el-button type="primary" :loading="saving" @click="save">保存费用</el-button></div></template>
</AppDialog>
</section>
</template>
<style scoped>
.overseas-fee-panel { margin-top: 18px; border: 1px solid var(--cc-border); background: #fff; }.panel-heading { display:flex; align-items:center; justify-content:space-between; gap:16px; padding:18px; border-bottom:1px solid var(--cc-border); }.panel-heading h2,.fee-groups h3,.edit-groups h3 { margin:0; color:var(--cc-text); font-size:16px; }.panel-heading p { margin:5px 0 0; color:var(--cc-muted); font-size:12px; }.fee-meta { display:flex; flex-wrap:wrap; gap:24px; padding:12px 18px; color:var(--cc-muted); font-size:12px; background:#f8fafc; }.fee-meta strong { color:var(--cc-text); }.fee-groups,.edit-groups { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:18px; padding:18px; }.fee-groups > div,.edit-groups > div { min-width:0; }.fee-groups table,.edit-groups table { width:100%; border-collapse:collapse; font-size:12px; }.edit-groups table { table-layout:fixed; }.fee-groups th,.fee-groups td,.edit-groups th,.edit-groups td { padding:9px 10px; border-bottom:1px solid var(--cc-border); text-align:left; }.fee-groups th,.edit-groups th { color:var(--cc-muted); font-weight:650; }.edit-groups th:first-child,.edit-groups td:first-child { width:76px; }.edit-groups :deep(.el-input-number) { width:100%; min-width:0; }.fee-error { display:flex; align-items:center; justify-content:space-between; gap:16px; padding:20px; background:#fff4f3; }.fee-error p { margin:5px 0; color:#b42318; font-size:12px; }.fee-loading { min-height:220px; }.fee-empty { min-height:160px; display:grid; place-items:center; color:var(--cc-muted); }.dialog-actions { display:flex; justify-content:flex-end; gap:8px; }.dialog-actions :deep(.el-button + .el-button) { margin-left:0; }
.edit-group-heading { display:flex; align-items:center; justify-content:space-between; gap:12px; margin-bottom:10px; }
.overseas-fee-panel { border: 1px solid var(--cc-border); background: var(--admin-surface, #fff); }.panel-heading { display:flex; align-items:center; justify-content:space-between; border-bottom:1px solid var(--cc-border); }.panel-heading h2 { margin:0; color:var(--cc-text); }.fee-groups h3 { margin:0; color:var(--cc-text); }.edit-groups h3 { margin:0; color:var(--cc-text); }.panel-heading p { margin:5px 0 0; color:var(--cc-muted); }.fee-meta { display:flex; flex-wrap:wrap; gap:var(--admin-space-7, 24px); padding:var(--admin-space-5, 12px) 18px; color:var(--cc-muted); font-size:var(--admin-font-caption, 12px); background:var(--admin-label-bg, #f8fafc); }.fee-meta strong { color:var(--cc-text); }.fee-groups { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); }.edit-groups { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); }.fee-groups > div { min-width:0; }.edit-groups > div { min-width:0; }.fee-groups table { width:100%; border-collapse:collapse; font-size:var(--admin-font-caption, 12px); }.edit-groups table { width:100%; border-collapse:collapse; font-size:var(--admin-font-caption, 12px); }.edit-groups table { table-layout:fixed; }.fee-groups th { border-bottom:1px solid var(--cc-border); text-align:left; }.fee-groups td { border-bottom:1px solid var(--cc-border); text-align:left; }.edit-groups th { border-bottom:1px solid var(--cc-border); text-align:left; }.edit-groups td { border-bottom:1px solid var(--cc-border); text-align:left; }.fee-groups th { color:var(--cc-muted); font-weight:650; }.edit-groups th { color:var(--cc-muted); font-weight:650; }.edit-groups th:first-child { width:76px; }.edit-groups td:first-child { width:76px; }.edit-groups :deep(.el-input-number) { width:100%; min-width:0; }.fee-error { display:flex; align-items:center; justify-content:space-between; gap:var(--admin-space-6, 16px); padding:20px; background:#fff4f3; }.fee-error p { margin:5px 0; color:#b42318; font-size:var(--admin-font-caption, 12px); }.fee-empty { display:grid; place-items:center; color:var(--cc-muted); }.dialog-actions { display:flex; gap:var(--admin-space-3, 8px); }.dialog-actions :deep(.el-button + .el-button) { margin-left:0; }
.edit-group-heading { display:flex; align-items:center; justify-content:space-between; gap:var(--admin-space-5, 12px); margin-bottom:var(--admin-space-4, 10px); }
.edit-group-heading h3 { min-width:0; }
.edit-group-heading :deep(.el-button) { flex:0 0 auto; }
@media (max-width:900px) { .edit-groups { grid-template-columns:1fr; } }
@@ -431,23 +431,23 @@ onBeforeUnmount(stopInitializationPolling)
<style scoped>
.dimension-rule-panel { min-width: 0; }
.rule-heading { align-items: center; }
.rule-heading h2 { margin: 0; color: var(--cc-text); font-size: 18px; }.rule-heading p { margin: 6px 0 0; color: var(--cc-muted); font-size: 13px; }
.heading-actions,.dialog-actions { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; }.heading-actions :deep(.el-button),.dialog-actions :deep(.el-button) { margin-left: 0; }
.rule-card { min-width: 0; }.rule-card-heading { display: flex; min-height: 68px; align-items: center; justify-content: space-between; gap: 16px; padding: 12px 18px; border-bottom: 1px solid var(--cc-border); }.rule-card-heading h2 { margin: 0; color: var(--cc-text); font-size: 18px; }.formula-band { display: flex; min-height: 52px; align-items: center; flex-wrap: wrap; gap: 12px 30px; padding: 12px 18px; border-bottom: 1px solid var(--cc-border); background: #f8fafc; color: var(--cc-muted); font-size: 13px; }.formula-band strong { color: #344054; }.loading-state { min-height: 280px; }
.rule-title-row { display: flex; align-items: center; flex-wrap: wrap; gap: 14px; }.rule-title-row :deep(.el-checkbox) { height: 30px; margin-right: 0; padding: 0 10px; border: 1px solid var(--cc-border); border-radius: 6px; background: #fff; }.rule-title-row :deep(.el-checkbox.is-checked) { border-color: var(--cc-primary); background: var(--cc-primary-soft); }
.rule-state { display: flex; min-height: 220px; align-items: center; justify-content: center; flex-direction: column; gap: 8px; color: #8797b2; text-align: center; }.rule-state strong { color: #475467; font-size: 13px; }.rule-state p { margin: 0; color: var(--cc-muted); font-size: 12px; }
.rule-error { display: flex; min-height: 180px; align-items: center; justify-content: space-between; gap: 18px; padding: 20px; background: #fff4f3; }.rule-error strong { color: #7a271a; }.rule-error p { margin: 5px 0; color: #b42318; font-size: 12px; }
.volume-value { color: #1570ef; }.editor-content { display: grid; gap: 20px; }.editor-formula { margin: 0; padding: 13px 15px; color: #475467; font-size: 13px; line-height: 1.7; }.editor-table-wrap { overflow-x: auto; }.editor-table { width: 100%; min-width: 920px; border-collapse: collapse; }.editor-table th,.editor-table td { padding: 10px 12px; border-bottom: 1px solid var(--cc-border); text-align: left; white-space: nowrap; }.editor-table thead th { background: #eef0fa; color: #475467; font-size: 12px; }.editor-table tbody th { color: var(--cc-text); font-size: 13px; }.editor-table :deep(.el-input-number),.editor-table :deep(.el-input) { width: 140px; }.editor-table :deep(.el-input-number .el-input__wrapper) { width: 100%; }
.charge-rule-card { margin-top: 18px; }.section-hint { margin: 5px 0 0; color: var(--cc-muted); font-size: 12px; font-weight: 400; }.charge-form { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 16px; }.charge-form :deep(.el-form-item:last-child) { grid-column: 1 / -1; }.charge-form :deep(.el-input-number), .charge-form :deep(.el-input) { width: 100%; }
.rule-heading h2 { margin: 0; color: var(--cc-text); }.rule-heading p { margin: var(--admin-space-2, 6px) 0 0; color: var(--cc-muted); }
.heading-actions { display: flex; align-items: center; flex-wrap: wrap; gap: var(--admin-space-3, 8px); }
.dialog-actions { display: flex; align-items: center; flex-wrap: wrap; gap: var(--admin-space-3, 8px); }.heading-actions :deep(.el-button) { margin-left: 0; }.dialog-actions :deep(.el-button) { margin-left: 0; }
.rule-card { min-width: 0; }.rule-card-heading { display: flex; align-items: center; justify-content: space-between; border-bottom: 1px solid var(--cc-border); }.rule-card-heading h2 { margin: 0; color: var(--cc-text); }.formula-band { display: flex; align-items: center; flex-wrap: wrap; border-bottom: 1px solid var(--cc-border); background: var(--admin-label-bg, #f8fafc); color: var(--cc-muted); font-size: var(--admin-font-body, 13px); }.formula-band strong { color: #344054; }
.rule-title-row { display: flex; align-items: center; flex-wrap: wrap; gap: 14px; }.rule-title-row :deep(.el-checkbox) { height: 30px; margin-right: 0; padding: 0 var(--admin-space-4, 10px); border: 1px solid var(--cc-border); border-radius: 6px; background: var(--admin-surface, #fff); }.rule-title-row :deep(.el-checkbox.is-checked) { border-color: var(--cc-primary); background: var(--cc-primary-soft); }
.rule-state { display: flex; align-items: center; justify-content: center; flex-direction: column; gap: var(--admin-space-3, 8px); color: #8797b2; text-align: center; }.rule-state strong { color: #475467; font-size: var(--admin-font-body, 13px); }.rule-state p { margin: 0; color: var(--cc-muted); font-size: var(--admin-font-caption, 12px); }
.rule-error { display: flex; align-items: center; justify-content: space-between; gap: 18px; background: #fff4f3; }.rule-error strong { color: #7a271a; }.rule-error p { margin: 5px 0; color: #b42318; font-size: var(--admin-font-caption, 12px); }
.volume-value { color: #1570ef; }.editor-content { display: grid; }.editor-formula { margin: 0; color: #475467; font-size: var(--admin-font-body, 13px); line-height: 1.7; }.editor-table-wrap { overflow-x: auto; }.editor-table { width: 100%; min-width: 920px; border-collapse: collapse; }.editor-table th { border-bottom: 1px solid var(--cc-border); text-align: left; white-space: nowrap; }.editor-table td { border-bottom: 1px solid var(--cc-border); text-align: left; white-space: nowrap; }.editor-table thead th { background: #eef0fa; color: #475467; font-size: var(--admin-font-caption, 12px); }.editor-table tbody th { color: var(--cc-text); font-size: var(--admin-font-body, 13px); }.editor-table :deep(.el-input-number) { width: 140px; }.editor-table :deep(.el-input) { width: 140px; }.editor-table :deep(.el-input-number .el-input__wrapper) { width: 100%; }.section-hint { margin: 5px 0 0; color: var(--cc-muted); font-size: var(--admin-font-caption, 12px); font-weight: 400; }.charge-form { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 var(--admin-space-6, 16px); }.charge-form :deep(.el-form-item:last-child) { grid-column: 1 / -1; }.charge-form :deep(.el-input-number) { width: 100%; }.charge-form :deep(.el-input) { width: 100%; }
.charge-form--simple { grid-template-columns: 1fr; }.charge-form--simple :deep(.el-form-item) { grid-column: 1; }
.charge-rule-editor-wrap { overflow-x: auto; }.charge-rule-editor { width: 100%; min-width: 920px; border-collapse: collapse; }.charge-rule-editor th,.charge-rule-editor td { padding: 9px 10px; border-bottom: 1px solid var(--cc-border); text-align: left; white-space: nowrap; }.charge-rule-editor thead th { background: #eef0fa; color: #475467; font-size: 12px; }.charge-rule-editor tbody th { color: var(--cc-text); font-size: 13px; }.charge-rule-editor :deep(.el-input-number),.charge-rule-editor :deep(.el-input) { width: 118px; }.charge-rule-editor :deep(.el-input-number .el-input__wrapper) { width: 100%; }
.charge-rule-editor-wrap { overflow-x: auto; }.charge-rule-editor { width: 100%; min-width: 920px; border-collapse: collapse; }.charge-rule-editor th { border-bottom: 1px solid var(--cc-border); text-align: left; white-space: nowrap; }.charge-rule-editor td { border-bottom: 1px solid var(--cc-border); text-align: left; white-space: nowrap; }.charge-rule-editor thead th { background: #eef0fa; color: #475467; font-size: var(--admin-font-caption, 12px); }.charge-rule-editor tbody th { color: var(--cc-text); font-size: var(--admin-font-body, 13px); }.charge-rule-editor :deep(.el-input-number) { width: 118px; }.charge-rule-editor :deep(.el-input) { width: 118px; }.charge-rule-editor :deep(.el-input-number .el-input__wrapper) { width: 100%; }
.charge-box-cell { min-height: 34px; display: flex; align-items: center; border-bottom: 1px solid var(--cc-border); }.charge-box-cell:last-child { border-bottom: 0; }
.rule-save-row { display: flex; justify-content: flex-end; padding: 12px 0 4px; }
.dialog-actions { justify-content: center; width: 100%; }
.rule-save-row { display: flex; justify-content: flex-end; padding: var(--admin-space-5, 12px) 0 var(--admin-space-1, 4px); }
.dialog-actions { width: 100%; }
.dialog-actions :deep(.el-button) { min-width: 112px; min-height: 46px; font-size: 15px; }
.charge-rule-card :deep(.el-table) { color: #344054; font-size: 13px; }
.charge-rule-card :deep(.el-table th.el-table__cell) { background: #f8fafc; color: #475467; font-size: 13px; font-weight: 650; }
.charge-rule-card :deep(.el-table td.el-table__cell) { color: #344054; font-size: 13px; }
.charge-rule-card :deep(.el-table) { color: #344054; font-size: var(--admin-font-body, 13px); }
.charge-rule-card :deep(.el-table th.el-table__cell) { background: var(--admin-label-bg, #f8fafc); color: #475467; font-size: var(--admin-font-body, 13px); font-weight: 650; }
.charge-rule-card :deep(.el-table td.el-table__cell) { color: #344054; font-size: var(--admin-font-body, 13px); }
.charge-rule-card :deep(.el-table .cell) { line-height: 1.5; text-align: center; }
.charge-rule-card :deep(.el-table .charge-remark-column .cell) { text-align: left; }
.charge-rule-card :deep(.el-table strong) { color: #1570ef; font-weight: 650; }
+56 -86
View File
@@ -2,112 +2,82 @@
/* Only remove decoration from text actions; retain icon-only paging/settings controls. */
.customer-density.admin-density .el-button :is(.lucide-search, .lucide-rotate-ccw, .lucide-copy),
.customer-density-dialog.admin-density-dialog .el-button .lucide-copy { display: none; }
.customer-density.admin-density .customers-table-actions .el-button--primary { background: var(--el-color-primary); box-shadow: none; border: 1px solid var(--el-color-primary); }
.customer-density.admin-density .customer-action-button {
--action-color: #52525b;
--action-border: #dfe3ee;
height: 28px; min-height: 28px; padding: 4px 10px; margin: 0;
border: 1px solid var(--action-border); border-radius: 6px;
color: var(--action-color); background: #fff; font-size: 13px; font-weight: 600; box-shadow: none;
}
.customer-density.admin-density .customer-list-table .customer-action-review { --action-color: #c76500; --action-border: #fed7aa; }
.customer-density.admin-density .customer-list-table .customer-action-reset-password { --action-color: #7c3aed; --action-border: #ddd6fe; }
.customer-density.admin-density .customer-list-table .customer-action-impersonate { --action-color: #1677cf; --action-border: #bfdbfe; }
.customer-density.admin-density .customer-list-table .customer-action-status.is-enable { --action-color: #15803d; --action-border: #bbf7d0; }
.customer-density.admin-density .customer-list-table .customer-action-status.is-disable,
.customer-density.admin-density .customer-list-table .customer-action-delete { --action-color: #dc2626; --action-border: #fecaca; }
.customer-density.admin-density .customer-action-button:hover:not(:disabled) { color: var(--action-color); border-color: var(--action-color); background: #f8fafc; box-shadow: none; filter: none; }
.customer-density.admin-density .customer-action-button:disabled { color: #a1a1aa; border-color: #e5e7eb; background: #fff; }
.customer-density.admin-density .customer-action-button:focus-visible { outline: 2px solid var(--action-color); outline-offset: 2px; }
.customer-density.admin-density .table-actions { gap: 6px; }
.customer-density.admin-density .customer-list-table .table-actions > .el-button,
.customer-density.admin-density .customer-list-table .table-actions > .el-dropdown { margin-left: 0; }
.customer-density.admin-density .customer-list-table .el-table__body tr:hover > td { background: #f3f7fb; }
.customer-density.admin-density.customer-detail-page .detail-page-layout { gap: 12px; }
.customer-density.admin-density.customer-detail-page .detail-header { padding: 8px 12px; background: #fff; border: 1px solid var(--cc-border); }
.customer-density.admin-density .detail-page-toolbar h1 { font-size: 16px; line-height: 24px; }
.customer-density.admin-density .detail-page-toolbar p { margin-top: 2px; font-size: 12px; }
.customer-density.admin-density .detail-tabs { border-radius: 0; box-shadow: none; border-bottom: 1px solid var(--cc-border); background: #fff; }
.customer-density.admin-density .detail-tabs button { height: 38px; padding: 0 16px; gap: 6px; font-size: 13px; border-bottom-width: 2px; transition: color .15s, background .15s; }
.customer-density.admin-density .detail-tab-icon { width: 18px; font: 17px/1 "Segoe UI Emoji", "Apple Color Emoji", "Noto Color Emoji", sans-serif; }
.customer-density.admin-density .tab-panel { padding-top: 0; min-height: 0; background: #fff; border: 1px solid var(--cc-border); border-top: 0; }
.customer-density.admin-density.customer-detail-page .detail-page-layout { gap: var(--admin-space-5, 12px); }
.customer-density.admin-density.customer-detail-page .detail-header { padding: var(--admin-space-3, 8px) var(--admin-space-5, 12px); background: var(--admin-surface, #fff); border: 1px solid var(--cc-border); }
.customer-density.admin-density .detail-page-toolbar h1 { font-size: var(--admin-font-title, 16px); line-height: 24px; }
.customer-density.admin-density .detail-page-toolbar p { margin-top: 2px; font-size: var(--admin-font-caption, 12px); }
.customer-density.admin-density .tab-panel { padding-top: 0; min-height: 0; background: var(--admin-surface, #fff); border: 1px solid var(--cc-border); border-top: 0; }
.customer-density.admin-density .basic-layout { grid-template-columns: 1fr; gap: 0; }
.customer-density.admin-density .detail-panel { padding: 12px; border: 0; border-bottom: 1px solid var(--cc-border); border-radius: 0; background: #fff; box-shadow: none; }
.customer-density.admin-density .detail-panel { padding: var(--admin-space-5, 12px); border: 0; border-bottom: 1px solid var(--cc-border); border-radius: 0; background: var(--admin-surface, #fff); box-shadow: none; }
.customer-density.admin-density .detail-panel:last-child { border-bottom: 0; }
.customer-density.admin-density .module-panel { min-height: 0; }
.customer-density.admin-density .panel-title { align-items: center; gap: 12px; margin-bottom: 8px; padding-bottom: 8px; }
.customer-density.admin-density .panel-title h2 { font-size: 14px; }
.customer-density.admin-density .panel-title p { margin-top: 3px; font-size: 12px; }
.customer-density.admin-density .info-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 0; border-top: 1px solid var(--cc-border); border-left: 1px solid var(--cc-border); }
.customer-density.admin-density .panel-title { align-items: center; gap: var(--admin-space-5, 12px); margin-bottom: var(--admin-space-3, 8px); padding-bottom: var(--admin-space-3, 8px); }
.customer-density.admin-density .panel-title h2 { font-size: var(--admin-font-section, 14px); }
.customer-density.admin-density .panel-title p { margin-top: 3px; font-size: var(--admin-font-caption, 12px); }
.customer-density.admin-density .info-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.customer-density.admin-density .info-grid--three { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.customer-density.admin-density .info-grid > div { display: grid; grid-template-columns: 112px minmax(0, 1fr); min-height: 36px; padding: 0; border-radius: 0; border-right: 1px solid var(--cc-border); border-bottom: 1px solid var(--cc-border); background: #fff; }
.customer-density.admin-density .info-grid dt { display: flex; align-items: center; margin: 0; padding: 6px 8px; background: #f8fafc; font-size: 12px; }
.customer-density.admin-density .info-grid dd { align-self: center; margin: 0; padding: 6px 8px; font-size: 13px; font-weight: 400; overflow-wrap: anywhere; }
.customer-density.admin-density .qualification-list { gap: 0; border: 1px solid var(--cc-border); }
.customer-density.admin-density .qualification-list button { grid-template-columns: minmax(0, 1fr) 100px 120px 48px; gap: 12px; min-height: 40px; padding: 6px 10px; border: 0; border-bottom: 1px solid var(--cc-border); border-radius: 0; }
.customer-density.admin-density .qualification-list button { grid-template-columns: minmax(0, 1fr) 100px 120px 48px; gap: var(--admin-space-5, 12px); min-height: 40px; padding: var(--admin-space-2, 6px) var(--admin-space-4, 10px); border: 0; border-bottom: 1px solid var(--cc-border); border-radius: 0; }
.customer-density.admin-density .qualification-list button:last-child { border-bottom: 0; }
.customer-density.admin-density .qualification-list strong { font-size: 13px; }
.customer-density.admin-density .qualification-list small { overflow-wrap: anywhere; font-size: 12px; }
.customer-density.admin-density .file-date { font-size: 12px; }
.customer-density.admin-density .file-preview-label { color: var(--cc-primary); font-size: 13px; }
.customer-density.admin-density .section-empty,.customer-density.admin-density .module-empty { min-height: 64px; padding: 12px; gap: 4px; }
.customer-density.admin-density .qualification-list strong { font-size: var(--admin-font-body, 13px); }
.customer-density.admin-density .qualification-list small { overflow-wrap: anywhere; font-size: var(--admin-font-caption, 12px); }
.customer-density.admin-density .file-date { font-size: var(--admin-font-caption, 12px); }
.customer-density.admin-density .file-preview-label { color: var(--cc-primary); font-size: var(--admin-font-body, 13px); }
.customer-density.admin-density .section-empty,.customer-density.admin-density .module-empty { min-height: var(--admin-empty-height, 64px); padding: var(--admin-space-5, 12px); gap: var(--admin-space-1, 4px); }
.customer-density.admin-density .section-empty > svg,.customer-density.admin-density .module-empty > svg { display: none; }
.customer-density.admin-density .section-empty span { font-size: 12px; }
.customer-density.admin-density .section-empty span { font-size: var(--admin-font-caption, 12px); }
.customer-density.admin-density .user-table-wrap { border: 1px solid var(--cc-border); border-radius: 0; }
.customer-density.admin-density .user-table-wrap table { font-size: 13px; }
.customer-density.admin-density .user-table-wrap th,.customer-density.admin-density .user-table-wrap td { height: 36px; padding: 6px 10px; }
.customer-density.admin-density .user-table-wrap table { font-size: var(--admin-font-body, 13px); }
.customer-density.admin-density .user-table-wrap th,.customer-density.admin-density .user-table-wrap td { height: var(--admin-row-height, 36px); padding: var(--admin-space-2, 6px) var(--admin-space-4, 10px); }
.customer-density.admin-density .user-identity > span { display: none; }
.customer-density.admin-density .user-identity small,.customer-density.admin-density .member-contact small { font-size: 12px; }
.customer-density-dialog.admin-density-dialog.el-dialog .el-dialog__body { background: #fff; }
.customer-density-dialog.admin-density-dialog .form-section { padding: 10px 0; }
.customer-density-dialog.admin-density-dialog .form-section__heading { margin-bottom: 8px; }
.customer-density-dialog.admin-density-dialog .form-grid { gap: 0 16px; }
.customer-density-dialog.admin-density-dialog .field-hint { font-size: 12px; }
.customer-density-dialog.admin-density-dialog .qualification-row > * { min-height: 44px; padding: 6px 10px; }
.customer-density-dialog.admin-density-dialog .qualification-header > * { min-height: 36px; }
.customer-density-dialog.admin-density-dialog .file-picker { min-height: 32px; }
.customer-density.admin-density .user-identity small,.customer-density.admin-density .member-contact small { font-size: var(--admin-font-caption, 12px); }
.customer-density-dialog.admin-density-dialog .form-section { padding: var(--admin-space-4, 10px) 0; }
.customer-density-dialog.admin-density-dialog .form-section__heading { margin-bottom: var(--admin-space-3, 8px); }
.customer-density-dialog.admin-density-dialog .form-grid { gap: 0 var(--admin-space-6, 16px); }
.customer-density-dialog.admin-density-dialog .field-hint { font-size: var(--admin-font-caption, 12px); }
.customer-density-dialog.admin-density-dialog .qualification-row > * { min-height: 44px; padding: var(--admin-space-2, 6px) var(--admin-space-4, 10px); }
.customer-density-dialog.admin-density-dialog .qualification-header > * { min-height: var(--admin-row-height, 36px); }
.customer-density-dialog.admin-density-dialog .file-picker { min-height: var(--admin-control-height, 32px); }
.customer-density-dialog.admin-density-dialog .qualification-file-name { max-width: none; overflow-wrap: anywhere; white-space: normal; }
.customer-density-dialog.admin-density-dialog .qualification-preview-action { font-size: 12px; }
.customer-density-dialog.admin-density-dialog .review-dialog-content { gap: 12px; }
.customer-density-dialog.admin-density-dialog .review-dialog-header { padding: 8px 10px; border-radius: 0; background: #fff; }
.customer-density-dialog.admin-density-dialog .qualification-preview-action { font-size: var(--admin-font-caption, 12px); }
.customer-density-dialog.admin-density-dialog .review-dialog-content { gap: var(--admin-space-5, 12px); }
.customer-density-dialog.admin-density-dialog .review-dialog-header { padding: var(--admin-space-3, 8px) var(--admin-space-4, 10px); border-radius: 0; background: var(--admin-surface, #fff); }
.customer-density-dialog.admin-density-dialog .review-dialog-title strong { white-space: normal; overflow-wrap: anywhere; }
.customer-density-dialog.admin-density-dialog .review-dialog-icon { display: none; }
.customer-density-dialog.admin-density-dialog .review-dialog-grid { gap: 12px; }
.customer-density-dialog.admin-density-dialog .review-dialog-section,.customer-density-dialog.admin-density-dialog .review-decision-card { padding: 10px; border-radius: 0; box-shadow: none; }
.customer-density-dialog.admin-density-dialog .review-section-heading { margin-bottom: 8px; }
.customer-density-dialog.admin-density-dialog .review-section-heading span { font-size: 12px; }
.customer-density-dialog.admin-density-dialog .review-info-grid { grid-template-columns: 1fr; gap: 0; border-top: 1px solid var(--cc-border); }
.customer-density-dialog.admin-density-dialog .review-info-grid > div { display: grid; grid-template-columns: 96px minmax(0,1fr); padding: 0; border-radius: 0; border-bottom: 1px solid var(--cc-border); background: #fff; }
.customer-density-dialog.admin-density-dialog .review-info-grid dt { margin: 0; padding: 6px 8px; font-size: 12px; background: #f8fafc; }
.customer-density-dialog.admin-density-dialog .review-info-grid dd { align-self: center; padding: 6px 8px; white-space: normal; overflow-wrap: anywhere; font-weight: 400; }
.customer-density-dialog.admin-density-dialog .review-dialog-grid { gap: var(--admin-space-5, 12px); }
.customer-density-dialog.admin-density-dialog .review-dialog-section,.customer-density-dialog.admin-density-dialog .review-decision-card { padding: var(--admin-space-4, 10px); border-radius: 0; box-shadow: none; }
.customer-density-dialog.admin-density-dialog .review-section-heading { margin-bottom: var(--admin-space-3, 8px); }
.customer-density-dialog.admin-density-dialog .review-section-heading span { font-size: var(--admin-font-caption, 12px); }
.customer-density-dialog.admin-density-dialog .review-info-grid { grid-template-columns: 1fr; }
.customer-density-dialog.admin-density-dialog .review-document-table { border-radius: 0; }
.customer-density-dialog.admin-density-dialog .review-document-header { font-size: 12px; }
.customer-density-dialog.admin-density-dialog .review-document-header { font-size: var(--admin-font-caption, 12px); }
.customer-density-dialog.admin-density-dialog .review-document-name { white-space: normal; }
.customer-density-dialog.admin-density-dialog .decision-summary { margin-bottom: 12px; padding: 8px 10px; border-radius: 0; }
.customer-density-dialog.admin-density-dialog .approval-credentials { margin-top: 12px; border-radius: 0; }
.customer-density-dialog.admin-density-dialog .approval-credentials > div { padding: 8px 10px; }
.customer-density-dialog.admin-density-dialog .decision-summary { margin-bottom: var(--admin-space-5, 12px); padding: var(--admin-space-3, 8px) var(--admin-space-4, 10px); border-radius: 0; }
.customer-density-dialog.admin-density-dialog .approval-credentials { margin-top: var(--admin-space-5, 12px); border-radius: 0; }
.customer-density-dialog.admin-density-dialog .approval-credentials > div { padding: var(--admin-space-3, 8px) var(--admin-space-4, 10px); }
.customer-density.admin-density .rule-heading,.customer-density.admin-density .rule-card-heading,.customer-density.admin-density .panel-heading { min-height: 0; padding: 8px 0; gap: 12px; }
.customer-density.admin-density .rule-heading h2,.customer-density.admin-density .rule-card-heading h2,.customer-density.admin-density .panel-heading h2 { font-size: 14px; }
.customer-density.admin-density .rule-heading p,.customer-density.admin-density .panel-heading p { margin-top: 3px; font-size: 12px; }
.customer-density.admin-density .formula-band { min-height: 36px; padding: 6px 10px; gap: 6px 24px; }
.customer-density.admin-density .rule-state,.customer-density.admin-density .rule-error,.customer-density.admin-density .loading-state,.customer-density.admin-density .fee-loading,.customer-density.admin-density .fee-empty { min-height: 64px; padding: 12px; }
.customer-density.admin-density .rule-heading,.customer-density.admin-density .rule-card-heading,.customer-density.admin-density .panel-heading { min-height: 0; padding: var(--admin-space-3, 8px) 0; gap: var(--admin-space-5, 12px); }
.customer-density.admin-density .rule-heading h2,.customer-density.admin-density .rule-card-heading h2,.customer-density.admin-density .panel-heading h2 { font-size: var(--admin-font-section, 14px); }
.customer-density.admin-density .rule-heading p,.customer-density.admin-density .panel-heading p { margin-top: 3px; font-size: var(--admin-font-caption, 12px); }
.customer-density.admin-density .formula-band { min-height: var(--admin-row-height, 36px); padding: var(--admin-space-2, 6px) var(--admin-space-4, 10px); gap: var(--admin-space-2, 6px) var(--admin-space-7, 24px); }
.customer-density.admin-density .rule-state,.customer-density.admin-density .rule-error,.customer-density.admin-density .loading-state,.customer-density.admin-density .fee-loading,.customer-density.admin-density .fee-empty { min-height: var(--admin-empty-height, 64px); padding: var(--admin-space-5, 12px); }
.customer-density.admin-density .rule-state > svg { display: none; }
.customer-density.admin-density .charge-rule-card { margin-top: 12px; }
.customer-density.admin-density .overseas-fee-panel { margin-top: 12px; }
.customer-density.admin-density .fee-groups { gap: 12px; padding: 8px 0; }
.customer-density.admin-density .fee-groups h3,.customer-density-dialog.admin-density-dialog .edit-groups h3,.customer-density-dialog.admin-density-dialog .groups h3 { font-size: 14px; }
.customer-density.admin-density .fee-groups th,.customer-density.admin-density .fee-groups td { padding: 6px 10px; }
.customer-density-dialog.admin-density-dialog .editor-content { gap: 12px; }
.customer-density-dialog.admin-density-dialog .editor-formula { padding: 6px 0; }
.customer-density.admin-density .charge-rule-card { margin-top: var(--admin-space-5, 12px); }
.customer-density.admin-density .overseas-fee-panel { margin-top: var(--admin-space-5, 12px); }
.customer-density.admin-density .fee-groups { gap: var(--admin-space-5, 12px); padding: var(--admin-space-3, 8px) 0; }
.customer-density.admin-density .fee-groups h3,.customer-density-dialog.admin-density-dialog .edit-groups h3,.customer-density-dialog.admin-density-dialog .groups h3 { font-size: var(--admin-font-section, 14px); }
.customer-density.admin-density .fee-groups th,.customer-density.admin-density .fee-groups td { padding: var(--admin-space-2, 6px) var(--admin-space-4, 10px); }
.customer-density-dialog.admin-density-dialog .editor-content { gap: var(--admin-space-5, 12px); }
.customer-density-dialog.admin-density-dialog .editor-formula { padding: var(--admin-space-2, 6px) 0; }
.customer-density-dialog.admin-density-dialog .editor-table th,.customer-density-dialog.admin-density-dialog .editor-table td,
.customer-density-dialog.admin-density-dialog .charge-rule-editor th,.customer-density-dialog.admin-density-dialog .charge-rule-editor td,
.customer-density-dialog.admin-density-dialog .edit-groups th,.customer-density-dialog.admin-density-dialog .edit-groups td { padding: 4px 8px; }
.customer-density-dialog.admin-density-dialog .edit-groups th,.customer-density-dialog.admin-density-dialog .edit-groups td { padding: var(--admin-space-1, 4px) var(--admin-space-3, 8px); }
.customer-density-dialog.admin-density-dialog .dialog-actions { justify-content: flex-end; }
.customer-density-dialog.admin-density-dialog .dialog-actions .el-button { min-height: 32px; min-width: 64px; font-size: 13px; }
.customer-density-dialog.admin-density-dialog .edit-groups,.customer-density-dialog.admin-density-dialog .groups { gap: 12px; padding: 0; }
.customer-density-dialog.admin-density-dialog .dialog-actions .el-button { min-height: var(--admin-control-height, 32px); min-width: 64px; font-size: var(--admin-font-body, 13px); }
.customer-density-dialog.admin-density-dialog .edit-groups,.customer-density-dialog.admin-density-dialog .groups { gap: var(--admin-space-5, 12px); padding: 0; }
@media (max-width: 960px) { .customer-density.admin-density .info-grid { grid-template-columns: repeat(2,minmax(0,1fr)); } }
@media (max-width: 720px) { .customer-density.admin-density .info-grid { grid-template-columns: 1fr; }.customer-density.admin-density .qualification-list button { grid-template-columns: minmax(0,1fr) auto; }.customer-density.admin-density .qualification-list .file-date { white-space: normal; } }
.customer-density-dialog .review-info-grid { --admin-field-label-width: 96px; }
@@ -26,7 +26,7 @@ describe('customer overseas storage fee api', () => {
it('keeps overseas fee business actions as semantic buttons at narrow widths', () => {
expect(overseasListSource).toContain('border stripe')
expect(overseasListSource).toContain('class="customer-action-button" size="small" plain')
expect(overseasListSource).toContain('class="density-action" size="small" plain')
expect(overseasListSource).toContain('size="small" plain type="danger"')
expect(overseasListSource).toContain('<el-table :data="group" size="small" border stripe>')
expect(overseasListSource).not.toContain('<Pencil')
@@ -244,8 +244,8 @@ onBeforeUnmount(() => {
</section>
</template> -->
<div ref="tabList" class="detail-tabs" role="tablist" aria-label="客户详情模块">
<button v-for="tab in tabs" :id="`customer-tab-${tab.key}`" :key="tab.key" type="button" role="tab" :class="{ active: activeTab === tab.key }" :aria-label="tab.label" :aria-selected="activeTab === tab.key" :aria-controls="`customer-panel-${tab.key}`" :tabindex="activeTab === tab.key ? 0 : -1" @click="selectTab(tab.key, $event.currentTarget as HTMLElement)" @keydown="handleTabKeydown($event, tab.key)"><span class="detail-tab-icon" aria-hidden="true">{{ tab.icon }}</span><span>{{ tab.label }}</span></button>
<div ref="tabList" class="detail-tabs density-tabs" role="tablist" aria-label="客户详情模块">
<button v-for="tab in tabs" :id="`customer-tab-${tab.key}`" :key="tab.key" type="button" role="tab" :class="{ active: activeTab === tab.key }" :aria-label="tab.label" :aria-selected="activeTab === tab.key" :aria-controls="`customer-panel-${tab.key}`" :tabindex="activeTab === tab.key ? 0 : -1" @click="selectTab(tab.key, $event.currentTarget as HTMLElement)" @keydown="handleTabKeydown($event, tab.key)"><span class="density-tab-icon" aria-hidden="true">{{ tab.icon }}</span><span>{{ tab.label }}</span></button>
</div>
<div v-if="customer" :id="`customer-panel-${activeTab}`" class="tab-panel" role="tabpanel" :aria-labelledby="`customer-tab-${activeTab}`" tabindex="0">
@@ -253,7 +253,7 @@ onBeforeUnmount(() => {
<div class="basic-layout">
<section class="detail-panel company-panel">
<div class="panel-title"><div><h2>企业基本信息</h2><p>工商档案与合作配置</p></div><Building2 :size="19" /></div>
<dl class="info-grid">
<dl class="info-grid density-fields">
<div><dt>公司名称</dt><dd>{{ displayText(customer.legalName || customer.tenantName) }}</dd></div>
<div><dt>客户昵称</dt><dd>{{ displayText(customer.customerNickname) }}</dd></div>
<div><dt>统一社会信用代码</dt><dd>{{ displayText(customer.unifiedSocialCreditCode) }}</dd></div>
@@ -275,7 +275,7 @@ onBeforeUnmount(() => {
</section>
<section class="detail-panel contact-panel">
<div class="panel-title"><div><h2>联系与合作信息</h2><p>客户联络及内部跟进信息</p></div><UserRound :size="19" /></div>
<dl class="info-grid info-grid--three">
<dl class="info-grid info-grid--three density-fields">
<div><dt>联系人</dt><dd>{{ displayText(customer.contactName) }}</dd></div><div><dt>公司联系电话</dt><dd>{{ displayText(customer.contactPhone) }}</dd></div>
<div><dt>联系邮箱</dt><dd>{{ displayText(customer.contactEmail) }}</dd></div><div><dt>联系微信</dt><dd>{{ displayText(customer.contactWechat) }}</dd></div>
<div><dt>客户套餐</dt><dd>{{ displayText(customer.servicePackage) }}</dd></div><div><dt>账期</dt><dd>{{ customer.paymentTermDays ?? 0 }} 天</dd></div>
@@ -314,41 +314,38 @@ onBeforeUnmount(() => {
</template>
<style scoped>
.detail-tabs { border-bottom: 1px solid var(--cc-border); }
.customer-detail-page { min-width: 0; }
.detail-header { display:flex; min-width:0; align-items:center; gap:12px; }
.detail-header { display:flex; min-width:0; align-items:center; gap:var(--admin-space-5, 12px); }
.back-button { flex:0 0 auto; }
.detail-page-toolbar { min-width:0; flex:1; }
.header-actions { display:flex; flex:0 0 auto; gap:8px; }
.header-actions { display:flex; flex:0 0 auto; gap:var(--admin-space-3, 8px); }
.header-actions :deep(.el-button + .el-button) { margin-left:0; }
.detail-tabs { display:flex; gap:0; overflow-x:auto; overflow-y:hidden; border-bottom:1px solid rgba(228,228,231,.5); border-radius:20px 20px 0 0; background:rgba(255,255,255,.95); box-shadow:0 -4px 16px rgba(0,0,0,.04); scrollbar-width:thin; }
.detail-tabs button { display:inline-flex; min-width:max-content; min-height:0; flex:0 0 auto; align-items:center; justify-content:center; gap:8px; padding:16px 28px; border:0; border-bottom:3px solid transparent; background:transparent; color:#71717a; font-size:15px; font-weight:600; white-space:nowrap; cursor:pointer; transition:all .3s cubic-bezier(.4,0,.2,1); }
.detail-tab-icon { font-size:18px; line-height:1; }
.detail-tabs button:hover { background:rgba(102,126,234,.05); color:#667eea; }
.detail-tabs button.active { border-bottom-color:#667eea; background:rgba(102,126,234,.08); color:#667eea; font-weight:800; }
.tab-panel { min-height:400px; padding-top:18px; }
.basic-layout { display:grid; grid-template-columns:minmax(0,1.1fr) minmax(0,.9fr); gap:18px; align-items:start; }
.detail-panel { min-width:0; padding:22px; border:1px solid rgba(255,255,255,.5); border-radius:12px; background:rgba(255,255,255,.95); box-shadow:0 3px 12px rgba(15,23,42,.05); }
.contact-panel,.user-panel { grid-column:1 / -1; }
.panel-title { display:flex; align-items:flex-start; justify-content:space-between; gap:16px; margin-bottom:18px; padding-bottom:14px; border-bottom:1px solid var(--cc-border); }
.panel-title h2 { margin:0; color:#344054; font-size:16px; }.panel-title p { margin:5px 0 0; color:var(--cc-muted); font-size:11px; }.panel-title > svg { color:var(--cc-primary); }
.info-grid { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:10px; margin:0; }
.info-grid--three { grid-template-columns:repeat(3,minmax(0,1fr)); }
.info-grid > div { min-width:0; padding:12px 14px; border-radius:10px; background:#f8f9fc; }.info-grid > div.wide { grid-column:1 / -1; }
.info-grid dt { margin-bottom:5px; color:var(--cc-muted); font-size:11px; }.info-grid dd { margin:0; color:#25324a; font-size:13px; font-weight:650; line-height:1.55; overflow-wrap:anywhere; }
.qualification-list { display:flex; flex-direction:column; gap:8px; }
.qualification-list button { display:grid; width:100%; grid-template-columns:38px minmax(0,1fr) auto auto 20px; align-items:center; gap:10px; padding:10px; border:1px solid var(--cc-border); border-radius:10px; background:#fff; color:inherit; text-align:left; cursor:pointer; }
.basic-layout { display:grid; align-items:start; }
.detail-panel { min-width:0; }
.contact-panel { grid-column:1 / -1; }
.user-panel { grid-column:1 / -1; }
.panel-title { display:flex; justify-content:space-between; border-bottom:1px solid var(--cc-border); }
.panel-title h2 { margin:0; color:#344054; }.panel-title p { margin:5px 0 0; color:var(--cc-muted); }.panel-title > svg { color:var(--cc-primary); }
.info-grid { margin:0; }.info-grid > div.wide { grid-column:1 / -1; }
.qualification-list { display:flex; flex-direction:column; }
.qualification-list button { display:grid; width:100%; align-items:center; background:var(--admin-surface, #fff); color:inherit; text-align:left; cursor:pointer; }
.qualification-list button:hover { border-color:#b9c3f5; background:#fafbff; }.qualification-list button:disabled { cursor:wait; opacity:.65; }
.qualification-list strong,.qualification-list small { display:block; }.qualification-list strong { font-size:12px; }.qualification-list small,.file-date { margin-top:3px; color:var(--cc-muted); font-size:10px; }.file-date { margin:0; white-space:nowrap; }
.section-empty,.module-empty { display:flex; min-height:150px; align-items:center; justify-content:center; flex-direction:column; gap:7px; color:#98a2b3; text-align:center; }
.section-empty strong,.module-empty strong { color:#475467; font-size:13px; }.section-empty span,.module-empty p { max-width:600px; margin:0; color:var(--cc-muted); font-size:11px; line-height:1.7; }
.qualification-list strong { display:block; }
.qualification-list small { display:block; }.qualification-list small { margin-top:3px; color:var(--cc-muted); }.file-date { margin-top:3px; color:var(--cc-muted); }.file-date { margin:0; white-space:nowrap; }
.section-empty { display:flex; align-items:center; justify-content:center; flex-direction:column; color:#98a2b3; text-align:center; }
.module-empty { display:flex; align-items:center; justify-content:center; flex-direction:column; color:#98a2b3; text-align:center; }
.section-empty strong { color:#475467; font-size:var(--admin-font-body, 13px); }
.module-empty strong { color:#475467; font-size:var(--admin-font-body, 13px); }.section-empty span { max-width:600px; margin:0; color:var(--cc-muted); line-height:1.7; }.module-empty p { max-width:600px; margin:0; color:var(--cc-muted); font-size:var(--admin-font-caption, 11px); line-height:1.7; }
.section-empty button { border:0; background:transparent; color:var(--cc-primary); cursor:pointer; }.section-empty.is-error svg { color:#d92d20; }
.user-table-wrap { overflow-x:auto; }
table { width:100%; min-width:720px; border-collapse:collapse; font-size:12px; }th { padding:10px 12px; background:#f8fafc; color:#667085; text-align:left; font-weight:650; }td { padding:12px; border-top:1px solid #edf0f4; color:#344054; }
.user-identity { display:flex; align-items:center; gap:9px; }.user-identity > span { display:grid; width:34px; height:34px; flex:0 0 auto; place-items:center; border-radius:50%; background:#eef1ff; color:var(--cc-primary); font-weight:700; }.user-identity strong,.user-identity small { display:block; }.user-identity small { margin-top:3px; color:var(--cc-muted); font-size:10px; }.role-list { display:flex; flex-wrap:wrap; gap:5px; }
.member-contact span,.member-contact small { display:block; }.member-contact small { margin-top:3px; color:var(--cc-muted); font-size:10px; }
.module-panel { min-height:360px; }.module-empty { min-height:260px; }.module-empty > svg { color:#8797b2; }.module-empty p { max-width:520px; font-size:12px; }
.contract-note { padding:16px 18px; border-radius:10px; background:#f8fafc; }.contract-note strong { color:#344054; font-size:12px; }.contract-note p { margin:5px 0 0; color:var(--cc-muted); font-size:11px; line-height:1.7; }
.qualification-preview { display:grid; min-height:320px; max-height:72vh; overflow:auto; place-items:center; border-radius:10px; background:#f8fafc; }.qualification-preview img { display:block; max-width:100%; height:auto; }.qualification-preview iframe { width:100%; min-height:68vh; border:0; background:#fff; }
table { width:100%; min-width:720px; border-collapse:collapse; font-size:var(--admin-font-caption, 12px); }th { padding:var(--admin-space-4, 10px) var(--admin-space-5, 12px); background:var(--admin-label-bg, #f8fafc); color:#667085; text-align:left; font-weight:650; }td { padding:var(--admin-space-5, 12px); border-top:1px solid #edf0f4; color:#344054; }
.user-identity { display:flex; align-items:center; gap:9px; }.user-identity > span { width:34px; height:34px; flex:0 0 auto; place-items:center; border-radius:50%; background:#eef1ff; color:var(--cc-primary); font-weight:700; }.user-identity strong { display:block; }.user-identity small { display:block; }.user-identity small { margin-top:3px; color:var(--cc-muted); }.role-list { display:flex; flex-wrap:wrap; gap:5px; }
.member-contact span { display:block; }
.member-contact small { display:block; }.member-contact small { margin-top:3px; color:var(--cc-muted); }.module-empty > svg { color:#8797b2; }.module-empty p { max-width:520px; font-size:var(--admin-font-caption, 12px); }
.contract-note { padding:var(--admin-space-6, 16px) 18px; border-radius:10px; background:var(--admin-label-bg, #f8fafc); }.contract-note strong { color:#344054; font-size:var(--admin-font-caption, 12px); }.contract-note p { margin:5px 0 0; color:var(--cc-muted); font-size:var(--admin-font-caption, 11px); line-height:1.7; }
.qualification-preview { display:grid; min-height:320px; max-height:72vh; overflow:auto; place-items:center; border-radius:10px; background:var(--admin-label-bg, #f8fafc); }.qualification-preview img { display:block; max-width:100%; height:auto; }.qualification-preview iframe { width:100%; min-height:68vh; border:0; background:var(--admin-surface, #fff); }
@media (max-width:960px) { .basic-layout { grid-template-columns:1fr; }.contact-panel,.user-panel { grid-column:auto; }.qualification-panel { order:3; }.user-panel { order:4; }.info-grid--three { grid-template-columns:repeat(2,minmax(0,1fr)); } }
@media (max-width:720px) { .detail-header { align-items:flex-start; flex-wrap:wrap; }.detail-page-toolbar { width:100%; flex-basis:100%; }.company-mark { width:40px; height:40px; }.header-actions { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); width:100%; }.header-actions :deep(.el-tag) { grid-column:1 / -1; justify-self:start; }.header-actions :deep(.el-button) { min-height:44px; }.detail-panel { padding:16px; }.info-grid { grid-template-columns:1fr; }.info-grid > div.wide { grid-column:auto; } }
@media (max-width:420px) { .company-mark { display:none; }.header-actions { grid-template-columns:1fr; }.header-actions :deep(.el-tag) { grid-column:auto; }.qualification-list button { grid-template-columns:36px minmax(0,1fr) auto; }.qualification-list .file-date,.qualification-list button > svg { display:none; } }
@@ -198,7 +198,8 @@ describe('CustomersView', () => {
expect(customersSource).toContain('class="qualification-validity" role="cell"')
expect(customersSource).toContain('grid-template-columns:170px minmax(0,1fr) 220px')
expect(customersSource).toContain('.qualification-validity :deep(.el-date-editor) { width:100%; min-width:0; }')
expect(customersSource).toContain('.qualification-file-name { min-width:0; max-width:140px;')
expect(customersSource).toContain('.qualification-file-name { min-width:0;')
expect(readFileSync(resolve(process.cwd(), 'src/modules/customers/customerDensity.css'), 'utf8')).toContain('max-width: none; overflow-wrap: anywhere; white-space: normal;')
})
it('loads the complete customer detail before editing masked list fields', async () => {
+56 -72
View File
@@ -1056,13 +1056,13 @@ onMounted(() => { void loadCustomers() })
<DataTable v-model:page="page" v-model:page-size="pageSize" :data="result.records" :columns="customerColumns" preference-key="customers-table" :loading="loading" :total="result.total" aria-label="客户列表" row-key="id" empty-text="暂无符合筛选条件的客户" class="customer-list-table" :stripe="true" border>
<template #toolbar-actions><div class="customers-table-actions"><el-button v-if="access.can('platform:customer:export') && access.can('platform:download:list')" :loading="exportSubmitting" :disabled="loading || exportSubmitting" @click="exportCustomers">导出Excel</el-button><el-button v-if="access.can('platform:customer:create')" type="primary" :disabled="loading" @click="openCreate">新增客户</el-button></div></template>
<template #actions="{ row }">
<div class="table-actions">
<el-button v-if="access.can('platform:customer:list')" class="customer-action-button customer-action-detail" size="small" @click="showDetail(row as PlatformTenantResource)">详情</el-button>
<el-button v-if="access.can('platform:customer:edit') && (row as PlatformTenantResource).status !== 'ARCHIVED'" class="customer-action-button customer-action-edit" size="small" @click="openEdit(row as PlatformTenantResource)">编辑</el-button>
<el-button v-if="hasReviewAction(row as PlatformTenantResource)" class="customer-action-button customer-action-review" size="small" :disabled="reviewSubmitting" @click="openDecision(row as PlatformTenantResource, access.can('platform:customer:approve') ? 'approve' : 'reject')">审核</el-button>
<el-button v-for="action in customerDirectSecondaryActions(row as PlatformTenantResource)" :key="action" :class="[`customer-action-button`, `customer-action-${action}`, { 'is-enable': action === 'status' && (row as PlatformTenantResource).status !== 'ACTIVE', 'is-disable': action === 'status' && (row as PlatformTenantResource).status === 'ACTIVE' }]" size="small" :disabled="customerActionDisabled(action)" @click="handleMoreAction(row as PlatformTenantResource, action)">{{ customerActionLabel(row as PlatformTenantResource, action) }}</el-button>
<div class="table-actions density-actions">
<el-button v-if="access.can('platform:customer:list')" class="density-action customer-action-detail" size="small" @click="showDetail(row as PlatformTenantResource)">详情</el-button>
<el-button v-if="access.can('platform:customer:edit') && (row as PlatformTenantResource).status !== 'ARCHIVED'" class="density-action customer-action-edit" size="small" @click="openEdit(row as PlatformTenantResource)">编辑</el-button>
<el-button v-if="hasReviewAction(row as PlatformTenantResource)" class="density-action" data-tone="warning" size="small" :disabled="reviewSubmitting" @click="openDecision(row as PlatformTenantResource, access.can('platform:customer:approve') ? 'approve' : 'reject')">审核</el-button>
<el-button v-for="action in customerDirectSecondaryActions(row as PlatformTenantResource)" :key="action" class="density-action" :data-tone="action === 'reset-password' ? 'purple' : action === 'impersonate' ? 'primary' : action === 'status' && (row as PlatformTenantResource).status !== 'ACTIVE' ? 'success' : 'danger'" size="small" :disabled="customerActionDisabled(action)" @click="handleMoreAction(row as PlatformTenantResource, action)">{{ customerActionLabel(row as PlatformTenantResource, action) }}</el-button>
<el-dropdown v-if="customerOverflowActions(row as PlatformTenantResource).length" trigger="click" popper-class="customer-action-menu" @command="handleMoreAction(row as PlatformTenantResource, $event as string)">
<el-button class="customer-action-button customer-action-more" size="small" plain :aria-label="customerMoreActionsLabel(row as PlatformTenantResource)" :title="customerMoreActionsLabel(row as PlatformTenantResource)"><MoreHorizontal :size="17" aria-hidden="true" /></el-button>
<el-button class="density-action customer-action-more" size="small" plain :aria-label="customerMoreActionsLabel(row as PlatformTenantResource)" :title="customerMoreActionsLabel(row as PlatformTenantResource)"><MoreHorizontal :size="17" aria-hidden="true" /></el-button>
<template #dropdown><el-dropdown-menu><el-dropdown-item v-for="action in customerOverflowActions(row as PlatformTenantResource)" :key="action" :command="action" :disabled="customerActionDisabled(action)" :class="{ 'customer-action-menu__danger': customerActionType(row as PlatformTenantResource, action) === 'danger' }">{{ customerActionLabel(row as PlatformTenantResource, action) }}</el-dropdown-item></el-dropdown-menu></template>
</el-dropdown>
</div>
@@ -1148,7 +1148,7 @@ onMounted(() => { void loadCustomers() })
<div class="review-dialog-grid">
<section class="review-dialog-section review-dialog-card">
<div class="review-section-heading"><div><h3>企业基本信息</h3><span>核对客户提交的主体资料</span></div><CircleCheck :size="18" /></div>
<dl class="review-info-grid">
<dl class="review-info-grid density-fields">
<div><dt>客户名称</dt><dd>{{ selectedReview.tenantName || '未填写' }}</dd></div>
<div><dt>客户编号</dt><dd>{{ selectedReview.tenantCode || '未生成' }}</dd></div>
<div><dt>联系人</dt><dd>{{ selectedReview.contactName || '未填写' }}</dd></div>
@@ -1210,90 +1210,74 @@ onMounted(() => { void loadCustomers() })
</template>
<style scoped>
.customers-table-heading { display:flex; min-width:0; align-items:baseline; gap:10px; } .customers-table-heading h2 { margin:0; color:var(--cc-text); font-size:16px; } .customers-table-heading span { color:var(--cc-muted); font-size:12px; white-space:nowrap; }
.customers-table-actions { display:flex; flex-wrap:wrap; gap:8px; } .customers-table-actions :deep(.el-button + .el-button) { margin-left:0; }
.table-actions { display:flex; flex-wrap:wrap; align-items:center; gap:2px 8px; } .table-actions :deep(.el-button) { margin-left:0; }
:global(.customer-action-menu .el-dropdown-menu__item) { min-height:40px; gap:8px; }
.customers-table-heading { display:flex; min-width:0; align-items:baseline; gap:var(--admin-space-4, 10px); } .customers-table-heading h2 { margin:0; color:var(--cc-text); font-size:var(--admin-font-title, 16px); } .customers-table-heading span { color:var(--cc-muted); font-size:var(--admin-font-caption, 12px); white-space:nowrap; }
.customers-table-actions { display:flex; flex-wrap:wrap; gap:var(--admin-space-3, 8px); } .customers-table-actions :deep(.el-button + .el-button) { margin-left:0; }
.table-actions { display:flex; flex-wrap:wrap; align-items:center; } .table-actions :deep(.el-button) { margin-left:0; }
:global(.customer-action-menu .el-dropdown-menu__item) { min-height:40px; gap:var(--admin-space-3, 8px); }
:global(.customer-action-menu .customer-action-menu__danger) { color:var(--el-color-danger); }
.review-sync-error { display:flex; min-width:0; align-items:center; justify-content:space-between; gap:12px; padding:10px 18px; border-bottom:1px solid #fecdca; background:#fffafa; color:#b42318; font-size:12px; } .review-sync-error > div { display:flex; min-width:0; align-items:center; gap:8px; } .review-sync-error span { overflow-wrap:anywhere; }
.review-sync-error { display:flex; min-width:0; align-items:center; justify-content:space-between; gap:var(--admin-space-5, 12px); padding:var(--admin-space-4, 10px) 18px; border-bottom:1px solid #fecdca; background:#fffafa; color:#b42318; font-size:var(--admin-font-caption, 12px); } .review-sync-error > div { display:flex; min-width:0; align-items:center; gap:var(--admin-space-3, 8px); } .review-sync-error span { overflow-wrap:anywhere; }
.customer-company-name { display:block; overflow:hidden; color:#334155; font-weight:500; text-overflow:ellipsis; white-space:nowrap; }
.customers-table-actions :deep(.el-button--default) { border-color:rgba(102,126,234,.22); }
:deep(.customer-list-table .el-table__header-wrapper th) { background:#f7f9fc; }
:deep(.customer-list-table .el-table) { --el-table-border-color:#e5eaf1; --el-table-row-hover-bg-color:rgba(102,126,234,.06); border:1px solid #e5eaf1; border-radius:0; overflow:hidden; }
:deep(.customer-list-table .el-table__inner-wrapper::before) { background:#e5eaf1; }
:deep(.customer-list-table .el-table__body tr.el-table__row--striped > td) { background:#fbfcff; }
:deep(.customer-list-table .el-table__body tr:hover > td) { background:rgba(102,126,234,.06); }
:deep(.customer-list-table .el-table__body td), :deep(.customer-list-table .el-table__header th) { border-right:1px solid #edf1f5; }
:deep(.customer-list-table .el-table__body td:last-child), :deep(.customer-list-table .el-table__header th:last-child) { border-right:0; }
:deep(.customer-list-table .customer-code-column .cell) { white-space:nowrap; }
:deep(.customer-list-table .el-tag) { border:0; border-radius:12px; font-weight:650; }
:deep(.customer-list-table .el-tag--success) { background:linear-gradient(135deg,rgba(34,197,94,.15),rgba(22,163,74,.1)); color:#16a34a; }
:deep(.customer-list-table .el-tag--warning) { background:linear-gradient(135deg,rgba(245,158,11,.15),rgba(234,88,12,.1)); color:#ea580c; }
:deep(.customer-list-table .el-tag--danger) { background:linear-gradient(135deg,rgba(239,68,68,.15),rgba(220,38,38,.1)); color:#dc2626; }
:deep(.customer-list-table .el-tag--info) { background:linear-gradient(135deg,rgba(102,126,234,.15),rgba(118,75,162,.1)); color:#667eea; }
.capital-input { display:grid; grid-template-columns:minmax(0,1fr) 130px; width:100%; gap:8px; } .capital-input :deep(.el-input-number),.capital-input :deep(.el-select) { width:100%; }
.qualification-heading { display:flex; justify-content:flex-end; margin-bottom:12px; } .qualification-hint { margin:12px 0 0; color:var(--cc-muted); font-size:12px; line-height:1.6; } .qualification-hint span,.qualification-label > span { color:var(--el-color-danger); }
.qualification-credit-code { max-width:420px; margin-top:16px; } .qualification-credit-code small,.field-hint { display:block; margin-top:6px; color:var(--cc-muted); font-size:11px; line-height:1.5; }
.qualification-table { overflow:hidden; margin-top:14px; border:1px solid var(--cc-border); border-radius:8px; } .qualification-row { display:grid; grid-template-columns:170px minmax(0,1fr) 220px; min-width:0; border-top:1px solid var(--cc-border); } .qualification-row:first-child { border-top:0; } .qualification-row > * { display:flex; min-width:0; min-height:68px; align-items:center; padding:12px 14px; } .qualification-header { min-height:44px; background:#f7f8fc; color:var(--cc-muted); font-size:12px; font-weight:600; } .qualification-header > * { min-height:44px; }
.capital-input { display:grid; grid-template-columns:minmax(0,1fr) 130px; width:100%; gap:var(--admin-space-3, 8px); } .capital-input :deep(.el-input-number) { width:100%; } .capital-input :deep(.el-select) { width:100%; }
.qualification-heading { display:flex; justify-content:flex-end; margin-bottom:var(--admin-space-5, 12px); } .qualification-hint { margin:var(--admin-space-5, 12px) 0 0; color:var(--cc-muted); font-size:var(--admin-font-caption, 12px); line-height:1.6; } .qualification-hint span { color:var(--el-color-danger); } .qualification-label > span { color:var(--el-color-danger); }
.qualification-credit-code { max-width:420px; margin-top:var(--admin-space-6, 16px); } .qualification-credit-code small { display:block; margin-top:var(--admin-space-2, 6px); color:var(--cc-muted); font-size:var(--admin-font-caption, 11px); line-height:1.5; } .field-hint { display:block; margin-top:var(--admin-space-2, 6px); color:var(--cc-muted); line-height:1.5; }
.qualification-table { overflow:hidden; margin-top:14px; border:1px solid var(--cc-border); border-radius:8px; } .qualification-row { display:grid; grid-template-columns:170px minmax(0,1fr) 220px; min-width:0; border-top:1px solid var(--cc-border); } .qualification-row:first-child { border-top:0; } .qualification-row > * { display:flex; min-width:0; align-items:center; } .qualification-header { min-height:44px; background:#f7f8fc; color:var(--cc-muted); font-size:var(--admin-font-caption, 12px); font-weight:600; }
.qualification-label { gap:5px; font-weight:600; } .qualification-label svg { margin-left:auto; color:var(--el-color-success); }
.qualification-optional { padding:2px 5px; border-radius:4px; background:var(--el-fill-color-light); color:var(--cc-muted); font-size:10px; font-weight:500; line-height:1.4; }
.qualification-file { gap:12px; } .qualification-file-control { display:flex; min-width:0; flex:1; flex-wrap:wrap; align-items:center; gap:8px; } .file-picker { position:relative; display:inline-flex; min-height:36px; flex:0 0 auto; align-items:center; gap:7px; overflow:hidden; padding:0 12px; border:1px solid var(--cc-border); border-radius:6px; background:#fff; color:var(--cc-text); cursor:pointer; } .file-picker:focus-within { outline:2px solid color-mix(in srgb,var(--cc-primary) 35%,transparent); outline-offset:2px; } .file-picker.is-disabled { cursor:not-allowed; opacity:.6; } .file-picker input { position:absolute; inset:0; width:100%; height:100%; opacity:0; cursor:inherit; } .qualification-file-name { min-width:0; max-width:140px; overflow:hidden; color:var(--cc-muted); font-size:12px; text-overflow:ellipsis; white-space:nowrap; } .qualification-validity :deep(.el-date-editor) { width:100%; min-width:0; } .qualification-error { width:100%; color:var(--el-color-danger); line-height:1.4; }
.qualification-preview-trigger { position:relative; display:grid; width:68px; height:68px; flex:0 0 68px; overflow:hidden; place-items:center; padding:0; border:1px solid var(--cc-border); border-radius:6px; background:#f8fafc; color:var(--cc-muted); cursor:pointer; }
.qualification-optional { padding:2px 5px; border-radius:var(--admin-radius, 4px); background:var(--el-fill-color-light); color:var(--cc-muted); font-size:var(--admin-font-caption, 10px); font-weight:500; line-height:1.4; }
.qualification-file { gap:var(--admin-space-5, 12px); } .qualification-file-control { display:flex; min-width:0; flex:1; flex-wrap:wrap; align-items:center; gap:var(--admin-space-3, 8px); } .file-picker { position:relative; display:inline-flex; flex:0 0 auto; align-items:center; gap:7px; overflow:hidden; padding:0 var(--admin-space-5, 12px); border:1px solid var(--cc-border); border-radius:6px; background:var(--admin-surface, #fff); color:var(--cc-text); cursor:pointer; } .file-picker:focus-within { outline:2px solid color-mix(in srgb,var(--cc-primary) 35%,transparent); outline-offset:2px; } .file-picker.is-disabled { cursor:not-allowed; opacity:.6; } .file-picker input { position:absolute; inset:0; width:100%; height:100%; opacity:0; cursor:inherit; } .qualification-file-name { min-width:0; overflow:hidden; color:var(--cc-muted); font-size:var(--admin-font-caption, 12px); text-overflow:ellipsis; } .qualification-validity :deep(.el-date-editor) { width:100%; min-width:0; } .qualification-error { width:100%; color:var(--el-color-danger); line-height:1.4; }
.qualification-preview-trigger { position:relative; display:grid; width:68px; height:68px; flex:0 0 68px; overflow:hidden; place-items:center; padding:0; border:1px solid var(--cc-border); border-radius:6px; background:var(--admin-label-bg, #f8fafc); color:var(--cc-muted); cursor:pointer; }
.qualification-preview-trigger:hover { border-color:var(--cc-primary); color:var(--cc-primary); }
.qualification-preview-trigger:focus-visible { outline:2px solid color-mix(in srgb,var(--cc-primary) 42%,transparent); outline-offset:2px; }
.qualification-preview-trigger:disabled { cursor:wait; opacity:.72; }
.qualification-preview-trigger img { width:100%; height:100%; object-fit:contain; background:#fff; }
.qualification-preview-trigger img { width:100%; height:100%; object-fit:contain; background:var(--admin-surface, #fff); }
.qualification-preview-placeholder { display:grid; place-items:center; gap:2px; }
.qualification-preview-placeholder small { font-size:10px; font-weight:700; }
.qualification-preview-action { position:absolute; inset:auto 0 0; display:flex; min-height:22px; align-items:center; justify-content:center; gap:3px; background:rgba(23,32,51,.78); color:#fff; font-size:10px; }
.qualification-preview { display:grid; max-height:min(70vh,760px); min-height:240px; place-items:center; overflow:auto; border:1px solid var(--cc-border); border-radius:6px; background:#f8fafc; }
.qualification-preview-placeholder small { font-size:var(--admin-font-caption, 10px); font-weight:700; }
.qualification-preview-action { position:absolute; inset:auto 0 0; display:flex; min-height:22px; align-items:center; justify-content:center; gap:3px; background:rgba(23,32,51,.78); color:var(--admin-surface, #fff); }
.qualification-preview { display:grid; max-height:min(70vh,760px); min-height:240px; place-items:center; overflow:auto; border:1px solid var(--cc-border); border-radius:6px; background:var(--admin-label-bg, #f8fafc); }
.qualification-preview img { display:block; max-width:100%; height:auto; }
.qualification-preview iframe { width:100%; min-height:min(68vh,720px); border:0; background:#fff; }
.qualification-load-error { display:flex; align-items:center; justify-content:space-between; gap:10px; margin-top:12px; padding:10px 12px; border:1px solid #fecdca; border-radius:6px; background:#fffafa; color:#b42318; font-size:12px; }
:global(.customer-editor-dialog.el-dialog) { display:flex; max-height:min(90vh,900px); flex-direction:column; }
:global(.customer-editor-dialog .el-dialog__body) { min-height:0; overflow-y:auto; overscroll-behavior:contain; }
:global(.customer-editor-dialog .el-dialog__footer) { flex:0 0 auto; }
.customer-editor-footer { display:flex; align-items:center; justify-content:space-between; gap:16px; } .customer-editor-footer > span { color:var(--cc-muted); font-size:12px; text-align:left; } .customer-editor-footer > div { display:flex; flex:0 0 auto; gap:8px; } .customer-editor-footer :deep(.el-button + .el-button) { margin-left:0; }
.decision-summary { display:flex; min-width:0; align-items:center; justify-content:space-between; gap:12px; margin-bottom:18px; padding:12px 14px; border-radius:8px; background:#f8fafc; } .decision-summary > * { min-width:0; overflow-wrap:anywhere; } .decision-summary strong { color:var(--cc-text); font-size:13px; } .decision-summary span { color:var(--cc-muted); font-size:11px; }
:global(.review-decision-dialog.el-dialog) { display:flex; max-height:min(90vh,860px); flex-direction:column; overflow:hidden; }
:global(.review-decision-dialog .el-dialog__header) { padding:22px 28px 16px; border-bottom:1px solid #eef1f6; }
:global(.review-decision-dialog .el-dialog__body) { min-height:0; overflow-y:auto; padding:20px 28px 24px; background:#fbfcfe; }
:global(.review-decision-dialog .el-dialog__footer) { flex:0 0 auto; }
.review-dialog-content { display:grid; gap:16px; }
.review-dialog-header { display:flex; align-items:center; justify-content:space-between; gap:16px; padding:14px 16px; border:1px solid #dbe4f0; border-radius:10px; background:linear-gradient(90deg,#f4f7ff,#fff); }
.qualification-preview iframe { width:100%; min-height:min(68vh,720px); border:0; background:var(--admin-surface, #fff); }
.qualification-load-error { display:flex; align-items:center; justify-content:space-between; gap:var(--admin-space-4, 10px); margin-top:var(--admin-space-5, 12px); padding:var(--admin-space-4, 10px) var(--admin-space-5, 12px); border:1px solid #fecdca; border-radius:6px; background:#fffafa; color:#b42318; font-size:var(--admin-font-caption, 12px); }
.customer-editor-footer { display:flex; align-items:center; justify-content:space-between; gap:var(--admin-space-6, 16px); } .customer-editor-footer > span { color:var(--cc-muted); font-size:var(--admin-font-caption, 12px); text-align:left; } .customer-editor-footer > div { display:flex; flex:0 0 auto; gap:var(--admin-space-3, 8px); } .customer-editor-footer :deep(.el-button + .el-button) { margin-left:0; }
.decision-summary { display:flex; min-width:0; align-items:center; justify-content:space-between; gap:var(--admin-space-5, 12px); background:var(--admin-label-bg, #f8fafc); } .decision-summary > * { min-width:0; overflow-wrap:anywhere; } .decision-summary strong { color:var(--cc-text); font-size:var(--admin-font-body, 13px); } .decision-summary span { color:var(--cc-muted); font-size:var(--admin-font-caption, 11px); }
.review-dialog-content { display:grid; }
.review-dialog-header { display:flex; align-items:center; justify-content:space-between; gap:var(--admin-space-6, 16px); border:1px solid #dbe4f0; }
.review-dialog-title { display:flex; min-width:0; align-items:center; gap:11px; }
.review-dialog-title > div { display:grid; min-width:0; gap:3px; }
.review-dialog-title strong { overflow:hidden; color:#18243a; font-size:16px; text-overflow:ellipsis; white-space:nowrap; }
.review-dialog-title span:not(.review-dialog-icon) { overflow:hidden; color:#718096; font-size:12px; text-overflow:ellipsis; white-space:nowrap; }
.review-dialog-icon { display:grid; width:36px; height:36px; flex:0 0 36px; place-items:center; border-radius:9px; background:#e9efff; color:#526fe8; }
.review-dialog-grid { display:grid; grid-template-columns:minmax(270px,.85fr) minmax(390px,1.15fr); gap:16px; }
.review-dialog-section,.review-decision-card { min-width:0; padding:16px; border:1px solid #e5eaf1; border-radius:10px; background:#fff; box-shadow:0 2px 8px rgba(15,23,42,.03); }
.review-section-heading { display:flex; align-items:flex-start; justify-content:space-between; gap:12px; margin-bottom:14px; }
.review-dialog-title strong { overflow:hidden; color:#18243a; font-size:var(--admin-font-title, 16px); text-overflow:ellipsis; }
.review-dialog-title span:not(.review-dialog-icon) { overflow:hidden; color:#718096; font-size:var(--admin-font-caption, 12px); text-overflow:ellipsis; white-space:nowrap; }
.review-dialog-icon { width:36px; height:var(--admin-row-height, 36px); flex:0 0 36px; place-items:center; border-radius:9px; background:#e9efff; color:#526fe8; }
.review-dialog-grid { display:grid; grid-template-columns:minmax(270px,.85fr) minmax(390px,1.15fr); }
.review-dialog-section { min-width:0; border:1px solid #e5eaf1; background:var(--admin-surface, #fff); }
.review-decision-card { min-width:0; border:1px solid #e5eaf1; background:var(--admin-surface, #fff); }
.review-section-heading { display:flex; align-items:flex-start; justify-content:space-between; gap:var(--admin-space-5, 12px); }
.review-section-heading > div { min-width:0; }
.review-section-heading h3 { margin:0; color:#1a2438; font-size:14px; font-weight:700; }
.review-section-heading span { display:block; margin-top:4px; color:#8a94a6; font-size:11px; line-height:1.4; }
.review-section-heading h3 { margin:0; color:#1a2438; font-size:var(--admin-font-section, 14px); font-weight:700; }
.review-section-heading span { display:block; margin-top:var(--admin-space-1, 4px); color:#8a94a6; line-height:1.4; }
.review-section-heading > svg { flex:0 0 auto; color:#657bea; }
.review-info-grid { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:10px; margin:0; }
.review-info-grid > div { min-width:0; padding:10px 11px; border-radius:7px; background:#f7f9fc; }
.review-info-grid dt { margin-bottom:4px; color:#8792a5; font-size:11px; }
.review-info-grid dd { min-width:0; margin:0; overflow:hidden; color:#28364d; font-size:13px; font-weight:600; text-overflow:ellipsis; white-space:nowrap; }
.review-document-table { overflow:hidden; border:1px solid #e4e9f1; border-radius:8px; }
.review-document-header,.review-document-row { display:grid; grid-template-columns:118px minmax(0,1fr) 138px; }
.review-document-header { background:#f5f7fb; color:#657188; font-size:11px; font-weight:700; }
.review-document-header > span,.review-document-row > span { min-width:0; padding:9px 10px; overflow-wrap:anywhere; border-right:1px solid #e4e9f1; }
.review-document-header > span:last-child,.review-document-row > span:last-child { border-right:0; }
.review-document-row { min-height:46px; border-top:1px solid #edf0f4; color:#334155; font-size:12px; }
.review-info-grid { margin:0; }
.review-document-table { overflow:hidden; border:1px solid #e4e9f1; }
.review-document-header { display:grid; grid-template-columns:118px minmax(0,1fr) 138px; }
.review-document-row { display:grid; grid-template-columns:118px minmax(0,1fr) 138px; }
.review-document-header { background:#f5f7fb; color:#657188; font-weight:700; }
.review-document-header > span { min-width:0; padding:9px var(--admin-space-4, 10px); overflow-wrap:anywhere; border-right:1px solid #e4e9f1; }
.review-document-row > span { min-width:0; padding:9px var(--admin-space-4, 10px); overflow-wrap:anywhere; border-right:1px solid #e4e9f1; }
.review-document-header > span:last-child { border-right:0; }
.review-document-row > span:last-child { border-right:0; }
.review-document-row { min-height:46px; border-top:1px solid #edf0f4; color:#334155; font-size:var(--admin-font-caption, 12px); }
.review-document-row > span { display:flex; align-items:center; }
.review-document-type { font-weight:650; }
.review-document-name { min-width:0; gap:6px; overflow:hidden; color:#4c5b72; text-overflow:ellipsis; white-space:nowrap; }
.review-document-name { min-width:0; gap:var(--admin-space-2, 6px); overflow:hidden; color:#4c5b72; text-overflow:ellipsis; }
.review-document-name svg { flex:0 0 auto; color:#8798cf; }
.review-document-actions { justify-content:flex-end; gap:6px; }
.review-document-actions { justify-content:flex-end; gap:var(--admin-space-2, 6px); }
.review-document-actions :deep(.el-button + .el-button) { margin-left:0; }
.review-document-state,.review-document-error,.review-document-empty { padding:14px; color:var(--cc-muted); font-size:12px; }
.review-document-error { display:flex; align-items:center; justify-content:space-between; gap:12px; margin-bottom:8px; border:1px solid #fecdca; border-radius:6px; background:#fffafa; color:#b42318; }
.review-document-state { padding:14px; color:var(--cc-muted); font-size:var(--admin-font-caption, 12px); }
.review-document-error { padding:14px; color:var(--cc-muted); font-size:var(--admin-font-caption, 12px); }
.review-document-empty { padding:14px; color:var(--cc-muted); font-size:var(--admin-font-caption, 12px); }
.review-document-error { display:flex; align-items:center; justify-content:space-between; gap:var(--admin-space-5, 12px); margin-bottom:var(--admin-space-3, 8px); border:1px solid #fecdca; border-radius:6px; background:#fffafa; color:#b42318; }
.review-document-empty { display:flex; align-items:center; justify-content:center; gap:7px; text-align:center; }.review-document-empty svg { color:#a5afc1; }
.review-decision-card :deep(.el-form-item) { margin-bottom:0; }.review-decision-card :deep(.el-form-item__label) { color:#4b5870; font-size:12px; font-weight:650; }.decision-summary > div > span { display:block; margin-top:4px; }.decision-type { margin:0 0 14px; }.password-input-row { display:grid; grid-template-columns:minmax(0,1fr) auto; width:100%; gap:8px; }.dialog-hint { margin:12px 0 0; color:var(--cc-muted); font-size:12px; line-height:1.6; }.archive-confirm { display:grid; gap:8px; margin:16px 0; }.archive-confirm strong { color:var(--cc-text); }.archive-confirm span { color:var(--cc-muted); font-size:12px; }
.approval-result-warning { display:flex; align-items:flex-start; gap:10px; padding:12px 14px; border:1px solid #fecdca; border-radius:8px; background:#fffafa; color:#b42318; } .approval-result-warning strong,.approval-result-warning span { display:block; } .approval-result-warning strong { font-size:14px; } .approval-result-warning span { margin-top:4px; font-size:12px; line-height:1.5; } .approval-credentials { margin:18px 0 0; border:1px solid var(--cc-border); border-radius:8px; } .approval-credentials > div { display:grid; grid-template-columns:90px minmax(0,1fr); gap:12px; padding:12px 14px; } .approval-credentials > div + div { border-top:1px solid var(--cc-border); } .approval-credentials dt { color:var(--cc-muted); font-size:12px; } .approval-credentials dd { display:flex; min-width:0; align-items:center; gap:10px; margin:0; } .approval-credentials code { min-width:0; flex:1; overflow-wrap:anywhere; color:var(--cc-text); font-size:14px; font-weight:700; }
.review-decision-card :deep(.el-form-item) { margin-bottom:0; }.review-decision-card :deep(.el-form-item__label) { color:#4b5870; font-size:var(--admin-font-caption, 12px); font-weight:650; }.decision-summary > div > span { display:block; margin-top:var(--admin-space-1, 4px); }.decision-type { margin:0 0 14px; }.password-input-row { display:grid; grid-template-columns:minmax(0,1fr) auto; width:100%; gap:var(--admin-space-3, 8px); }.dialog-hint { margin:var(--admin-space-5, 12px) 0 0; color:var(--cc-muted); font-size:var(--admin-font-caption, 12px); line-height:1.6; }.archive-confirm { display:grid; gap:var(--admin-space-3, 8px); margin:var(--admin-space-6, 16px) 0; }.archive-confirm strong { color:var(--cc-text); }.archive-confirm span { color:var(--cc-muted); font-size:var(--admin-font-caption, 12px); }
.approval-result-warning { display:flex; align-items:flex-start; gap:var(--admin-space-4, 10px); padding:var(--admin-space-5, 12px) 14px; border:1px solid #fecdca; border-radius:8px; background:#fffafa; color:#b42318; } .approval-result-warning strong { display:block; } .approval-result-warning span { display:block; } .approval-result-warning strong { font-size:var(--admin-font-section, 14px); } .approval-result-warning span { margin-top:var(--admin-space-1, 4px); font-size:var(--admin-font-caption, 12px); line-height:1.5; } .approval-credentials { margin:18px 0 0; border:1px solid var(--cc-border); } .approval-credentials > div { display:grid; grid-template-columns:90px minmax(0,1fr); gap:var(--admin-space-5, 12px); } .approval-credentials > div + div { border-top:1px solid var(--cc-border); } .approval-credentials dt { color:var(--cc-muted); font-size:var(--admin-font-caption, 12px); } .approval-credentials dd { display:flex; min-width:0; align-items:center; gap:var(--admin-space-4, 10px); margin:0; } .approval-credentials code { min-width:0; flex:1; overflow-wrap:anywhere; color:var(--cc-text); font-size:var(--admin-font-section, 14px); font-weight:700; }
@media (max-width:720px) { .customer-editor-footer,.review-sync-error { align-items:stretch; flex-direction:column; } .customers-table-actions { display:grid; grid-template-columns:1fr 1fr; width:100%; } .customers-table-actions :deep(.el-button) { min-height:40px; margin:0; } .qualification-table { overflow-x:auto; } .qualification-row { min-width:640px; } .customer-editor-footer > div { display:grid; grid-template-columns:repeat(auto-fit,minmax(110px,1fr)); width:100%; } .customer-editor-footer :deep(.el-button) { min-height:40px; margin:0; } .decision-summary { align-items:flex-start; flex-direction:column; }.review-dialog-grid { grid-template-columns:1fr; gap:12px; }.review-info-grid { grid-template-columns:1fr 1fr; }.review-document-header,.review-document-row { grid-template-columns:104px minmax(0,1fr) 124px; }.review-dialog-header { align-items:flex-start; }.password-input-row { grid-template-columns:1fr; }.table-actions :deep(.el-button) { min-height:40px; } }
@media (max-width:480px) { :global(.review-decision-dialog .el-dialog__header),:global(.review-decision-dialog .el-dialog__body) { padding-left:16px; padding-right:16px; }.review-info-grid { grid-template-columns:1fr; }.review-document-header { display:none; }.review-document-row { grid-template-columns:1fr; gap:6px; padding:10px; }.review-document-row > span { padding:0; border-right:0; }.review-document-actions { justify-content:flex-start; }.review-dialog-title strong { max-width:190px; } }
@media (max-width:360px) { .customers-table-actions,.capital-input { grid-template-columns:1fr; } }
@@ -73,7 +73,7 @@ onMounted(() => { void load() })
<template v-if="error" #state><section class="surface-panel error-state" role="alert"><div><strong>{{ error.message }}</strong><ApiErrorMeta :details="error" /></div><el-button type="primary" @click="load">重新加载</el-button></section></template>
<section v-if="!error" class="surface-panel table-panel">
<DataTable v-model:page="page" v-model:page-size="size" :data="rows" :columns="columns" :loading="loading" :total="total" preference-key="overseas-storage-fees-table" row-key="id" border stripe empty-text="暂无海外仓阶梯费用配置" @update:page="load" @update:page-size="load">
<template #actions="{ row }"><el-button v-if="access.can('platform:overseas-storage-fee:edit')" class="customer-action-button" size="small" plain @click="openEdit(row as OverseasStorageFeeConfig)">编辑</el-button></template>
<template #actions="{ row }"><el-button v-if="access.can('platform:overseas-storage-fee:edit')" class="density-action" size="small" plain @click="openEdit(row as OverseasStorageFeeConfig)">编辑</el-button></template>
<template #storage="{ row }">
<dl class="tier-list" aria-label="仓租阶梯单价,币种见本行币种列">
<div v-for="tier in tiersFor(row as OverseasStorageFeeConfig, 'STORAGE')" :key="tier.id" class="tier-item"><dt>{{ label(tier) }}:</dt><dd>{{ tier.unitPrice }}</dd></div>
@@ -89,7 +89,7 @@ onMounted(() => { void load() })
</section>
</ListPageLayout>
<AppDialog class="admin-density-dialog customer-density-dialog" v-model="dialogOpen" title="编辑海外仓阶梯费用" width="min(1120px, calc(100vw - 28px))" :confirm-loading="saving" @confirm="save">
<el-form label-position="top"><el-form-item label="币种"><el-select v-model="form.currency" style="width:180px"><el-option label="USD" value="USD" /><el-option label="CNY" value="CNY" /><el-option label="EUR" value="EUR" /></el-select></el-form-item><div class="groups"><div v-for="(group, index) in [storageTiers, returnTiers]" :key="index"><div class="group-heading"><h3>{{ index === 0 ? '仓租(CBM/天)' : '退货仓储费(托/天)' }} · {{ group.length }}档</h3><el-button type="primary" plain size="small" :disabled="form.tiers.length >= 100" @click="addTier(index === 0 ? 'STORAGE' : 'RETURN_STORAGE')">新增阶梯</el-button></div><el-table :data="group" size="small" border stripe><el-table-column label="操作" width="88" fixed="left"><template #default="{ row }"><el-button size="small" plain type="danger" :disabled="group.length <= 1" @click="removeTier(row as OverseasStorageFeeTier)">删除</el-button></template></el-table-column><el-table-column label="开始天数"><template #default="{ row }"><el-input-number v-model="row.startDay" :min="1" :max="36500" :controls="false" placeholder="请输入开始天数" /></template></el-table-column><el-table-column label="结束天数"><template #default="{ row }"><el-input-number v-if="row.endDay !== null" v-model="row.endDay" :min="1" :max="36500" :controls="false" placeholder="请输入结束天数" /><span v-else>以上</span></template></el-table-column><el-table-column label="价格"><template #default="{ row }"><el-input-number v-model="row.unitPrice" :min="0" :precision="6" :controls="false" placeholder="请输入单价" /></template></el-table-column></el-table></div></div></el-form>
<el-form label-position="top"><el-form-item label="币种"><el-select v-model="form.currency" style="width:180px"><el-option label="USD" value="USD" /><el-option label="CNY" value="CNY" /><el-option label="EUR" value="EUR" /></el-select></el-form-item><div class="groups"><div v-for="(group, index) in [storageTiers, returnTiers]" :key="index"><div class="group-heading"><h3>{{ index === 0 ? '仓租(CBM/天)' : '退货仓储费(托/天)' }} · {{ group.length }}档</h3><el-button class="density-action" data-tone="primary" type="primary" plain size="small" :disabled="form.tiers.length >= 100" @click="addTier(index === 0 ? 'STORAGE' : 'RETURN_STORAGE')">新增阶梯</el-button></div><el-table :data="group" size="small" border stripe><el-table-column label="操作" width="88" fixed="left"><template #default="{ row }"><el-button class="density-action" data-tone="danger" size="small" plain type="danger" :disabled="group.length <= 1" @click="removeTier(row as OverseasStorageFeeTier)">删除</el-button></template></el-table-column><el-table-column label="开始天数"><template #default="{ row }"><el-input-number v-model="row.startDay" :min="1" :max="36500" :controls="false" placeholder="请输入开始天数" /></template></el-table-column><el-table-column label="结束天数"><template #default="{ row }"><el-input-number v-if="row.endDay !== null" v-model="row.endDay" :min="1" :max="36500" :controls="false" placeholder="请输入结束天数" /><span v-else>以上</span></template></el-table-column><el-table-column label="价格"><template #default="{ row }"><el-input-number v-model="row.unitPrice" :min="0" :precision="6" :controls="false" placeholder="请输入单价" /></template></el-table-column></el-table></div></div></el-form>
</AppDialog>
</div>
</AdminPage>
@@ -97,17 +97,17 @@ onMounted(() => { void load() })
<style scoped>
.error-state { display:flex; justify-content:space-between; padding:20px; color:#b42318; }
.tier-list { display:flex; flex-wrap:wrap; gap:4px 16px; margin:0; padding:2px 0; white-space:normal; }
.tier-list { display:flex; flex-wrap:wrap; gap:var(--admin-space-1, 4px) var(--admin-space-6, 16px); margin:0; padding:2px 0; white-space:normal; }
.tier-list:empty::after { content:'—'; color:var(--cc-muted); }
.tier-item { display:flex; align-items:baseline; min-width:0; max-width:100%; line-height:24px; }
.tier-item dt { flex:0 0 auto; color:var(--cc-muted); font-size:12px; }
.tier-item dd { min-width:0; margin:0; color:var(--cc-text); font-size:13px; font-weight:600; font-variant-numeric:tabular-nums; overflow-wrap:anywhere; }
.groups { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:20px; }
.tier-item dt { flex:0 0 auto; color:var(--cc-muted); font-size:var(--admin-font-caption, 12px); }
.tier-item dd { min-width:0; margin:0; color:var(--cc-text); font-size:var(--admin-font-body, 13px); font-weight:600; font-variant-numeric:tabular-nums; overflow-wrap:anywhere; }
.groups { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); }
.groups > div { min-width:0; }
.groups :deep(.el-table) { width:100%; }
.groups :deep(.el-input-number) { width:100%; min-width:0; }
.groups h3 { margin:0 0 10px; font-size:15px; }
.group-heading { display:flex; align-items:center; justify-content:space-between; gap:12px; margin-bottom:10px; }
.groups h3 { margin:0 0 var(--admin-space-4, 10px); }
.group-heading { display:flex; align-items:center; justify-content:space-between; gap:var(--admin-space-5, 12px); margin-bottom:var(--admin-space-4, 10px); }
.group-heading h3 { min-width:0; margin:0; }
.group-heading :deep(.el-button) { flex:0 0 auto; }
@media (max-width:900px) { .groups { grid-template-columns:1fr; } }
@@ -4,6 +4,8 @@ import { resolve } from 'node:path'
const source = readFileSync(resolve(process.cwd(), 'src/modules/domestic-containers/pages/DomesticContainerInventoryDetailView.vue'), 'utf8')
const densitySource = readFileSync(resolve(process.cwd(), 'src/styles/adminDensity.css'), 'utf8')
describe('DomesticContainerInventoryDetailView', () => {
it('does not invent an action column for immutable transaction history', () => {
expect(source).not.toContain("label: '操作'")
@@ -17,6 +19,7 @@ describe('DomesticContainerInventoryDetailView', () => {
})
it('uses a four-column desktop field grid and preserves long business text', () => {
expect(source).toContain('grid-template-columns: repeat(4, minmax(0, 1fr))')
expect(source).toContain('overflow-wrap: anywhere')
expect(source).toContain('class="detail-fields density-fields"')
expect(densitySource).toContain('overflow-wrap: anywhere')
})
})
@@ -68,7 +68,7 @@ onMounted(load)
<template v-else-if="detail">
<section class="detail-section" aria-labelledby="inventory-basic-heading">
<h2 id="inventory-basic-heading">整柜信息</h2>
<dl class="detail-fields">
<dl class="detail-fields density-fields">
<div><dt>货物名称</dt><dd>{{ detail.inventory.cargoName }}</dd></div>
<div><dt>集装箱号</dt><dd>{{ detail.inventory.containerNo }}</dd></div>
<div><dt>国内仓</dt><dd>{{ detail.inventory.warehouseName }}</dd></div>
@@ -97,19 +97,16 @@ onMounted(load)
</template>
<style scoped>
.inventory-detail-page { display: grid; min-width: 0; gap: 12px; }
.detail-header { display: flex; align-items: center; gap: 12px; min-height: 40px; }
.detail-header h1 { margin: 0; font-size: 16px; letter-spacing: 0; }
.detail-section, .transactions { min-width: 0; border: 1px solid var(--cc-border); background: #fff; }
.detail-section { padding: 12px; }
.detail-section h2, .transactions h2 { margin: 0; font-size: 14px; letter-spacing: 0; }
.detail-fields { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); margin: 8px 0 0; border-top: 1px solid var(--cc-border); border-left: 1px solid var(--cc-border); }
.detail-fields > div { min-width: 0; display: grid; grid-template-columns: 112px minmax(0, 1fr); min-height: 36px; border-right: 1px solid var(--cc-border); border-bottom: 1px solid var(--cc-border); }
.detail-fields dt { display: flex; align-items: center; padding: 6px 8px; background: #f8fafc; color: var(--cc-muted); font-size: 12px; }
.detail-fields dd { align-self: center; margin: 6px 8px; font-size: 13px; overflow-wrap: anywhere; }
.transactions { padding-top: 12px; overflow: hidden; }
.transactions h2 { margin: 0 12px 8px; }
.detail-loading { min-height: 180px; padding: 24px; border: 1px solid var(--cc-border); background: #fff; color: var(--cc-muted); }
.inventory-detail-page { display: grid; min-width: 0; gap: var(--admin-space-5, 12px); }
.detail-header { display: flex; align-items: center; gap: var(--admin-space-5, 12px); min-height: 40px; }
.detail-header h1 { margin: 0; font-size: var(--admin-font-title, 16px); letter-spacing: 0; }
.detail-section, .transactions { min-width: 0; border: 1px solid var(--cc-border); background: var(--admin-surface, #fff); }
.detail-section { padding: var(--admin-space-5, 12px); }
.detail-section h2, .transactions h2 { margin: 0; font-size: var(--admin-font-section, 14px); letter-spacing: 0; }
.detail-fields { grid-template-columns: repeat(4, minmax(0, 1fr)); margin: var(--admin-space-3, 8px) 0 0; }
.transactions { padding-top: var(--admin-space-5, 12px); overflow: hidden; }
.transactions h2 { margin: 0 var(--admin-space-5, 12px) var(--admin-space-3, 8px); }
.detail-loading { min-height: 180px; padding: var(--admin-space-7, 24px); border: 1px solid var(--cc-border); background: var(--admin-surface, #fff); color: var(--cc-muted); }
@media (max-width: 960px) { .detail-fields { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 520px) { .detail-header { flex-wrap: wrap; } .detail-header h1 { width: 100%; font-size: 18px; } .detail-fields { grid-template-columns: 1fr; } }
</style>
@@ -15,7 +15,7 @@ describe('DomesticContainersView', () => {
for (const fact of ['summary.inboundBoxes', 'summary.outboundBoxes', 'summary.availableBoxes', 'summary.inboundVolumeCbm']) expect(source).toContain(fact)
expect(source).toContain("import './domesticContainersSummary.css'")
expect(summaryStyles).toContain('display: flex; flex-wrap: wrap')
expect(summaryStyles).toContain('font-size: 14px')
expect(summaryStyles).toContain('font-size: var(--admin-font-section, 14px)')
expect(summaryStyles).not.toContain('position: absolute')
})
it('does not offer an inventory detail action before an inbound has formed inventory', () => { expect(source).toContain("canViewInventory(row as ContainerOrder | InventoryRecord)"); expect(source).toContain("row.status === 'COMPLETED'") })
@@ -2,7 +2,7 @@
import '@/styles/adminDensity.css'
import { computed, onMounted, reactive, ref, watch } from 'vue'
import { ElMessage, type FormInstance, type FormRules } from 'element-plus'
import { Eye, Plus } from '@lucide/vue'
import { Plus } from '@lucide/vue'
import { useRouter } from 'vue-router'
import AdminPage from '@/shared/components/AdminPage.vue'
import ApiErrorMeta from '@/shared/components/ApiErrorMeta.vue'
@@ -74,7 +74,7 @@ watch([page, pageSize], load); onMounted(async () => { await Promise.all([loadWa
<template><AdminPage as="div" class="domestic-containers-page admin-density">
<LoadErrorState v-if="warehouseError" compact title="国内仓候选加载失败" :message="warehouseError.message" :loading="warehouseLoading" @retry="loadWarehouses"><template #details><ApiErrorMeta :details="warehouseError" /></template></LoadErrorState>
<ListPageLayout :aria-label="`${activeTab === 'inbound' ? '整柜入库单' : activeTab === 'outbound' ? '整柜出库单' : '整柜库存'}列表`"><template #toolbar><div class="business-query-panel"><div class="status-tabs" role="tablist" aria-label="整柜业务类型"><button v-for="(tab, index) in businessTabs" :key="tab.value" type="button" role="tab" :aria-selected="activeTab === tab.value" aria-controls="domestic-container-results" :tabindex="activeTab === tab.value ? 0 : -1" :class="['status-tab', { 'is-active': activeTab === tab.value }]" @click="switchTab(tab.value)" @keydown="handleTabKeydown($event, index)"><span class="density-tab-icon" aria-hidden="true">{{ tab.icon }}</span>{{ tab.label }}</button></div><QueryToolbar v-model="filters" class="business-query-toolbar" ariaLabel="整柜业务筛选" :fields="queryFields" :preference-key="`domestic-containers-${activeTab}-query`" @search="search" /></div></template><template v-if="loadError" #state><LoadErrorState title="整柜业务加载失败" :message="loadError.message" :loading="loading" @retry="load"><template #details><ApiErrorMeta :details="loadError" /></template></LoadErrorState></template><section v-if="!loadError" id="domestic-container-results" class="surface-panel table-panel"><div v-if="activeTab === 'inventory'" class="inventory-summary"><div><span>入库箱数</span><strong>{{ summary.inboundBoxes }}</strong></div><div><span>已出库箱数</span><strong>{{ summary.outboundBoxes }}</strong></div><div><span>当前库存</span><strong>{{ summary.availableBoxes }}</strong></div><div><span>累计入库体积</span><strong>{{ summary.inboundVolumeCbm.toFixed(2) }} m³</strong></div></div><DataTable v-model:page="page" v-model:page-size="pageSize" :data="rows" :columns="columns" :loading="loading" :total="total" stripe row-key="id" :preference-key="`domestic-containers-${activeTab}-table`" :empty-text="activeTab === 'inbound' ? '暂无整柜入库单' : activeTab === 'outbound' ? '暂无整柜出库单' : '暂无整柜库存'"><template #toolbar-actions><el-button v-if="activeTab === 'inbound' && access.can('platform:domestic-container:inbound:create')" type="primary" @click="openInbound"><Plus :size="16" />新建入库单</el-button><el-button v-if="activeTab === 'outbound' && access.can('platform:domestic-container:outbound:create')" type="primary" @click="openOutbound"><Plus :size="16" />新建出库单</el-button></template><template #actions="{ row }"><el-button v-if="canViewInventory(row as ContainerOrder | InventoryRecord)" size="small" type="primary" plain @click="viewInventory(row as ContainerOrder | InventoryRecord)"><Eye :size="15" />查看</el-button><span v-else>—</span></template><template #status="{ row }"><StatusBadge :status="statusLabel(row as ContainerOrder | InventoryRecord)" /></template></DataTable></section></ListPageLayout>
<ListPageLayout :aria-label="`${activeTab === 'inbound' ? '整柜入库单' : activeTab === 'outbound' ? '整柜出库单' : '整柜库存'}列表`"><template #toolbar><div class="business-query-panel"><div class="status-tabs" role="tablist" aria-label="整柜业务类型"><button v-for="(tab, index) in businessTabs" :key="tab.value" type="button" role="tab" :aria-selected="activeTab === tab.value" aria-controls="domestic-container-results" :tabindex="activeTab === tab.value ? 0 : -1" :class="['status-tab', { 'is-active': activeTab === tab.value }]" @click="switchTab(tab.value)" @keydown="handleTabKeydown($event, index)"><span class="density-tab-icon" aria-hidden="true">{{ tab.icon }}</span>{{ tab.label }}</button></div><QueryToolbar v-model="filters" class="business-query-toolbar" ariaLabel="整柜业务筛选" :fields="queryFields" :preference-key="`domestic-containers-${activeTab}-query`" @search="search" /></div></template><template v-if="loadError" #state><LoadErrorState title="整柜业务加载失败" :message="loadError.message" :loading="loading" @retry="load"><template #details><ApiErrorMeta :details="loadError" /></template></LoadErrorState></template><section v-if="!loadError" id="domestic-container-results" class="surface-panel table-panel"><div v-if="activeTab === 'inventory'" class="inventory-summary"><div><span>入库箱数</span><strong>{{ summary.inboundBoxes }}</strong></div><div><span>已出库箱数</span><strong>{{ summary.outboundBoxes }}</strong></div><div><span>当前库存</span><strong>{{ summary.availableBoxes }}</strong></div><div><span>累计入库体积</span><strong>{{ summary.inboundVolumeCbm.toFixed(2) }} m³</strong></div></div><DataTable v-model:page="page" v-model:page-size="pageSize" :data="rows" :columns="columns" :loading="loading" :total="total" stripe row-key="id" :preference-key="`domestic-containers-${activeTab}-table`" :empty-text="activeTab === 'inbound' ? '暂无整柜入库单' : activeTab === 'outbound' ? '暂无整柜出库单' : '暂无整柜库存'"><template #toolbar-actions><el-button v-if="activeTab === 'inbound' && access.can('platform:domestic-container:inbound:create')" type="primary" @click="openInbound"><Plus :size="16" />新建入库单</el-button><el-button v-if="activeTab === 'outbound' && access.can('platform:domestic-container:outbound:create')" type="primary" @click="openOutbound"><Plus :size="16" />新建出库单</el-button></template><template #actions="{ row }"><el-button v-if="canViewInventory(row as ContainerOrder | InventoryRecord)" class="density-action" size="small" @click="viewInventory(row as ContainerOrder | InventoryRecord)">查看</el-button><span v-else>—</span></template><template #status="{ row }"><StatusBadge :status="statusLabel(row as ContainerOrder | InventoryRecord)" /></template></DataTable></section></ListPageLayout>
<AppDialog v-model="dialogOpen" class="domestic-container-create-dialog admin-density-dialog" :title="dialogType === 'inbound' ? '新建整柜入库单' : '新建整柜出库单'" width="min(720px, calc(100vw - 28px))" :confirm-loading="saving" :close-on-click-modal="false" :confirm-text="dialogType === 'inbound' ? '创建入库单' : '创建出库单'" @confirm="submit"><LoadErrorState v-if="submitError" compact title="创建失败" :message="submitError.message" :show-retry="false"><template #details><ApiErrorMeta :details="submitError" /></template></LoadErrorState><el-form ref="formRef" :model="currentForm" :rules="rules" label-position="top"><el-row :gutter="16"><template v-if="dialogType === 'inbound'"><el-col :xs="24" :sm="12"><el-form-item label="国内仓" prop="warehouseId"><el-select v-model="inboundForm.warehouseId" :loading="warehouseLoading" :disabled="Boolean(warehouseError)" placeholder="请选择启用的国内仓"><el-option v-for="item in warehouseOptions" :key="String(item.value)" :label="item.label" :value="String(item.value)" /></el-select></el-form-item></el-col><el-col :xs="24" :sm="12"><el-form-item label="货物名称" prop="cargoName"><el-input v-model="inboundForm.cargoName" maxlength="128" placeholder="请输入货物名称" /></el-form-item></el-col><el-col :xs="24" :sm="12"><el-form-item label="集装箱号" prop="containerNo"><el-input v-model="inboundForm.containerNo" maxlength="32" placeholder="如 MSCU1234567" /></el-form-item></el-col><el-col :xs="24" :sm="12"><el-form-item label="箱子数量" prop="boxCount"><el-input-number v-model="inboundForm.boxCount" :min="1" :max="999999999" :precision="0" controls-position="right" placeholder="请输入入库箱数" /></el-form-item></el-col><el-col :xs="24" :sm="12"><el-form-item label="体积(m³)" prop="volumeCbm"><el-input-number v-model="inboundForm.volumeCbm" :min="0.0001" :max="999999999999.9999" :precision="4" controls-position="right" placeholder="请输入体积" /></el-form-item></el-col></template><template v-else><el-col :xs="24" :sm="12"><el-form-item label="国内仓" prop="warehouseId"><el-select v-model="outboundForm.warehouseId" :loading="warehouseLoading" :disabled="Boolean(warehouseError)" placeholder="请选择启用的国内仓" @change="loadCandidates"><el-option v-for="item in warehouseOptions" :key="String(item.value)" :label="item.label" :value="String(item.value)" /></el-select></el-form-item></el-col><el-col :xs="24" :sm="12"><el-form-item label="绑定集装箱号" prop="containerNo"><el-select v-model="outboundForm.inventoryId" :loading="candidateLoading" :disabled="!outboundForm.warehouseId || Boolean(candidateError)" placeholder="请选择已有集装箱号" @change="selectInventory"><el-option v-for="item in inventoryCandidates" :key="String(item.id)" :label="`${item.containerNo} · 可用 ${item.availableBoxes} 箱`" :value="String(item.id)" /></el-select><input v-model="outboundForm.containerNo" class="sr-only" tabindex="-1"></el-form-item></el-col><el-col :xs="24" :sm="12"><el-form-item label="货物名称"><el-input v-model="outboundForm.cargoName" readonly /></el-form-item></el-col><el-col :xs="24" :sm="12"><el-form-item label="出库箱数" prop="boxCount"><el-input-number v-model="outboundForm.boxCount" :min="1" :max="999999999" :precision="0" controls-position="right" placeholder="请输入出库箱数" /></el-form-item></el-col><el-col :xs="24" :sm="12"><el-form-item label="可用库存"><el-input :model-value="`${outboundForm.availableBoxes} 箱`" readonly /></el-form-item></el-col><el-col v-if="candidateError" :xs="24"><LoadErrorState compact title="可出库库存加载失败" :message="candidateError.message" @retry="loadCandidates"><template #details><ApiErrorMeta :details="candidateError" /></template></LoadErrorState></el-col></template><el-col :xs="24" :sm="12"><el-form-item :label="dialogType === 'inbound' ? '入库时间' : '出库时间'" prop="operationTime"><el-date-picker v-model="currentForm.operationTime" type="datetime" value-format="YYYY-MM-DDTHH:mm:ss" format="YYYY-MM-DD HH:mm" /></el-form-item></el-col><el-col :xs="24" :sm="12"><el-form-item label="状态" prop="status"><el-select v-model="currentForm.status"><el-option :label="dialogType === 'inbound' ? '已入库' : '已出库'" value="COMPLETED" /><el-option :label="dialogType === 'inbound' ? '待入库' : '待出库'" value="PENDING" /></el-select></el-form-item></el-col><el-col :xs="24"><el-form-item label="备注"><el-input v-model="currentForm.remark" type="textarea" :rows="3" maxlength="500" show-word-limit placeholder="请输入备注" /></el-form-item></el-col></el-row></el-form></AppDialog></AdminPage></template>
<style scoped>.domestic-containers-page{min-width:0}.business-query-panel{overflow:hidden;border:1px solid var(--cc-border);border-radius:4px;background:#fff}.status-tabs{display:flex;max-width:100%;overflow-x:auto;margin-bottom:0;border-bottom:1px solid #e5e7eb;background:#f9fafb;scrollbar-width:thin}.status-tab{position:relative;flex:0 0 auto;display:inline-flex;align-items:center;gap:6px;min-height:38px;padding:0 16px;border:0;background:transparent;color:var(--cc-muted);font:inherit;font-size:13px;cursor:pointer}.status-tab::after{position:absolute;right:12px;bottom:0;left:12px;height:3px;background:transparent;content:''}.status-tab:hover{color:var(--cc-primary);background:#f6f9fd}.status-tab:focus-visible{outline:2px solid var(--cc-primary);outline-offset:-2px}.status-tab.is-active{color:var(--cc-primary);font-weight:600}.status-tab.is-active::after{background:var(--cc-primary)}.business-query-toolbar{border:0}.inventory-summary{display:flex;align-items:center;gap:0;margin:0 16px 12px;border:1px solid var(--cc-border);background:#f8fafc}.inventory-summary>div{display:flex;align-items:baseline;gap:8px;min-width:0;padding:9px 16px;border-right:1px solid var(--cc-border)}.inventory-summary>div:last-child{border-right:0}.inventory-summary span{color:var(--cc-muted);font-size:12px}.inventory-summary strong{font-size:15px}.domestic-container-create-dialog :deep(.el-select),.domestic-container-create-dialog :deep(.el-date-editor),.domestic-container-create-dialog :deep(.el-input-number){width:100%}.sr-only{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0,0,0,0)}@media(max-width:720px){.status-tab{min-height:46px;padding:0 14px}.inventory-summary{display:grid;grid-template-columns:repeat(2,minmax(0,1fr))}.inventory-summary>div:nth-child(2){border-right:0}.inventory-summary>div:nth-child(-n+2){border-bottom:1px solid var(--cc-border)}}@media(max-width:360px){.status-tab{padding:0 10px;font-size:13px}.inventory-summary{grid-template-columns:1fr}.inventory-summary>div{border-right:0;border-bottom:1px solid var(--cc-border)}.inventory-summary>div:last-child{border-bottom:0}}</style>
<style scoped>.domestic-containers-page{min-width:0}.business-query-panel{overflow:hidden;border:1px solid var(--cc-border);border-radius:var(--admin-radius, 4px);background:var(--admin-surface, #fff)}.status-tabs{display:flex;max-width:100%;overflow-x:auto;margin-bottom:0;border-bottom:1px solid var(--admin-table-border, #e5e7eb);background:#f9fafb;scrollbar-width:thin}.status-tab{position:relative;flex:0 0 auto;display:inline-flex;align-items:center;gap:var(--admin-space-2, 6px);min-height:var(--admin-tab-height, 38px);padding:0 var(--admin-space-6, 16px);border:0;background:transparent;color:var(--cc-muted);font:inherit;font-size:var(--admin-font-body, 13px);cursor:pointer}.status-tab::after{position:absolute;right:12px;bottom:0;left:12px;height:3px;background:transparent;content:''}.status-tab:hover{color:var(--cc-primary);background:#f6f9fd}.status-tab:focus-visible{outline:2px solid var(--cc-primary);outline-offset:-2px}.status-tab.is-active{color:var(--cc-primary);font-weight:600}.status-tab.is-active::after{background:var(--cc-primary)}.business-query-toolbar{border:0}.inventory-summary{display:flex;align-items:center;gap:0;margin:0 var(--admin-space-6, 16px) var(--admin-space-5, 12px);border:1px solid var(--cc-border);background:var(--admin-label-bg, #f8fafc)}.inventory-summary>div{display:flex;align-items:baseline;gap:var(--admin-space-3, 8px);min-width:0;padding:9px var(--admin-space-6, 16px);border-right:1px solid var(--cc-border)}.inventory-summary>div:last-child{border-right:0}.inventory-summary span{color:var(--cc-muted);font-size:var(--admin-font-caption, 12px)}.inventory-summary strong{font-size:15px}.domestic-container-create-dialog :deep(.el-select),.domestic-container-create-dialog :deep(.el-date-editor),.domestic-container-create-dialog :deep(.el-input-number){width:100%}.sr-only{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0,0,0,0)}@media(max-width:720px){.status-tab{min-height:46px;padding:0 14px}.inventory-summary{display:grid;grid-template-columns:repeat(2,minmax(0,1fr))}.inventory-summary>div:nth-child(2){border-right:0}.inventory-summary>div:nth-child(-n+2){border-bottom:1px solid var(--cc-border)}}@media(max-width:360px){.status-tab{padding:0 10px;font-size:13px}.inventory-summary{grid-template-columns:1fr}.inventory-summary>div{border-right:0;border-bottom:1px solid var(--cc-border)}.inventory-summary>div:last-child{border-bottom:0}}</style>
@@ -1,9 +1,9 @@
.domestic-containers-page .table-panel .inventory-summary {
display: flex; flex-wrap: wrap; gap: 6px 28px; margin: 0;
padding: 8px 12px; border: 0; border-bottom: 1px solid var(--cc-border); background: #fff;
display: flex; flex-wrap: wrap; gap: var(--admin-space-2, 6px) 28px; margin: 0;
padding: var(--admin-space-3, 8px) var(--admin-space-5, 12px); border: 0; border-bottom: 1px solid var(--cc-border); background: var(--admin-surface, #fff);
}
.domestic-containers-page .table-panel .inventory-summary > div {
display: flex; align-items: baseline; min-width: 0; gap: 8px; padding: 0; border: 0;
display: flex; align-items: baseline; min-width: 0; gap: var(--admin-space-3, 8px); padding: 0; border: 0;
}
.domestic-containers-page .table-panel .inventory-summary span { color: var(--cc-muted); font-size: 12px; }
.domestic-containers-page .table-panel .inventory-summary strong { font-size: 14px; font-weight: 600; }
.domestic-containers-page .table-panel .inventory-summary span { color: var(--cc-muted); font-size: var(--admin-font-caption, 12px); }
.domestic-containers-page .table-panel .inventory-summary strong { font-size: var(--admin-font-section, 14px); font-weight: 600; }
@@ -4,7 +4,7 @@ import '../measurement/measurementDensity.css'
import { computed, onActivated, onBeforeUnmount, onMounted, ref, watch } from 'vue'
import { useRouter } from 'vue-router'
import { ElMessage, ElMessageBox } from 'element-plus'
import { CircleCheckBig, Eye, Ruler, SearchCheck } from '@lucide/vue'
import { CircleCheckBig } from '@lucide/vue'
import { useAccessPreviewStore } from '@/core/access/accessPreview'
import { listPlatformTenants, type PlatformTenantResource } from '@/modules/iam'
import { listPlatformWarehouses } from '@/modules/warehouses'
@@ -374,14 +374,12 @@ onBeforeUnmount(() => { disposed = true; requestSerial += 1; optionsRequestSeria
</template>
<template #pagination-left>当前显示 {{ result.records.length }} 条 · 共 {{ result.total }} 条</template>
<template #actions="{ row }">
<div class="row-actions">
<el-button v-if="access.can(actionPermission)" size="small" type="primary" @click="openTask(row as InboundOrderSummary)">
<SearchCheck v-if="isReview" :size="15" />
<Ruler v-else :size="15" />
<div class="row-actions density-actions">
<el-button v-if="access.can(actionPermission)" class="density-action" :data-tone="isReview ? 'warning' : 'purple'" size="small" @click="openTask(row as InboundOrderSummary)">
{{ taskLabel }}
</el-button>
<el-button v-if="access.can('platform:firstleg:inbound:view') && (row as InboundOrderSummary).orderId" size="small" plain @click="openDetail(row as InboundOrderSummary)">
<Eye :size="15" />查看
<el-button v-if="access.can('platform:firstleg:inbound:view') && (row as InboundOrderSummary).orderId" class="density-action" size="small" @click="openDetail(row as InboundOrderSummary)">
查看
</el-button>
<span v-if="!access.can(actionPermission) && (!access.can('platform:firstleg:inbound:view') || !(row as InboundOrderSummary).orderId)" class="muted-action">无操作权限</span>
</div>
@@ -453,14 +451,13 @@ onBeforeUnmount(() => { disposed = true; requestSerial += 1; optionsRequestSeria
<style scoped>
.inbound-task-page { min-width: 0; }
.state-stack { display: grid; gap: 12px; }
.state-stack { display: grid; gap: var(--admin-space-5, 12px); }
.review-navigation-error { margin-bottom: 14px; }
.row-actions { display: flex; align-items: center; gap: 8px; white-space: nowrap; }
.row-actions .el-button { margin: 0; }
.row-actions { white-space: nowrap; }
.primary-cell { display: grid; gap: 3px; min-width: 0; }
.primary-cell strong, .primary-cell span { overflow-wrap: anywhere; }
.inbound-code-cell { display: grid; gap: 3px; min-width: 0; }
.primary-cell span, .muted-action { color: var(--cc-muted); font-size: 12px; }
.primary-cell span, .muted-action { color: var(--cc-muted); font-size: var(--admin-font-caption, 12px); }
@media (max-width: 520px) {
.row-actions { align-items: stretch; flex-direction: column; }
.row-actions :deep(.el-button) { width: 100%; }
@@ -469,7 +466,7 @@ onBeforeUnmount(() => { disposed = true; requestSerial += 1; optionsRequestSeria
<style>
.inbound-task-dialog.admin-density-workspace.el-dialog:not(.is-fullscreen) { height: auto; max-height: 92vh; overflow: hidden; }
.inbound-task-dialog .el-dialog__body { max-height: calc(92vh - 62px); overflow: auto; padding: 12px 16px; }
.inbound-task-dialog .el-dialog__body { max-height: calc(92vh - 62px); }
@media (max-width: 520px) {
.inbound-task-dialog .el-dialog__body { padding: 14px; }
}
@@ -7,6 +7,8 @@ const panelSource = readFileSync(resolve(process.cwd(), 'src/modules/inbounds/me
const taskListSource = readFileSync(resolve(process.cwd(), 'src/modules/inbounds/components/InboundTaskList.vue'), 'utf8')
const orderListSource = readFileSync(resolve(process.cwd(), 'src/modules/unified-orders/pages/UnifiedOrdersView.vue'), 'utf8')
const densitySource = readFileSync(resolve(process.cwd(), 'src/styles/adminDensity.css'), 'utf8')
describe('InboundMeasurementDialog', () => {
it('owns the complete measurement workspace behind one centered dialog', () => {
expect(source).toContain("import AppDialog from '@/shared/components/AppDialog.vue'")
@@ -14,7 +16,8 @@ describe('InboundMeasurementDialog', () => {
expect(source).toContain('class="inbound-measurement-workspace-dialog admin-density-dialog admin-density-workspace inbound-density"')
expect(source).toContain('width="min(1560px, calc(100vw - 48px))"')
expect(source).toContain('height: 92vh')
expect(source).toContain('padding: 10px 20px')
expect(densitySource).toContain('.admin-density-dialog.el-dialog .el-dialog__header')
expect(densitySource).toContain('padding: var(--admin-space-4, 10px) 48px var(--admin-space-4, 10px) var(--admin-space-6, 16px)')
expect(source).toContain('<WeightOcrPanel')
expect(source).toContain('<InboundMeasurementPanel')
expect(source).toContain('class="measurement-dialog-weight"')
@@ -127,9 +127,9 @@ function handleSubmitted() {
<style scoped>
.measurement-dialog-workspace { display: grid; min-width: 0; }
.measurement-dialog-weight { min-width: 0; padding: 0 0 16px; border-bottom: 1px solid var(--cc-border); }
.measurement-dialog-footer { display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: 10px; }
.measurement-dialog-footer__hint { margin-right: auto; color: var(--el-color-warning-dark-2); font-size: 12px; }
.measurement-dialog-weight { min-width: 0; padding: 0 0 var(--admin-space-6, 16px); border-bottom: 1px solid var(--cc-border); }
.measurement-dialog-footer { display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: var(--admin-space-4, 10px); }
.measurement-dialog-footer__hint { margin-right: auto; color: var(--el-color-warning-dark-2); font-size: var(--admin-font-caption, 12px); }
.measurement-dialog-footer :deep(.el-button) { margin-left: 0; }
@media (max-width: 720px) {
.measurement-dialog-weight { padding-bottom: 14px; }
@@ -140,9 +140,7 @@ function handleSubmitted() {
<style>
.inbound-measurement-workspace-dialog.el-dialog { display: flex; max-width: calc(100vw - 32px); height: 92vh; max-height: 92vh; margin-bottom: 0; flex-direction: column; overflow: hidden; }
.inbound-measurement-workspace-dialog .el-dialog__header { flex: 0 0 auto; margin: 0; padding: 10px 20px; border-bottom: 1px solid var(--cc-border); }
.inbound-measurement-workspace-dialog .el-dialog__body { min-height: 0; flex: 1 1 auto; overflow-y: auto; padding: 12px 16px; }
.inbound-measurement-workspace-dialog .el-dialog__footer { flex: 0 0 auto; padding: 8px 16px; border-top: 1px solid var(--cc-border); }
.inbound-measurement-workspace-dialog .el-dialog__body { flex: 1 1 auto; }
.inbound-measurement-workspace-dialog .measurement-panel { border: 0; border-radius: 0; box-shadow: none; }
.inbound-measurement-workspace-dialog .measurement-panel__heading .measurement-panel__actions,
.inbound-measurement-workspace-dialog .measurement-panel__footer { display: none; }
@@ -280,7 +280,7 @@ describe('InboundMeasurementPanel', () => {
expect(source).not.toContain('<el-collapse-item name="declaration"')
expect(source).toContain('grid-template-columns: repeat(4, minmax(0, 1fr))')
expect(source).toContain('.measurement-data-grid { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr));')
expect(source).toContain('.measurement-table { width: 100%; font-size: 12px; }')
expect(source).toContain('.measurement-table { width: 100%; font-size: var(--admin-font-caption, 12px); }')
expect(source).toContain('.measurement-data-grid :deep(.el-input__wrapper)')
expect(source).toContain('defineExpose({ saveDraft, submitForReview, updateWeightEvidence, submissionBlockReason, mayMaintain, maySubmit, saving, submitting, canEdit, canSubmit, isDirty })')
})
@@ -908,71 +908,71 @@ defineExpose({ saveDraft, submitForReview, updateWeightEvidence, submissionBlock
.measurement-panel__heading > div:first-child { display: flex; min-width: 0; align-items: flex-start; gap: 11px; }
.measurement-panel__icon { display: grid; width: 34px; height: 34px; flex: 0 0 34px; place-items: center; border-radius: 8px; background: #eef4ff; color: var(--cc-primary); }
.measurement-panel h2, .measurement-panel h3 { margin: 0; color: var(--cc-text); }
.measurement-panel h2 { font-size: 16px; }
.measurement-panel h3 { font-size: 14px; }
.measurement-panel__heading p, .measurement-section__heading p { margin: 4px 0 0; color: var(--cc-muted); font-size: 12px; line-height: 1.55; }
.measurement-panel__actions, .measurement-panel__footer > div { display: flex; flex: 0 0 auto; flex-wrap: wrap; justify-content: flex-end; gap: 8px; }
.measurement-declaration-table { margin-top: 12px; }
.measurement-panel h2 { font-size: var(--admin-font-title, 16px); }
.measurement-panel h3 { font-size: var(--admin-font-section, 14px); }
.measurement-panel__heading p, .measurement-section__heading p { margin: var(--admin-space-1, 4px) 0 0; color: var(--cc-muted); font-size: var(--admin-font-caption, 12px); line-height: 1.55; }
.measurement-panel__actions, .measurement-panel__footer > div { display: flex; flex: 0 0 auto; flex-wrap: wrap; justify-content: flex-end; gap: var(--admin-space-3, 8px); }
.measurement-declaration-table { margin-top: var(--admin-space-5, 12px); }
.measurement-panel__actions :deep(.el-button), .measurement-panel__footer :deep(.el-button) { margin-left: 0; }
.measurement-skeleton { padding: 24px; }
.measurement-panel > :deep(.el-alert), .measurement-alert { margin: 16px 20px 0; width: auto; }
.measurement-skeleton { padding: var(--admin-space-7, 24px); }
.measurement-panel > :deep(.el-alert), .measurement-alert { margin: var(--admin-space-6, 16px) 20px 0; width: auto; }
.measurement-alert p { margin: 5px 0 0; line-height: 1.55; }
.measurement-alert ul { margin: 7px 0 0; padding-left: 18px; font-size: 12px; line-height: 1.65; }
.measurement-alert ul { margin: 7px 0 0; padding-left: 18px; font-size: var(--admin-font-caption, 12px); line-height: 1.65; }
.measurement-alert .el-button { margin-top: 9px; }
.measurement-form fieldset { min-width: 0; margin: 0; padding: 0; border: 0; }
.measurement-section { padding: 16px 0px; border-top: 1px solid var(--cc-border); }
.measurement-summary-section { display: grid; gap: 12px; border-top: 0; }
.measurement-section { padding: var(--admin-space-6, 16px) 0px; border-top: 1px solid var(--cc-border); }
.measurement-summary-section { display: grid; gap: var(--admin-space-5, 12px); border-top: 0; }
.measurement-summary-header { display: flex; align-items: flex-start; justify-content: space-between; gap: 20px; }
.measurement-summary-context { display: flex; min-width: 0; flex-wrap: wrap; align-items: center; gap: 8px 20px; }
.measurement-summary-header span { color: var(--cc-muted); font-size: 11px; }
.measurement-summary-context { display: flex; min-width: 0; flex-wrap: wrap; align-items: center; gap: var(--admin-space-3, 8px) 20px; }
.measurement-summary-header span { color: var(--cc-muted); font-size: var(--admin-font-caption, 11px); }
.measurement-deviation-alert { margin: 0; width: auto; }
.measurement-deviation-alert p { margin: 3px 0 0; line-height: 1.55; }
.measurement-summary-context p { display: inline-flex; min-width: 0; align-items: baseline; gap: 6px; margin: 0; overflow-wrap: anywhere; color: var(--cc-text); font-size: 12px; font-weight: 600; }
.measurement-summary-context p { display: inline-flex; min-width: 0; align-items: baseline; gap: var(--admin-space-2, 6px); margin: 0; overflow-wrap: anywhere; color: var(--cc-text); font-size: var(--admin-font-caption, 12px); font-weight: 600; }
.measurement-summary-status { display: flex; flex: 0 0 auto; align-items: center; gap: 9px; }
.measurement-metrics { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); margin: 2px 0; }
.measurement-subheading { margin: 0; color: #344054; font-size: 12px; font-weight: 700; }
.measurement-metrics > div { display: grid; min-width: 0; min-height: 58px; align-content: center; gap: 4px; padding: 5px 18px; border-left: 1px solid var(--cc-border); }
.measurement-subheading { margin: 0; color: #344054; font-size: var(--admin-font-caption, 12px); font-weight: 700; }
.measurement-metrics > div { display: grid; min-width: 0; min-height: 58px; align-content: center; gap: var(--admin-space-1, 4px); padding: 5px 18px; border-left: 1px solid var(--cc-border); }
.measurement-metrics > div:first-child { padding-left: 0; border-left: 0; }
.measurement-metrics dt { color: var(--cc-muted); font-size: 12px; }
.measurement-metrics dt { color: var(--cc-muted); font-size: var(--admin-font-caption, 12px); }
.measurement-metrics dd { min-width: 0; margin: 0; color: var(--cc-text); font-size: 18px; font-weight: 700; overflow-wrap: anywhere; }
.measurement-metrics small { color: var(--cc-muted); font-size: 11px; font-weight: 500; }
.measurement-metrics small { color: var(--cc-muted); font-size: var(--admin-font-caption, 11px); font-weight: 500; }
.measurement-metric--primary dd { color: var(--cc-primary); font-size: 20px; }
.measurement-metric--warning dd { color: var(--el-color-warning-dark-2); }
.measurement-declaration { padding-top: 12px; border-top: 1px solid var(--cc-border); }
.measurement-declaration h4 { margin: 0 0 10px; color: #344054; font-size: 12px; font-weight: 700; }
.measurement-section__heading { display: flex; align-items: flex-start; justify-content: space-between; gap: 18px; margin-bottom: 16px; }
.measurement-declaration { padding-top: var(--admin-space-5, 12px); border-top: 1px solid var(--cc-border); }
.measurement-declaration h4 { margin: 0 0 var(--admin-space-4, 10px); color: #344054; font-size: var(--admin-font-caption, 12px); font-weight: 700; }
.measurement-section__heading { display: flex; align-items: flex-start; justify-content: space-between; gap: 18px; margin-bottom: var(--admin-space-6, 16px); }
.measurement-section__heading > div { min-width: 0; }
.measurement-section-actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 8px; }.measurement-section-actions :deep(.el-button) { margin-left: 0; }
.warehouse-suggestion-form { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 16px; }.warehouse-suggestion-form :deep(.el-select),.warehouse-suggestion-form :deep(.el-input-number) { width: 100%; }.warehouse-suggestion-form :deep(.el-form-item:last-child) { grid-column: 1 / -1; }.suggestion-preview { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); margin: 4px 0 0; border: 1px solid var(--cc-border); border-radius: 6px; }.suggestion-preview div { padding: 12px; border-right: 1px solid var(--cc-border); }.suggestion-preview div:last-child { border-right: 0; }.suggestion-preview dt { color: var(--cc-muted); font-size: 11px; }.suggestion-preview dd { margin: 5px 0 0; color: var(--cc-text); font-size: 13px; font-weight: 650; }
.measurement-form-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 16px; }
.measurement-data-grid { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 12px; }
.measurement-form label { display: flex; min-width: 0; flex-direction: column; gap: 7px; color: #344054; font-size: 12px; font-weight: 650; }
.measurement-section-actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: var(--admin-space-3, 8px); }.measurement-section-actions :deep(.el-button) { margin-left: 0; }
.warehouse-suggestion-form { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 var(--admin-space-6, 16px); }.warehouse-suggestion-form :deep(.el-select),.warehouse-suggestion-form :deep(.el-input-number) { width: 100%; }.warehouse-suggestion-form :deep(.el-form-item:last-child) { grid-column: 1 / -1; }.suggestion-preview { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); margin: var(--admin-space-1, 4px) 0 0; border: 1px solid var(--cc-border); border-radius: 6px; }.suggestion-preview div { padding: var(--admin-space-5, 12px); border-right: 1px solid var(--cc-border); }.suggestion-preview div:last-child { border-right: 0; }.suggestion-preview dt { color: var(--cc-muted); font-size: var(--admin-font-caption, 11px); }.suggestion-preview dd { margin: 5px 0 0; color: var(--cc-text); font-size: var(--admin-font-body, 13px); font-weight: 650; }
.measurement-form-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 var(--admin-space-6, 16px); }
.measurement-data-grid { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: var(--admin-space-5, 12px); }
.measurement-form label { display: flex; min-width: 0; flex-direction: column; gap: 7px; color: #344054; font-size: var(--admin-font-caption, 12px); font-weight: 650; }
.measurement-form :deep(.el-input-number), .measurement-form :deep(.el-input) { width: 100%; }
.measurement-data-grid label { display: flex; min-width: 0; flex-direction: column; gap: 5px; color: #344054; font-size: 12px; font-weight: 600; }
.measurement-data-grid label { display: flex; min-width: 0; flex-direction: column; gap: 5px; color: #344054; font-size: var(--admin-font-caption, 12px); font-weight: 600; }
.measurement-data-grid :deep(.el-input__wrapper) { border-radius: 3px; }
.measurement-readonly-field { display: grid; min-width: 0; align-content: start; gap: 7px; }
.measurement-readonly-field span { color: #344054; font-size: 12px; font-weight: 600; }
.measurement-readonly-field strong { min-height: 32px; padding: 7px 0; overflow-wrap: anywhere; color: var(--cc-text); font-size: 13px; line-height: 18px; }
.measurement-readonly-field span { color: #344054; font-size: var(--admin-font-caption, 12px); font-weight: 600; }
.measurement-readonly-field strong { min-height: var(--admin-control-height, 32px); padding: 7px 0; overflow-wrap: anywhere; color: var(--cc-text); font-size: var(--admin-font-body, 13px); line-height: 18px; }
.measurement-image-preview { display: grid; min-height: 240px; place-items: center; }
.measurement-image-preview img { display: block; width: auto; max-width: 100%; max-height: 68vh; object-fit: contain; }
.manual-entry-alert { margin-bottom: 16px; }
.manual-entry-alert ul { margin: 6px 0 0; padding-left: 18px; }
.manual-entry-alert { margin-bottom: var(--admin-space-6, 16px); }
.manual-entry-alert ul { margin: var(--admin-space-2, 6px) 0 0; padding-left: 18px; }
.manual-entry-form :deep(.el-input-number), .manual-entry-form :deep(.el-date-editor) { width: 100%; }
.manual-entry-upload { width: 100%; }
.measurement-items-section { background: #fff; }
.measurement-table { width: 100%; font-size: 12px; }
.measurement-table :deep(.el-table__header th) { padding: 8px 0; background: #f6f8fa; color: #475467; font-size: 11px; font-weight: 700; }
.measurement-items-section { background: var(--admin-surface, #fff); }
.measurement-table { width: 100%; font-size: var(--admin-font-caption, 12px); }
.measurement-table :deep(.el-table__header th) { padding: var(--admin-space-3, 8px) 0; background: #f6f8fa; color: #475467; font-size: var(--admin-font-caption, 11px); font-weight: 700; }
.measurement-table :deep(.el-table__cell) { padding: 7px 0; }
.measurement-table :deep(.cell) { line-height: 1.35; }
.measurement-table :deep(.measurement-table__row--error > td.el-table__cell) { background: var(--el-color-danger-light-9); }
.warehouse-suggestion-table { display: grid; gap: 10px; margin-top: 18px; }.warehouse-suggestion-table h3 { margin: 0; color: var(--cc-text); font-size: 14px; }
.warehouse-suggestion-table { display: grid; gap: var(--admin-space-4, 10px); margin-top: 18px; }.warehouse-suggestion-table h3 { margin: 0; color: var(--cc-text); font-size: var(--admin-font-section, 14px); }
.measurement-box-code { font-weight: 650; }
.measurement-count-editor { width: 104px !important; }
.measurement-row-details { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 8px 20px; margin: 0; padding: 4px 18px 8px 62px; }
.measurement-row-details dt { color: var(--cc-muted); font-size: 11px; }
.measurement-row-details dd { margin: 3px 0 0; overflow-wrap: anywhere; color: var(--cc-text); font-size: 12px; font-weight: 600; }
.measurement-panel__footer { position: sticky; bottom: 0; z-index: 2; display: flex; align-items: center; justify-content: space-between; gap: 16px; padding: 12px 20px; border-top: 1px solid var(--cc-border); background: #fff; }
.measurement-panel__footer > span { display: inline-flex; align-items: center; gap: 6px; color: var(--cc-muted); font-size: 12px; }
.measurement-row-details { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--admin-space-3, 8px) 20px; margin: 0; padding: var(--admin-space-1, 4px) 18px var(--admin-space-3, 8px) 62px; }
.measurement-row-details dt { color: var(--cc-muted); font-size: var(--admin-font-caption, 11px); }
.measurement-row-details dd { margin: 3px 0 0; overflow-wrap: anywhere; color: var(--cc-text); font-size: var(--admin-font-caption, 12px); font-weight: 600; }
.measurement-panel__footer { position: sticky; bottom: 0; z-index: 2; display: flex; align-items: center; justify-content: space-between; gap: var(--admin-space-6, 16px); padding: var(--admin-space-5, 12px) 20px; border-top: 1px solid var(--cc-border); background: var(--admin-surface, #fff); }
.measurement-panel__footer > span { display: inline-flex; align-items: center; gap: var(--admin-space-2, 6px); color: var(--cc-muted); font-size: var(--admin-font-caption, 12px); }
.measurement-panel__footer > span:has(svg) { color: #b54708; font-weight: 650; }
.sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; }
@@ -1,30 +1,30 @@
@media (min-width: 961px) {
.inbound-density.admin-density-dialog .measurement-dialog-weight { padding-bottom: 10px; }
.inbound-density.admin-density-dialog .measurement-section { padding: 10px 0; }
.inbound-density.admin-density-dialog .measurement-summary-section { gap: 8px; }
.inbound-density.admin-density-dialog .measurement-section__heading { align-items: center; margin-bottom: 8px; }
.inbound-density.admin-density-dialog .measurement-dialog-weight { padding-bottom: var(--admin-space-4, 10px); }
.inbound-density.admin-density-dialog .measurement-section { padding: var(--admin-space-4, 10px) 0; }
.inbound-density.admin-density-dialog .measurement-summary-section { gap: var(--admin-space-3, 8px); }
.inbound-density.admin-density-dialog .measurement-section__heading { align-items: center; margin-bottom: var(--admin-space-3, 8px); }
.inbound-density.admin-density-dialog .measurement-metrics { grid-template-columns: repeat(5, minmax(0, 1fr)); }
.inbound-density.admin-density-dialog .measurement-metrics > div { min-height: 36px; padding: 6px 10px; display: flex; align-items: baseline; gap: 8px; }
.inbound-density.admin-density-dialog .measurement-metrics dd { font-size: 14px; }
.inbound-density.admin-density-dialog .measurement-metrics small { font-size: 12px; }
.inbound-density.admin-density-dialog .measurement-data-grid { gap: 8px 12px; }
.inbound-density.admin-density-dialog .measurement-declaration { padding-top: 8px; }
.inbound-density.admin-density-dialog .measurement-declaration h4 { margin-bottom: 6px; font-size: 13px; }
.inbound-density.admin-density-dialog .measurement-panel > .el-alert { margin: 8px 0; }
.inbound-density.admin-density-dialog .measurement-summary-header span { font-size: 12px; }
.inbound-density.admin-density-dialog .measurement-table .el-table__header th { padding: 4px 0; font-size: 13px; }
.inbound-density.admin-density-dialog .weight-heading { flex-wrap: wrap; gap: 8px; padding-bottom: 8px; }
.inbound-density.admin-density-dialog .weight-heading__actions { flex-wrap: wrap; gap: 12px; }
.inbound-density.admin-density-dialog .measurement-metrics > div { min-height: var(--admin-row-height, 36px); padding: var(--admin-space-2, 6px) var(--admin-space-4, 10px); display: flex; align-items: baseline; gap: var(--admin-space-3, 8px); }
.inbound-density.admin-density-dialog .measurement-metrics dd { font-size: var(--admin-font-section, 14px); }
.inbound-density.admin-density-dialog .measurement-metrics small { font-size: var(--admin-font-caption, 12px); }
.inbound-density.admin-density-dialog .measurement-data-grid { gap: var(--admin-space-3, 8px) var(--admin-space-5, 12px); }
.inbound-density.admin-density-dialog .measurement-declaration { padding-top: var(--admin-space-3, 8px); }
.inbound-density.admin-density-dialog .measurement-declaration h4 { margin-bottom: var(--admin-space-2, 6px); font-size: var(--admin-font-body, 13px); }
.inbound-density.admin-density-dialog .measurement-panel > .el-alert { margin: var(--admin-space-3, 8px) 0; }
.inbound-density.admin-density-dialog .measurement-summary-header span { font-size: var(--admin-font-caption, 12px); }
.inbound-density.admin-density-dialog .measurement-table .el-table__header th { padding: var(--admin-space-1, 4px) 0; font-size: var(--admin-font-body, 13px); }
.inbound-density.admin-density-dialog .weight-heading { flex-wrap: wrap; gap: var(--admin-space-3, 8px); padding-bottom: var(--admin-space-3, 8px); }
.inbound-density.admin-density-dialog .weight-heading__actions { flex-wrap: wrap; gap: var(--admin-space-5, 12px); }
.inbound-density.admin-density-dialog .pallet-picker { width: 340px; }
.inbound-density.admin-density-dialog .weight-list { margin-top: 8px; }
.inbound-density.admin-density-dialog .weight-grid { grid-template-columns: repeat(auto-fill, minmax(420px, 1fr)); gap: 8px; }
.inbound-density.admin-density-dialog .weight-card { display: grid; grid-template-columns: 120px minmax(0, 1fr); gap: 10px; align-items: start; padding: 8px; border-radius: 2px; }
.inbound-density.admin-density-dialog .weight-list { margin-top: var(--admin-space-3, 8px); }
.inbound-density.admin-density-dialog .weight-grid { grid-template-columns: repeat(auto-fill, minmax(420px, 1fr)); gap: var(--admin-space-3, 8px); }
.inbound-density.admin-density-dialog .weight-card { display: grid; grid-template-columns: 120px minmax(0, 1fr); gap: var(--admin-space-4, 10px); align-items: start; padding: var(--admin-space-3, 8px); border-radius: 2px; }
.inbound-density.admin-density-dialog .weight-card .weight-thumb { width: 120px; border-radius: 2px; }
.inbound-density.admin-density-dialog .weight-card__body { min-width: 0; padding: 0; text-align: left; }
.inbound-density.admin-density-dialog .weight-card__value { min-height: 28px; padding-right: 32px; text-align: left; }
.inbound-density.admin-density-dialog .weight-card__value strong { font-size: 14px; }
.inbound-density.admin-density-dialog .weight-card__facts { gap: 3px; margin-top: 4px; padding-top: 4px; }
.inbound-density.admin-density-dialog .weight-card__facts div { font-size: 12px; }
.inbound-density.admin-density-dialog .weight-card__correct { width: auto; margin-top: 6px; }
.inbound-density.admin-density-dialog .weight-card__value { min-height: var(--admin-action-height, 28px); padding-right: 32px; text-align: left; }
.inbound-density.admin-density-dialog .weight-card__value strong { font-size: var(--admin-font-section, 14px); }
.inbound-density.admin-density-dialog .weight-card__facts { gap: 3px; margin-top: var(--admin-space-1, 4px); padding-top: var(--admin-space-1, 4px); }
.inbound-density.admin-density-dialog .weight-card__facts div { font-size: var(--admin-font-caption, 12px); }
.inbound-density.admin-density-dialog .weight-card__correct { width: auto; margin-top: var(--admin-space-2, 6px); }
}
@@ -305,41 +305,41 @@ defineExpose({ confirmApprove, isPending, canReview, actionLoading })
</template>
<style scoped>
.review-panel { display: grid; gap: 12px; color: var(--cc-text); }
.review-toolbar { display: flex; align-items: center; justify-content: space-between; gap: 14px; position: sticky; top: -12px; z-index: 3; flex-wrap: wrap; margin: -12px -16px 0; padding: 8px 16px; border-bottom: 1px solid var(--cc-border); background: #fff; }
.review-toolbar__status, .review-toolbar__actions { display: flex; min-width: 0; align-items: center; flex-wrap: wrap; gap: 8px; }
.review-toolbar__status > span { color: var(--cc-muted); font-size: 12px; }
.review-panel { display: grid; gap: var(--admin-space-5, 12px); color: var(--cc-text); }
.review-toolbar { display: flex; align-items: center; justify-content: space-between; gap: 14px; position: sticky; top: -12px; z-index: 3; flex-wrap: wrap; margin: -var(--admin-space-5, 12px) -var(--admin-space-6, 16px) 0; padding: var(--admin-space-3, 8px) var(--admin-space-6, 16px); border-bottom: 1px solid var(--cc-border); background: var(--admin-surface, #fff); }
.review-toolbar__status, .review-toolbar__actions { display: flex; min-width: 0; align-items: center; flex-wrap: wrap; gap: var(--admin-space-3, 8px); }
.review-toolbar__status > span { color: var(--cc-muted); font-size: var(--admin-font-caption, 12px); }
.review-toolbar__actions { justify-content: flex-end; }
.review-toolbar__actions :deep(.el-button) { margin-left: 0; }
.review-toolbar__status > .review-toolbar__context { padding-left: 8px; border-left: 1px solid var(--cc-border); font-size: 13px; line-height: 1.55; overflow-wrap: anywhere; }
.review-alert-title { display: inline-flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.review-data, .review-items { min-width: 0; padding: 0 0 12px; border-bottom: 1px solid var(--cc-border); }
.review-section-heading { display: flex; align-items: center; justify-content: space-between; gap: 16px; margin-bottom: 8px; }
.review-toolbar__status > .review-toolbar__context { padding-left: var(--admin-space-3, 8px); border-left: 1px solid var(--cc-border); font-size: var(--admin-font-body, 13px); line-height: 1.55; overflow-wrap: anywhere; }
.review-alert-title { display: inline-flex; align-items: center; gap: var(--admin-space-3, 8px); flex-wrap: wrap; }
.review-data, .review-items { min-width: 0; padding: 0 0 var(--admin-space-5, 12px); border-bottom: 1px solid var(--cc-border); }
.review-section-heading { display: flex; align-items: center; justify-content: space-between; gap: var(--admin-space-6, 16px); margin-bottom: var(--admin-space-3, 8px); }
.review-section-heading > div { min-width: 0; }
.review-section-heading h3 { margin: 0; color: var(--cc-text); font-size: 14px; line-height: 1.35; }
.review-section-heading > span { color: var(--cc-muted); font-size: 12px; white-space: nowrap; }
.review-section-heading h3 { margin: 0; color: var(--cc-text); font-size: var(--admin-font-section, 14px); line-height: 1.35; }
.review-section-heading > span { color: var(--cc-muted); font-size: var(--admin-font-caption, 12px); white-space: nowrap; }
.review-comparison { border-top: 1px solid var(--cc-border); }
.review-comparison__row { display: grid; grid-template-columns: 78px minmax(0, 1fr); align-items: center; min-width: 0; padding: 8px 0; }
.review-comparison__row { display: grid; grid-template-columns: 78px minmax(0, 1fr); align-items: center; min-width: 0; padding: var(--admin-space-3, 8px) 0; }
.review-comparison__row + .review-comparison__row { border-top: 1px dashed var(--cc-border); }
.review-comparison__row > strong { color: #344054; font-size: 12px; }
.review-metrics, .review-declaration { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 8px 16px; min-width: 0; margin: 0; }
.review-comparison__row > strong { color: #344054; font-size: var(--admin-font-caption, 12px); }
.review-metrics, .review-declaration { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: var(--admin-space-3, 8px) var(--admin-space-6, 16px); min-width: 0; margin: 0; }
.review-declaration { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.review-metrics > div, .review-declaration > div { display: flex; min-width: 0; align-items: baseline; gap: 6px; }
.review-metrics dt, .review-declaration dt { color: var(--cc-muted); font-size: 12px; line-height: 1.4; }
.review-metrics dd { margin: 0; overflow-wrap: anywhere; color: #172033; font-size: 14px; font-weight: 600; line-height: 1.35; }
.review-metrics dd small, .review-declaration dd small { color: var(--cc-muted); font-size: 12px; font-weight: 500; }
.review-metrics > div, .review-declaration > div { display: flex; min-width: 0; align-items: baseline; gap: var(--admin-space-2, 6px); }
.review-metrics dt, .review-declaration dt { color: var(--cc-muted); font-size: var(--admin-font-caption, 12px); line-height: 1.4; }
.review-metrics dd { margin: 0; overflow-wrap: anywhere; color: #172033; font-size: var(--admin-font-section, 14px); font-weight: 600; line-height: 1.35; }
.review-metrics dd small, .review-declaration dd small { color: var(--cc-muted); font-size: var(--admin-font-caption, 12px); font-weight: 500; }
.review-metrics .review-metric--primary dd { color: #067647; }
.review-declaration dd { margin: 0; overflow-wrap: anywhere; color: var(--cc-text); font-size: 13px; font-weight: 650; line-height: 1.4; }
.review-declaration dd { margin: 0; overflow-wrap: anywhere; color: var(--cc-text); font-size: var(--admin-font-body, 13px); font-weight: 650; line-height: 1.4; }
.review-table-scroll { width: 100%; min-width: 0; overflow: hidden; }
.review-table { width: 100%; font-size: 12px; }
.review-table :deep(.el-table__header th) { padding: 8px 0; background: #f6f8fa; color: #475467; font-size: 12px; font-weight: 700; }
.review-table { width: 100%; font-size: var(--admin-font-caption, 12px); }
.review-table :deep(.el-table__header th) { padding: var(--admin-space-3, 8px) 0; background: #f6f8fa; color: #475467; font-size: var(--admin-font-caption, 12px); font-weight: 700; }
.review-table :deep(.el-table__header .cell) { white-space: nowrap; }
.review-table :deep(.el-table__cell) { padding: 7px 0; }
.review-table :deep(.review-time-cell .cell) { white-space: nowrap; }
.review-table :deep(.measurement-evidence-thumb) { min-width: 72px; border-radius: 4px; }
.review-table :deep(.measurement-evidence-thumb) { min-width: 72px; border-radius: var(--admin-radius, 4px); }
.review-image-preview { display: grid; min-height: 240px; place-items: center; }
.review-image-preview img { display: block; width: auto; max-width: 100%; max-height: 70vh; object-fit: contain; }
.review-image-preview__error { display: grid; justify-items: center; gap: 8px; color: var(--el-color-danger); }
.review-image-preview__error { display: grid; justify-items: center; gap: var(--admin-space-3, 8px); color: var(--el-color-danger); }
@media (max-width: 720px) {
.review-toolbar { align-items: stretch; flex-direction: column; margin: -14px -14px 0; padding: 12px 14px; }
.review-toolbar__status > .review-toolbar__context { width: 100%; padding-top: 8px; padding-left: 0; border-top: 1px solid var(--cc-border); border-left: 0; }
@@ -99,7 +99,7 @@ describe('WeightOcrPanel contract', () => {
})
it('keeps each weight summary label and value on one compact line', () => {
expect(source).toContain('.weight-summary { display: flex; align-items: baseline; gap: 6px; white-space: nowrap; }')
expect(source).toContain('.weight-summary { display: flex; align-items: baseline; gap: var(--admin-space-2, 6px); white-space: nowrap; }')
expect(source).not.toContain('.weight-summary { display: grid;')
})
})
@@ -684,42 +684,42 @@ defineExpose({ reload: loadWorkspace })
<style scoped>
.weight-workspace { min-width: 0; color: var(--el-text-color-primary); }
.weight-heading { display: flex; align-items: center; justify-content: space-between; gap: 20px; padding-bottom: 10px; border-bottom: 1px solid var(--cc-border); }
.weight-heading h3 { display: flex; align-items: center; gap: 8px; margin: 0; font-size: 15px; }
.weight-heading { display: flex; align-items: center; justify-content: space-between; gap: 20px; padding-bottom: var(--admin-space-4, 10px); border-bottom: 1px solid var(--cc-border); }
.weight-heading h3 { display: flex; align-items: center; gap: var(--admin-space-3, 8px); margin: 0; font-size: 15px; }
.weight-heading h3 svg { color: var(--cc-primary); }
.weight-heading__actions { display: flex; flex: 0 0 auto; align-items: center; flex-wrap: wrap; justify-content: flex-end; gap: 16px; }
.weight-heading__actions { display: flex; flex: 0 0 auto; align-items: center; flex-wrap: wrap; justify-content: flex-end; gap: var(--admin-space-6, 16px); }
.pallet-picker { width: clamp(360px, 36vw, 520px); min-width: 0; }
.pallet-picker :deep(.el-select) { width: 100%; }
.weight-summary { display: flex; align-items: baseline; gap: 6px; white-space: nowrap; }
.weight-summary { display: flex; align-items: baseline; gap: var(--admin-space-2, 6px); white-space: nowrap; }
.weight-summary-group { display: flex; align-items: center; flex-wrap: wrap; gap: 20px; }
.weight-summary span { color: var(--cc-muted); font-size: 12px; }
.weight-summary strong { color: #067647; font-size: 16px; }
.weight-summary span { color: var(--cc-muted); font-size: var(--admin-font-caption, 12px); }
.weight-summary strong { color: #067647; font-size: var(--admin-font-title, 16px); }
.visually-hidden-input { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; }
.weight-action-error { margin-bottom: 14px; }
.weight-list { min-height: 0; margin-top: 14px; }
.weight-empty { display: flex; min-height: 0; align-items: center; gap: 10px; padding: 4px 0 0; color: var(--cc-muted); }
.weight-empty strong { color: #344054; font-size: 14px; }
.weight-empty { display: flex; min-height: 0; align-items: center; gap: var(--admin-space-4, 10px); padding: var(--admin-space-1, 4px) 0 0; color: var(--cc-muted); }
.weight-empty strong { color: #344054; font-size: var(--admin-font-section, 14px); }
.weight-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); gap: 14px; }
.weight-card { position: relative; min-width: 0; overflow: hidden; border: 1px solid var(--cc-border); border-radius: 6px; background: #fff; }
.weight-card__body { padding: 12px; text-align: center; }
.weight-card { position: relative; min-width: 0; overflow: hidden; border: 1px solid var(--cc-border); border-radius: 6px; background: var(--admin-surface, #fff); }
.weight-card__body { padding: var(--admin-space-5, 12px); text-align: center; }
.weight-delete { position: absolute; z-index: 2; top: 8px; right: 8px; width: 30px; height: 30px; padding: 0; border: 1px solid rgb(255 255 255 / 82%); background: rgb(255 255 255 / 92%); box-shadow: 0 1px 3px rgb(16 24 40 / 16%); }
.weight-card__value { width: 100%; padding: 0; border: 0; background: transparent; color: inherit; cursor: pointer; text-align: center; }
.weight-card__value strong { color: #101828; font-size: 17px; }
.weight-card__value:focus-visible { outline: 2px solid var(--cc-primary); outline-offset: 4px; }
.weight-card__facts { display: grid; gap: 6px; margin: 10px 0 0; padding-top: 10px; border-top: 1px solid var(--cc-border); }
.weight-card__facts div { display: flex; justify-content: space-between; gap: 8px; font-size: 11px; line-height: 1.4; }
.weight-card__facts { display: grid; gap: var(--admin-space-2, 6px); margin: var(--admin-space-4, 10px) 0 0; padding-top: var(--admin-space-4, 10px); border-top: 1px solid var(--cc-border); }
.weight-card__facts div { display: flex; justify-content: space-between; gap: var(--admin-space-3, 8px); font-size: var(--admin-font-caption, 11px); line-height: 1.4; }
.weight-card__facts dt { color: var(--cc-muted); }
.weight-card__facts dd { min-width: 0; margin: 0; color: #344054; overflow-wrap: anywhere; text-align: right; }
.weight-card__correct { width: 100%; margin: 12px 0 0; }
.weight-card__correct { width: 100%; margin: var(--admin-space-5, 12px) 0 0; }
.weight-preview { display: block; width: 100%; max-height: min(50vh, 460px); object-fit: contain; border-radius: 6px; background: #f2f4f7; }
.weight-key-facts { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px 12px; margin-top: 8px; padding: 8px 0; border-block: 1px solid var(--cc-border); }
.weight-key-facts div { display: grid; min-width: 0; gap: 4px; }
.weight-key-facts span { color: var(--cc-muted); font-size: 12px; }
.weight-key-facts strong { color: #101828; font-size: 14px; }
.preview-weight { display: grid; grid-template-columns: minmax(0, 1fr) 120px; align-items: end; gap: 12px; margin-top: 14px; }
.preview-weight label { display: grid; gap: 6px; color: #475467; font-size: 12px; }
.weight-key-facts { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--admin-space-3, 8px) var(--admin-space-5, 12px); margin-top: var(--admin-space-3, 8px); padding: var(--admin-space-3, 8px) 0; border-block: 1px solid var(--cc-border); }
.weight-key-facts div { display: grid; min-width: 0; gap: var(--admin-space-1, 4px); }
.weight-key-facts span { color: var(--cc-muted); font-size: var(--admin-font-caption, 12px); }
.weight-key-facts strong { color: #101828; font-size: var(--admin-font-section, 14px); }
.preview-weight { display: grid; grid-template-columns: minmax(0, 1fr) 120px; align-items: end; gap: var(--admin-space-5, 12px); margin-top: 14px; }
.preview-weight label { display: grid; gap: var(--admin-space-2, 6px); color: #475467; font-size: var(--admin-font-caption, 12px); }
.preview-weight :deep(.el-input-number), .preview-weight :deep(.el-segmented) { width: 100%; }
.correction-reason { display: grid; gap: 6px; margin-top: 14px; color: #475467; font-size: 12px; }
.correction-reason { display: grid; gap: var(--admin-space-2, 6px); margin-top: 14px; color: #475467; font-size: var(--admin-font-caption, 12px); }
@media (max-width: 720px) {
.weight-heading { flex-direction: column; }
@@ -204,7 +204,7 @@ function formatNumber(value?: number | null, digits = 3) {
>
<template #toolbar-start><div class="detail-section__heading"><div><h2>装柜货物明细 <span>{{ detail.items.length }} 条</span></h2><p>仓库备注将随装柜完成资料下发海外仓</p></div></div></template>
<template #actions="{ row }">
<el-button size="small" type="primary" plain :aria-label="`编辑 ${row.inboundCode || row.orderNo} 的备注`" @click="emit('editRemarks', row as LoadingItem)"><Pencil :size="14" />编辑备注</el-button>
<el-button :class="{ 'density-action': mode === 'preload' }" size="small" :type="mode === 'preload' ? undefined : 'primary'" :plain="mode !== 'preload'" :aria-label="`编辑 ${row.inboundCode || row.orderNo} 的备注`" @click="emit('editRemarks', row as LoadingItem)"><Pencil v-if="mode !== 'preload'" :size="14" />编辑备注</el-button>
</template>
<template #unitType="{ row }"><el-tag size="small" :type="row.unitType === 'SEALED_PALLET' ? 'success' : 'info'">{{ row.unitType === 'SEALED_PALLET' ? '封托托盘' : '箱件' }}</el-tag><small v-if="row.indivisible">整托不可拆</small></template>
<template #grossWeight="{ row }">{{ formatNumber(row.grossWeightKg) }}</template>
@@ -236,58 +236,58 @@ function formatNumber(value?: number | null, digits = 3) {
</template>
<style scoped>
.loading-detail-content { display: grid; min-width: 0; gap: 16px; padding: 16px; border: 1px solid var(--cc-border); border-radius: 8px; background: #fff; }
.loading-detail-content { display: grid; min-width: 0; gap: var(--admin-space-6, 16px); padding: var(--admin-space-6, 16px); border: 1px solid var(--cc-border); border-radius: 8px; background: var(--admin-surface, #fff); }
.detail-layout { display: grid; min-width: 0; }
.detail-overview { min-width: 0; }
.detail-page-header { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; align-items: center; gap: 16px; padding: 8px 0 12px; }
.detail-page-header { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; align-items: center; gap: var(--admin-space-6, 16px); padding: var(--admin-space-3, 8px) 0 var(--admin-space-5, 12px); }
.detail-page-header--embedded { grid-template-columns: minmax(0, 1fr) auto; padding-top: 0; }
.detail-page-header__back { margin-right: 2px; }
.detail-page-header__identity { min-width: 0; }
.detail-page-header__identity h1 { margin: 0; color: var(--cc-text); font-size: 18px; line-height: 1.35; }
.detail-page-header__identity p { margin: 3px 0 0; color: var(--cc-muted); font-size: 12px; }
.detail-page-header__actions { display: flex; align-items: center; justify-content: flex-end; gap: 8px; }
.detail-page-header__identity p { margin: 3px 0 0; color: var(--cc-muted); font-size: var(--admin-font-caption, 12px); }
.detail-page-header__actions { display: flex; align-items: center; justify-content: flex-end; gap: var(--admin-space-3, 8px); }
.detail-page-header__actions :deep(.el-button + .el-button) { margin-left: 0; }
.detail-summary { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 6px 28px; padding: 10px 0 2px; border-top: 1px solid var(--cc-border); }
.detail-summary > div { display: flex; min-width: 0; min-height: 26px; align-items: center; gap: 6px; }
.detail-summary > div > span:first-child { flex: 0 0 auto; color: var(--cc-muted); font-size: 12px; white-space: nowrap; }
.detail-summary { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--admin-space-2, 6px) 28px; padding: var(--admin-space-4, 10px) 0 2px; border-top: 1px solid var(--cc-border); }
.detail-summary > div { display: flex; min-width: 0; min-height: 26px; align-items: center; gap: var(--admin-space-2, 6px); }
.detail-summary > div > span:first-child { flex: 0 0 auto; color: var(--cc-muted); font-size: var(--admin-font-caption, 12px); white-space: nowrap; }
.detail-summary > div > span:first-child::after { content: ":"; }
.detail-summary strong { min-width: 0; color: var(--cc-text); font-size: 13px; overflow-wrap: anywhere; }
.detail-summary strong { min-width: 0; color: var(--cc-text); font-size: var(--admin-font-body, 13px); overflow-wrap: anywhere; }
.detail-summary :deep(.status-badge) { width: fit-content; }
.detail-summary small { color: var(--cc-muted); font-size: 11px; }
.detail-summary small { color: var(--cc-muted); font-size: var(--admin-font-caption, 11px); }
.detail-summary__wide { grid-column: auto; }
.detail-layout :deep(.detail-page-layout__content) { display: grid; min-width: 0; gap: 20px; }
.readiness-note { display: flex; min-width: 0; align-items: center; padding: 2px 4px; color: var(--cc-text); font-size: 12px; }
.readiness-note { display: flex; min-width: 0; align-items: center; padding: 2px var(--admin-space-1, 4px); color: var(--cc-text); font-size: var(--admin-font-caption, 12px); }
.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: 16px 0 0; border-top: 1px solid var(--cc-border); }
.detail-section, .history-section { min-width: 0; overflow: hidden; padding: var(--admin-space-6, 16px) 0 0; border-top: 1px solid var(--cc-border); }
.detail-section__heading h2 { margin: 0; font-size: 15px; }
.detail-section__heading h2 span { margin-left: 6px; color: var(--cc-muted); font-size: 12px; font-weight: 400; }
.detail-section__heading p { margin: 2px 0 0; color: var(--cc-muted); font-size: 11px; line-height: 1.35; }
.detail-section :deep(.data-table__toolbar) { min-height: 52px; padding: 7px 12px; }
.detail-section__heading h2 span { margin-left: var(--admin-space-2, 6px); color: var(--cc-muted); font-size: var(--admin-font-caption, 12px); font-weight: 400; }
.detail-section__heading p { margin: 2px 0 0; color: var(--cc-muted); font-size: var(--admin-font-caption, 11px); line-height: 1.35; }
.detail-section :deep(.data-table__toolbar) { min-height: 52px; padding: 7px var(--admin-space-5, 12px); }
.detail-section :deep(.el-table) { width: 100%; }
.detail-section :deep(.el-table th.el-table__cell) { padding: 7px 0; }
.detail-section :deep(.el-table td.el-table__cell) { padding: 6px 0; }
.detail-section :deep(.el-table td.el-table__cell) { padding: var(--admin-space-2, 6px) 0; }
.detail-section :deep(.identifier-column .cell) { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.detail-section small { display: block; margin-top: 3px; color: var(--cc-muted); }
.remark-cell { min-width: 0; line-height: 1.45; overflow-wrap: anywhere; }
.history-section header { display: flex; align-items: baseline; gap: 10px; margin-bottom: 18px; }
.history-section h2 { margin: 0; font-size: 16px; }
.history-section header span { color: var(--cc-muted); font-size: 12px; }
.preload-density { gap: 12px; padding: 12px; border-radius: 4px; }
.preload-density .detail-page-header { gap: 12px; padding: 0 0 8px; }
.preload-density .detail-page-header__identity h1 { font-size: 16px; overflow-wrap: anywhere; }
.history-section header { display: flex; align-items: baseline; gap: var(--admin-space-4, 10px); margin-bottom: 18px; }
.history-section h2 { margin: 0; font-size: var(--admin-font-title, 16px); }
.history-section header span { color: var(--cc-muted); font-size: var(--admin-font-caption, 12px); }
.preload-density { gap: var(--admin-space-5, 12px); padding: var(--admin-space-5, 12px); border-radius: var(--admin-radius, 4px); }
.preload-density .detail-page-header { gap: var(--admin-space-5, 12px); padding: 0 0 var(--admin-space-3, 8px); }
.preload-density .detail-page-header__identity h1 { font-size: var(--admin-font-title, 16px); overflow-wrap: anywhere; }
.preload-density .detail-page-header__actions { flex-wrap: wrap; }
.preload-density .detail-summary { gap: 0; padding: 0; border-left: 1px solid var(--cc-border); }
.preload-density .detail-summary > div { gap: 0; align-items: stretch; min-height: 36px; border-bottom: 1px solid var(--cc-border); border-right: 1px solid var(--cc-border); }
.preload-density .detail-summary > div > span:first-child { display: flex; align-items: center; flex: 0 0 96px; padding: 6px 8px; background: #f8fafc; }
.preload-density .detail-summary > div { gap: 0; align-items: stretch; min-height: var(--admin-row-height, 36px); border-bottom: 1px solid var(--cc-border); border-right: 1px solid var(--cc-border); }
.preload-density .detail-summary > div > span:first-child { display: flex; align-items: center; flex: 0 0 96px; padding: var(--admin-space-2, 6px) var(--admin-space-3, 8px); background: var(--admin-label-bg, #f8fafc); }
.preload-density .detail-summary > div > span:first-child::after { content: none; }
.preload-density .detail-summary strong, .preload-density .detail-summary :deep(.status-badge) { align-self: center; margin: 6px 8px; font-weight: 400; }
.preload-density .detail-summary strong, .preload-density .detail-summary :deep(.status-badge) { align-self: center; margin: var(--admin-space-2, 6px) var(--admin-space-3, 8px); font-weight: 400; }
.preload-density .detail-summary__wide { grid-column: span 2; }
.preload-density .detail-layout :deep(.detail-page-layout__content) { gap: 12px; }
.preload-density .detail-section, .preload-density .history-section { padding-top: 8px; }
.preload-density .detail-section__heading h2, .preload-density .history-section h2 { font-size: 14px; }
.preload-density .history-section header { margin-bottom: 8px; }
.preload-density .detail-section__heading p, .preload-density small { font-size: 12px; }
.preload-density .detail-layout :deep(.detail-page-layout__content) { gap: var(--admin-space-5, 12px); }
.preload-density .detail-section, .preload-density .history-section { padding-top: var(--admin-space-3, 8px); }
.preload-density .detail-section__heading h2, .preload-density .history-section h2 { font-size: var(--admin-font-section, 14px); }
.preload-density .history-section header { margin-bottom: var(--admin-space-3, 8px); }
.preload-density .detail-section__heading p, .preload-density small { font-size: var(--admin-font-caption, 12px); }
.preload-density .readiness-note { align-items: baseline; }
.preload-density .readiness-note span { white-space: normal; overflow: visible; overflow-wrap: anywhere; }
@media (max-width: 720px) { .preload-density .detail-summary { grid-template-columns: 1fr; } .preload-density .detail-summary__wide { grid-column: auto; } }
@@ -136,7 +136,8 @@ describe('LoadingListsView', () => {
})
it('uses bordered list actions with a stable 320px layout', () => {
expect(source).toContain('size="small" type="primary" plain')
expect(source).toContain(":class=\"{ 'density-action': isPreload }\"")
expect(source).toContain(':type="isPreload ? undefined : \'primary\'" :plain="!isPreload"')
expect(source).not.toContain('<el-button v-if="canView" link')
expect(source).toContain("key: 'actions', label: '操作', fixed: 'left', width: 110")
expect(source).toContain('flex-wrap: wrap')
@@ -201,7 +202,7 @@ describe('LoadingListsView', () => {
expect(detailSource).toContain('min-height: 26px')
expect(detailSource).toContain('> div > span:first-child::after { content: ":"; }')
expect(detailSource).not.toContain('> div > span::after { content: ":"; }')
expect(detailSource).toContain('background: #fff')
expect(detailSource).toContain('background: var(--admin-surface, #fff)')
expect(detailSource).toContain('overflow-wrap: anywhere')
expect(detailSource).toContain("className: 'identifier-column'")
expect(detailSource).toContain('showOverflowTooltip: true')
@@ -217,7 +218,7 @@ describe('LoadingListsView', () => {
expect(detailSource).toContain('<section class="detail-section">')
expect(detailSource).not.toContain('class="detail-section surface-panel"')
expect(detailSource).not.toContain('class="history-section surface-panel"')
expect(detailSource).toContain('padding: 16px 0 0')
expect(detailSource).toContain('padding: var(--admin-space-6, 16px) 0 0')
expect(detailSource).toContain("fixed: 'left', width: 132")
})
@@ -219,8 +219,8 @@ onMounted(() => {
<ContainerSimulationCreate :dense="isPreload" v-if="canCreate" success-message="模拟柜及预装清单已创建" @created="onSimulationCreated" />
</template>
<template #actions="{ row }">
<div class="row-actions">
<el-button v-if="canView" size="small" type="primary" plain @click="openDetail(row)"><Eye :size="15" />查看</el-button>
<div class="row-actions" :class="{ 'density-actions': isPreload }">
<el-button v-if="canView" :class="{ 'density-action': isPreload }" size="small" :type="isPreload ? undefined : 'primary'" :plain="!isPreload" @click="openDetail(row)"><Eye v-if="!isPreload" :size="15" />查看</el-button>
</div>
</template>
<template #customers="{ row }"><LineClampText :content="(row as PreloadSummary).customers.map((item) => item.customerName || '客户信息缺失').join('、')" /></template>
@@ -239,9 +239,9 @@ onMounted(() => {
</template>
<style scoped>
.loading-lists-page { display: grid; min-width: 0; gap: 16px; }
.table-panel { min-width: 0; overflow: hidden; border: 1px solid var(--cc-border); background: #fff; }
.row-actions { display: flex; align-items: center; flex-wrap: wrap; gap: 6px; }
.loading-lists-page { display: grid; min-width: 0; gap: var(--admin-space-6, 16px); }
.table-panel { min-width: 0; overflow: hidden; border: 1px solid var(--cc-border); background: var(--admin-surface, #fff); }
.row-actions { display: flex; align-items: center; flex-wrap: wrap; gap: var(--admin-space-2, 6px); }
.row-actions :deep(.el-button) { margin-left: 0; }
@media (max-width: 720px) { .row-actions { gap: 4px; } }
</style>
@@ -60,7 +60,7 @@ async function print() {
</template>
<style scoped>
.print-scope { display:flex; margin:18px 0 12px; }
.print-options { display:flex; align-items:center; gap:12px; margin-bottom:16px; }
.print-scope { display:flex; margin:18px 0 var(--admin-space-5, 12px); }
.print-options { display:flex; align-items:center; gap:var(--admin-space-5, 12px); margin-bottom:var(--admin-space-6, 16px); }
.print-options .el-select { width:240px; }
</style>
@@ -10,7 +10,7 @@ describe('PreloadPlanDetailView prototype contract', () => {
expect(source).toContain('<AdminPage>')
expect(source).toContain('@back="goBack"')
expect(source).toContain("router.push({ name: 'loading-plans' })")
expect(source).toContain('@media (min-width: 1920px) { .preload-detail-page { width: 100%; max-width: none; gap: 12px; } }')
expect(source).toContain('@media (min-width: 1920px) { .preload-detail-page { width: 100%; max-width: none; gap: var(--admin-space-5, 12px); } }')
expect(source).not.toContain('AppDrawer')
})
@@ -290,13 +290,13 @@ watch([planId, warehouseId], () => void loadDetail())
</template>
<style scoped>
.preload-detail-page { display: grid; min-width: 0; width: 100%; gap: 12px; }
.transport-form { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 16px; }
.preload-detail-page { display: grid; min-width: 0; width: 100%; gap: var(--admin-space-5, 12px); }
.transport-form { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 var(--admin-space-6, 16px); }
.transport-form :deep(.el-form-item:last-child) { grid-column: 1 / -1; }
.confirm-content { display: grid; gap: 12px; }
.confirm-content { display: grid; gap: var(--admin-space-5, 12px); }
.remarks-form { margin-top: 14px; }
.remarks-form small { display: block; margin-top: 4px; color: var(--cc-muted); }
.remarks-form small { display: block; margin-top: var(--admin-space-1, 4px); color: var(--cc-muted); }
:global(.loading-transport-dialog.el-dialog) { max-height: 90vh; overflow: hidden; }
@media (min-width: 1920px) { .preload-detail-page { width: 100%; max-width: none; gap: 12px; } }
@media (min-width: 1920px) { .preload-detail-page { width: 100%; max-width: none; gap: var(--admin-space-5, 12px); } }
@media (max-width: 720px) { .transport-form { grid-template-columns: 1fr; } .transport-form :deep(.el-form-item:last-child) { grid-column: auto; } }
</style>
@@ -6,6 +6,8 @@ import { unifiedOrderStatusOptions } from '../api/unifiedOrders'
const source = readFileSync(resolve(process.cwd(), 'src/modules/unified-orders/pages/UnifiedOrdersView.vue'), 'utf8').replace(/\r\n/g, '\n')
const routeSource = readFileSync(resolve(process.cwd(), 'src/modules/unified-orders/routes.ts'), 'utf8').replace(/\r\n/g, '\n')
const densitySource = readFileSync(resolve(process.cwd(), 'src/styles/adminDensity.css'), 'utf8')
describe('UnifiedOrdersView contract', () => {
it('exposes desktop pallet printing only for one-click dropship orders', () => {
expect(source).toContain("detail.value?.summary.fulfillmentType === 'ONE_CLICK_DROPSHIP'")
@@ -637,8 +639,9 @@ describe('UnifiedOrdersView contract', () => {
})
it('keeps detail tabs compact on one horizontally scrollable row', () => {
expect(source).toContain('.detail-tabs { display: flex; overflow-x: auto; overflow-y: hidden;')
expect(source).toContain('min-width: max-content; min-height: 38px; flex: 0 0 auto;')
expect(source).toContain('class="detail-tabs density-tabs"')
expect(densitySource).toContain('overflow-x: auto; overflow-y: hidden;')
expect(densitySource).toContain('min-height: var(--admin-tab-height)')
expect(source).not.toContain('grid-template-columns: repeat(6, minmax(124px, 1fr))')
expect(source).not.toContain('.detail-tabs { grid-template-columns: repeat(6, 132px); }')
})
@@ -649,9 +652,9 @@ describe('UnifiedOrdersView contract', () => {
expect(source).toContain('<el-table :data="outerMeasurementRows" border stripe')
expect(source).toContain('<el-table :data="actualMeasurementRows" row-key="id" border stripe')
expect(source).toContain('<el-table :data="customerMeasurementRows" border stripe empty-text="暂无客户下单数据"')
expect(source).toContain('--el-table-border-color: #e5e7eb')
expect(source).toContain('--el-table-row-hover-bg-color: #f3f7fb')
expect(source).toContain('.el-table--striped .el-table__body tr.el-table__row--striped')
expect(densitySource).toContain('--el-table-border-color: var(--admin-table-border)')
expect(densitySource).toContain('--el-table-row-hover-bg-color: var(--admin-row-hover)')
expect(densitySource).toContain('.el-table__body tr.el-table__row--striped')
expect(source).toContain("label: '真实集装箱号'")
expect(source).toContain("label: '内部柜次号'")
expect(source).toContain('<dt>真实集装箱号</dt>')
@@ -660,7 +663,7 @@ describe('UnifiedOrdersView contract', () => {
it('uses outlined semantic buttons for row business actions while keeping detail navigation explicit', () => {
const rowActions = source.slice(source.indexOf('<template #actions'), source.indexOf('<template #inboundCode'))
expect(rowActions).toContain('order-action-button')
expect(rowActions).toContain('density-action')
expect(rowActions).not.toContain('v-for="action in directUnifiedOrderActions(row as UnifiedOrderResource)" :key="action" link')
expect(rowActions).toContain('@click="openDetail(row as UnifiedOrderResource)">详情</el-button>')
expect(source).toContain('<el-button link class="back-button"')
@@ -2187,7 +2187,7 @@ onBeforeUnmount(() => {
<template>
<AdminPage>
<section class="business-page unified-orders-page">
<section class="business-page unified-orders-page admin-density">
<AppDialog class="admin-density-dialog" v-model="feeConfirmOpen" title="编辑计费数据" confirm-text="确认并发送" :confirm-loading="confirmFeeSending || feeConfirmReferenceLoading" width="min(920px, 94vw)" :teleported="false" @confirm="executeConfirmAndSendFee">
<el-alert class="fee-confirm-price-alert" title="价格表单价仅作为参考;本次对客单价可人工修改,客户将一次确认计费量、单价和费用合计。" type="info" :closable="false" show-icon />
<el-form label-position="top" class="fee-confirm-form">
@@ -2245,13 +2245,13 @@ onBeforeUnmount(() => {
<div><dt>最近更新</dt><dd>{{ formatDate(detail.summary.updatedAt) }}</dd></div>
</dl>
<div class="detail-tabs" role="tablist" aria-label="订单详情模块">
<button v-for="tab in detailTabs" :id="`order-detail-tab-${tab.key}`" :key="tab.key" type="button" role="tab" :class="{ active: activeDetailTab === tab.key }" :aria-selected="activeDetailTab === tab.key" :aria-controls="`order-detail-panel-${tab.key}`" :tabindex="activeDetailTab === tab.key ? 0 : -1" @click="selectDetailTab(tab.key)"><span class="detail-tab-icon" aria-hidden="true">{{ tab.icon }}</span><span>{{ tab.label }}</span></button>
<div class="detail-tabs density-tabs" role="tablist" aria-label="订单详情模块">
<button v-for="tab in detailTabs" :id="`order-detail-tab-${tab.key}`" :key="tab.key" type="button" role="tab" :class="{ active: activeDetailTab === tab.key }" :aria-selected="activeDetailTab === tab.key" :aria-controls="`order-detail-panel-${tab.key}`" :tabindex="activeDetailTab === tab.key ? 0 : -1" @click="selectDetailTab(tab.key)"><span class="density-tab-icon" aria-hidden="true">{{ tab.icon }}</span><span>{{ tab.label }}</span></button>
</div>
<section :id="`order-detail-panel-${activeDetailTab}`" class="detail-panel" role="tabpanel" :aria-labelledby="`order-detail-tab-${activeDetailTab}`" tabindex="0">
<template v-if="activeDetailTab === 'basic'">
<dl class="order-info-grid">
<dl class="order-info-grid density-fields">
<div><dt>公司</dt><dd>{{ customerName(detail.summary) }}</dd></div>
<div><dt>运输方式</dt><dd>{{ firstLegTransportModeLabel(detail.order.order?.transportMode) }}</dd></div>
<div><dt>当前状态</dt><dd><StatusBadge :status="statusLabel(detail.summary.mainStatus)" /></dd></div>
@@ -2603,11 +2603,11 @@ onBeforeUnmount(() => {
<el-button v-if="can('platform:firstleg:smart-loading:list')" type="primary" @click="smartLoadingOpen = true"><PackageOpen :size="16" />智能装柜</el-button>
</template>
<template #actions="{ row }">
<div class="row-actions">
<el-button class="order-action-button" size="small" @click="openDetail(row as UnifiedOrderResource)">详情</el-button>
<el-button v-for="action in directUnifiedOrderActions(row as UnifiedOrderResource)" :key="action" :class="['order-action-button', `order-action-${action}`]" size="small" :loading="action === 'approve' && approvalLoadingId === String((row as UnifiedOrderResource).orderId)" @click="runMoreAction(action, row as UnifiedOrderResource)">{{ unifiedOrderActionLabel(action) }}</el-button>
<div class="row-actions density-actions">
<el-button class="density-action" size="small" @click="openDetail(row as UnifiedOrderResource)">详情</el-button>
<el-button v-for="action in directUnifiedOrderActions(row as UnifiedOrderResource)" :key="action" class="density-action" :data-tone="action === 'approve' ? 'warning' : action === 'measurement' ? 'purple' : 'neutral'" size="small" :loading="action === 'approve' && approvalLoadingId === String((row as UnifiedOrderResource).orderId)" @click="runMoreAction(action, row as UnifiedOrderResource)">{{ unifiedOrderActionLabel(action) }}</el-button>
<el-dropdown v-if="moreUnifiedOrderActions(row as UnifiedOrderResource).length" trigger="click" @command="runMoreAction($event, row as UnifiedOrderResource)">
<el-button class="order-action-button" size="small" :aria-label="moreUnifiedOrderActionsLabel(row as UnifiedOrderResource)" :title="moreUnifiedOrderActionsLabel(row as UnifiedOrderResource)"><MoreHorizontal :size="17" aria-hidden="true" /></el-button>
<el-button class="density-action" size="small" :aria-label="moreUnifiedOrderActionsLabel(row as UnifiedOrderResource)" :title="moreUnifiedOrderActionsLabel(row as UnifiedOrderResource)"><MoreHorizontal :size="17" aria-hidden="true" /></el-button>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item v-for="action in moreUnifiedOrderActions(row as UnifiedOrderResource)" :key="action" :command="action">{{ unifiedOrderActionLabel(action) }}</el-dropdown-item>
@@ -2800,20 +2800,21 @@ onBeforeUnmount(() => {
</template>
<style scoped>
.detail-overview > div { display: flex; min-width: 0; align-items: baseline; gap: 6px; }
.detail-overview dt { flex: 0 0 auto; color: var(--cc-muted); font-size: 12px; }
.detail-overview dd { min-width: 0; margin: 0; color: var(--cc-text); font-size: 13px; overflow-wrap: anywhere; font-variant-numeric: tabular-nums; }
.detail-overview small { color: var(--cc-muted); font-size: 12px; }
.detail-tabs button:focus-visible { outline: 2px solid var(--cc-primary); outline-offset: -2px; }
.panel-title > div:first-child { display: flex; align-items: baseline; flex-wrap: wrap; gap: 4px 12px; }
.compact-empty { margin: 0; padding: 14px 10px; color: var(--cc-muted); text-align: center; font-size: 13px; }
.detail-tabs { border: 1px solid var(--cc-border); border-block: 0; }
.detail-overview > div { display: flex; min-width: 0; align-items: baseline; gap: var(--admin-space-2, 6px); }
.detail-overview dt { flex: 0 0 auto; color: var(--cc-muted); font-size: var(--admin-font-caption, 12px); }
.detail-overview dd { min-width: 0; margin: 0; color: var(--cc-text); font-size: var(--admin-font-body, 13px); overflow-wrap: anywhere; font-variant-numeric: tabular-nums; }
.detail-overview small { color: var(--cc-muted); font-size: var(--admin-font-caption, 12px); }
.panel-title > div:first-child { display: flex; align-items: baseline; flex-wrap: wrap; gap: var(--admin-space-1, 4px) var(--admin-space-5, 12px); }
.compact-empty { margin: 0; padding: 14px var(--admin-space-4, 10px); color: var(--cc-muted); text-align: center; font-size: var(--admin-font-body, 13px); }
.approval-facts { margin: 0; border-top: 1px solid var(--cc-border); }
.approval-facts > div { display: grid; grid-template-columns: 88px minmax(0, 1fr); gap: 12px; padding: 8px 0; border-bottom: 1px solid var(--cc-border); }
.approval-facts dt { color: var(--cc-muted); font-size: 12px; }
.approval-facts dd { min-width: 0; margin: 0; overflow-wrap: anywhere; font-size: 13px; }
.approval-hint { margin: 12px 0 0; color: #475467; font-size: 13px; }
.detail-panel :deep(.el-table) { font-size: 13px; --el-table-header-bg-color: #f8fafc; }
.detail-panel :deep(.el-table__cell) { height: 36px; padding: 4px 0; }
.approval-facts > div { display: grid; grid-template-columns: 88px minmax(0, 1fr); gap: var(--admin-space-5, 12px); padding: var(--admin-space-3, 8px) 0; border-bottom: 1px solid var(--cc-border); }
.approval-facts dt { color: var(--cc-muted); font-size: var(--admin-font-caption, 12px); }
.approval-facts dd { min-width: 0; margin: 0; overflow-wrap: anywhere; font-size: var(--admin-font-body, 13px); }
.approval-hint { margin: var(--admin-space-5, 12px) 0 0; color: #475467; font-size: var(--admin-font-body, 13px); }
.detail-panel :deep(.el-table) { font-size: var(--admin-font-body, 13px); --el-table-header-bg-color: var(--admin-label-bg, #f8fafc); }
.detail-panel :deep(.el-table__cell) { height: var(--admin-row-height, 36px); padding: var(--admin-space-1, 4px) 0; }
.detail-panel :deep(.el-table .cell) { line-height: 22px; overflow-wrap: anywhere; }
.detail-panel :deep(.el-table__empty-block) { min-height: 44px; }
.detail-panel :deep(.el-table__empty-text) { line-height: 44px; }
@@ -2821,36 +2822,36 @@ onBeforeUnmount(() => {
.measurement-section-title:has([aria-expanded="false"]) { border-bottom: 1px solid var(--cc-border); }
.fulfillment-facts + .fulfillment-facts { border-top: 0; }
@media (min-width: 961px) {
.table-card :deep(.data-table__toolbar.is-compact) { min-height: 44px; padding: 6px 12px; }
.table-card :deep(.data-table__pagination) { min-height: 44px; padding: 6px 12px; }
.table-card :deep(.data-table__toolbar.is-compact) { min-height: 44px; padding: var(--admin-space-2, 6px) var(--admin-space-5, 12px); }
.table-card :deep(.data-table__pagination) { min-height: 44px; padding: var(--admin-space-2, 6px) var(--admin-space-5, 12px); }
}
.unified-orders-page { min-width: 0; gap: 12px; --cc-list-table-font-size: 13px; --cc-list-query-font-size: 13px; --cc-list-action-font-size: 13px; }
.page-intro { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; margin-bottom: 16px; }
.unified-orders-page { min-width: 0; gap: var(--admin-space-5, 12px); --cc-list-table-font-size: 13px; --cc-list-query-font-size: 13px; --cc-list-action-font-size: 13px; }
.page-intro { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--admin-space-6, 16px); margin-bottom: var(--admin-space-6, 16px); }
.page-intro h1 { margin: 0; color: var(--cc-text); font-size: 22px; line-height: 1.35; }
.page-intro p { margin: 5px 0 0; color: var(--cc-muted); font-size: 13px; line-height: 1.6; }
.page-intro p { margin: 5px 0 0; color: var(--cc-muted); font-size: var(--admin-font-body, 13px); line-height: 1.6; }
.page-intro :deep(.el-button) { flex: 0 0 auto; }
.orders-query-panel { overflow: hidden; border: 1px solid var(--cc-border); border-radius: 4px; background: #fff; }
.status-tabs { display: flex; max-width: 100%; overflow-x: auto; margin-bottom: 0; border-bottom: 1px solid #e5e7eb; background: #f9fafb; scrollbar-width: thin; }
.status-tab { position: relative; flex: 0 0 auto; min-height: 38px; padding: 0 16px; border: 0; background: transparent; color: var(--cc-muted); font: inherit; font-size: 13px; cursor: pointer; }
.orders-query-panel { overflow: hidden; border: 1px solid var(--cc-border); border-radius: var(--admin-radius, 4px); background: var(--admin-surface, #fff); }
.status-tabs { display: flex; max-width: 100%; overflow-x: auto; margin-bottom: 0; border-bottom: 1px solid var(--admin-table-border, #e5e7eb); background: #f9fafb; scrollbar-width: thin; }
.status-tab { position: relative; flex: 0 0 auto; min-height: var(--admin-tab-height, 38px); padding: 0 var(--admin-space-6, 16px); border: 0; background: transparent; color: var(--cc-muted); font: inherit; font-size: var(--admin-font-body, 13px); cursor: pointer; }
.status-tab::after { position: absolute; right: 12px; bottom: 0; left: 12px; height: 3px; background: transparent; content: ''; }
.status-tab:hover { color: var(--cc-primary); background: #f6f9fd; }
.status-tab:focus-visible { outline: 2px solid var(--cc-primary); outline-offset: -2px; }
.status-tab.is-active { color: var(--cc-primary); font-weight: 600; }
.status-tab.is-active::after { background: var(--cc-primary); }
.orders-query-toolbar { border: 0; padding: 10px 12px; }
.table-card { overflow: hidden; border-radius: 4px; box-shadow: none; }
.coload-error { margin: 8px 12px; padding: 8px 10px; border: 1px solid var(--el-color-danger-light-5); border-radius: 4px; background: var(--el-color-danger-light-9); color: var(--el-color-danger); font-size: 12px; }
.orders-query-toolbar { border: 0; padding: var(--admin-space-4, 10px) var(--admin-space-5, 12px); }
.table-card { overflow: hidden; border-radius: var(--admin-radius, 4px); box-shadow: none; }
.coload-error { margin: var(--admin-space-3, 8px) var(--admin-space-5, 12px); padding: var(--admin-space-3, 8px) var(--admin-space-4, 10px); border: 1px solid var(--el-color-danger-light-5); border-radius: var(--admin-radius, 4px); background: var(--el-color-danger-light-9); color: var(--el-color-danger); font-size: var(--admin-font-caption, 12px); }
.coload-error :deep(.api-error-meta) { margin-top: 5px; }
.order-code-cell { display: grid; justify-items: start; gap: 4px; }
.order-code-cell { display: grid; justify-items: start; gap: var(--admin-space-1, 4px); }
.coload-tag-trigger { margin: 0; padding: 0; border: 0; background: transparent; cursor: pointer; }
.coload-tag-trigger:focus-visible { outline: 2px solid var(--el-color-primary); outline-offset: 2px; border-radius: 3px; }
.coload-selection { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 12px; }
.coload-selection > span { padding: 4px 7px; border: 1px solid var(--cc-border); border-radius: 4px; background: var(--cc-surface-muted); font-size: 12px; }
.coload-detail-summary { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 10px; }
.coload-detail-scroll { overflow-x: auto; border: 1px solid var(--cc-border); border-radius: 4px; }
.coload-detail-table { width: 100%; border-collapse: collapse; font-size: 12px; }
.coload-detail-table th,.coload-detail-table td { padding: 8px 10px; border-bottom: 1px solid var(--cc-border); text-align: left; vertical-align: top; }
.coload-selection { display: flex; flex-wrap: wrap; gap: var(--admin-space-2, 6px); margin-top: var(--admin-space-5, 12px); }
.coload-selection > span { padding: var(--admin-space-1, 4px) 7px; border: 1px solid var(--cc-border); border-radius: var(--admin-radius, 4px); background: var(--cc-surface-muted); font-size: var(--admin-font-caption, 12px); }
.coload-detail-summary { display: flex; align-items: center; justify-content: space-between; gap: var(--admin-space-5, 12px); margin-bottom: var(--admin-space-4, 10px); }
.coload-detail-scroll { overflow-x: auto; border: 1px solid var(--cc-border); border-radius: var(--admin-radius, 4px); }
.coload-detail-table { width: 100%; border-collapse: collapse; font-size: var(--admin-font-caption, 12px); }
.coload-detail-table th,.coload-detail-table td { padding: var(--admin-space-3, 8px) var(--admin-space-4, 10px); border-bottom: 1px solid var(--cc-border); text-align: left; vertical-align: top; }
.coload-detail-table th { background: var(--cc-surface-muted); color: var(--cc-muted); font-weight: 600; }
.coload-detail-table tbody tr:last-child td { border-bottom: 0; }
.coload-detail-table td { min-width: 210px; }
@@ -2858,95 +2859,49 @@ onBeforeUnmount(() => {
.coload-detail-table td strong,.coload-detail-table td span { display: block; overflow-wrap: anywhere; }
.coload-detail-table td span { color: var(--cc-muted); }
.coload-detail-table :deep(.el-button) { min-height: 24px; padding: 2px 0; }
.coload-toolbar-action { display: flex; min-width: 0; align-items: center; gap: 8px; }
.subsequent-fulfillment { margin-top: 12px; padding-top: 10px; border-top: 1px solid var(--cc-border); }
.subsequent-fulfillment h3 { margin: 0 0 8px; font-size: 14px; }
.coload-toolbar-action { display: flex; min-width: 0; align-items: center; gap: var(--admin-space-3, 8px); }
.subsequent-fulfillment { margin-top: var(--admin-space-5, 12px); padding-top: var(--admin-space-4, 10px); border-top: 1px solid var(--cc-border); }
.subsequent-fulfillment h3 { margin: 0 0 var(--admin-space-3, 8px); font-size: var(--admin-font-section, 14px); }
.fulfillment-facts { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); margin: 0; border-top: 1px solid var(--cc-border); border-left: 1px solid var(--cc-border); overflow: hidden; }
.batch-approval-summary { display: flex; align-items: center; justify-content: space-between; gap: 16px; margin: 12px 0 8px; color: var(--cc-muted); font-size: 13px; white-space: nowrap; }
.batch-approval-summary { display: flex; align-items: center; justify-content: space-between; gap: var(--admin-space-6, 16px); margin: var(--admin-space-5, 12px) 0 var(--admin-space-3, 8px); color: var(--cc-muted); font-size: var(--admin-font-body, 13px); white-space: nowrap; }
.batch-approval-summary strong { color: var(--cc-text); }
.batch-approval-table :deep(.cell) { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.fulfillment-facts > div { display: flex; align-items: baseline; gap: 10px; min-width: 0; padding: 8px 10px; border-right: 1px solid var(--cc-border); border-bottom: 1px solid var(--cc-border); background: #fff; }
.fulfillment-facts > div { display: flex; align-items: baseline; gap: var(--admin-space-4, 10px); min-width: 0; padding: var(--admin-space-3, 8px) var(--admin-space-4, 10px); border-right: 1px solid var(--cc-border); border-bottom: 1px solid var(--cc-border); background: var(--admin-surface, #fff); }
.fulfillment-facts > div.wide { grid-column: span 2; }
.fulfillment-facts dt { flex: 0 0 auto; color: var(--cc-muted); font-size: 12px; }
.fulfillment-facts dd { min-width: 0; margin: 0; overflow-wrap: anywhere; font-size: 13px; }
.fulfillment-empty { margin: 0; padding: 12px; border: 1px dashed var(--cc-border); border-radius: 6px; color: var(--cc-muted); text-align: center; }
.table-card :deep(.el-table) { --el-table-border-color: #e5e7eb; --el-table-header-bg-color: #f9fafb; --el-table-row-hover-bg-color: #f3f7fb; --el-table-tr-bg-color: #fff; color: #374151; font-size: 13px; }
.table-card :deep(.el-table th.el-table__cell) { height: 36px; padding: 4px 0; background: #f9fafb; color: #374151; font-size: 13px; font-weight: 600; }
.table-card :deep(.el-table td.el-table__cell) { height: 36px; padding: 4px 0; }
.table-card :deep(.el-table--striped .el-table__body tr.el-table__row--striped td.el-table__cell) { background: #fcfcfd; }
.table-card :deep(.el-table__body tr:hover > td.el-table__cell) { background: #f3f7fb; }
.row-actions { display: flex; align-items: center; gap: 6px; white-space: nowrap; }
.fulfillment-facts dt { flex: 0 0 auto; color: var(--cc-muted); font-size: var(--admin-font-caption, 12px); }
.fulfillment-facts dd { min-width: 0; margin: 0; overflow-wrap: anywhere; font-size: var(--admin-font-body, 13px); }
.fulfillment-empty { margin: 0; padding: var(--admin-space-5, 12px); border: 1px dashed var(--cc-border); border-radius: 6px; color: var(--cc-muted); text-align: center; }
.row-actions { display: flex; align-items: center; gap: var(--admin-space-2, 6px); white-space: nowrap; }
.row-actions :deep(.el-button) { margin-left: 0; }
.table-card :deep(.data-table__system-cell .row-actions .el-button) { height: 28px; margin-left: 0; padding: 4px 10px; font-size: 13px; }
.row-actions :deep(.order-action-button) {
--el-button-text-color: #52525b;
--el-button-bg-color: #fff;
--el-button-border-color: #dfe3ee;
--el-button-hover-text-color: #3f3f46;
--el-button-hover-bg-color: #f8fafc;
--el-button-hover-border-color: #b8c0d4;
--el-button-active-text-color: var(--el-button-hover-text-color);
--el-button-active-bg-color: var(--el-button-hover-bg-color);
--el-button-active-border-color: var(--el-button-hover-border-color);
--el-button-disabled-text-color: #a1a1aa;
--el-button-disabled-bg-color: #fff;
--el-button-disabled-border-color: #e5e7eb;
border-radius: 6px;
font-weight: 600;
box-shadow: none;
}
.row-actions :deep(.order-action-approve) {
--el-button-text-color: #c76500;
--el-button-border-color: #fed7aa;
--el-button-hover-text-color: #a34b00;
--el-button-hover-bg-color: #fff7ed;
--el-button-hover-border-color: #fb923c;
}
.row-actions :deep(.order-action-measurement) {
--el-button-text-color: #7c3aed;
--el-button-border-color: #ddd6fe;
--el-button-hover-text-color: #6d28d9;
--el-button-hover-bg-color: #f5f3ff;
--el-button-hover-border-color: #a78bfa;
}
.row-actions :deep(.order-action-button:focus-visible) { outline: 2px solid var(--el-button-text-color); outline-offset: 2px; }
.table-card :deep(.data-table__system-cell .row-actions .el-dropdown) { margin-left: 0; }
.planning-cell { display: grid; min-width: 0; gap: 4px; }
.planning-cell > small { color: var(--cc-muted); font-size: 11px; }
.planning-cell { display: grid; min-width: 0; gap: var(--admin-space-1, 4px); }
.planning-cell > small { color: var(--cc-muted); font-size: var(--admin-font-caption, 11px); }
.planning-volume-input,.planning-arrival-input { width: 100%; max-width: 100%; }
.planning-cell :deep(.el-input__wrapper) { min-height: 32px; padding-right: 9px; padding-left: 9px; }
.planning-cell :deep(.el-input__wrapper) { min-height: var(--admin-control-height, 32px); padding-right: 9px; padding-left: 9px; }
.planning-cell :deep(.el-input__inner) { min-width: 0; text-align: left; }
.inbound-relation-cell { display: grid; min-width: 0; gap: 5px; }
.inbound-code-value { display: block; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.order-relation-link { width: fit-content; max-width: 100%; height: auto; justify-content: flex-start; padding: 0; font-size: 12px; }
.order-relation-link { width: fit-content; max-width: 100%; height: auto; justify-content: flex-start; padding: 0; font-size: var(--admin-font-caption, 12px); }
.order-relation-link :deep(span) { min-width: 0; white-space: normal; text-align: left; }
.primary-cell { display: grid; min-width: 0; gap: 4px; }
.primary-cell { display: grid; min-width: 0; gap: var(--admin-space-1, 4px); }
.primary-cell strong { overflow-wrap: anywhere; }
.primary-cell span { color: var(--cc-muted); font-size: 12px; overflow-wrap: anywhere; }
.order-detail-header { display: flex; min-width: 0; min-height: 40px; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 0; padding: 8px 12px; border: 1px solid var(--cc-border); border-bottom: 0; border-radius: 4px 4px 0 0; background: #fff; }
.order-detail-heading { display: flex; min-width: 0; align-items: center; gap: 10px; }
.primary-cell span { color: var(--cc-muted); font-size: var(--admin-font-caption, 12px); overflow-wrap: anywhere; }
.order-detail-header { display: flex; min-width: 0; min-height: 40px; align-items: center; justify-content: space-between; gap: var(--admin-space-5, 12px); margin-bottom: 0; padding: var(--admin-space-3, 8px) var(--admin-space-5, 12px); border: 1px solid var(--cc-border); border-bottom: 0; border-radius: 4px 4px 0 0; background: var(--admin-surface, #fff); }
.order-detail-heading { display: flex; min-width: 0; align-items: center; gap: var(--admin-space-4, 10px); }
.back-button { flex: 0 0 auto; padding: 0; }
.order-title { min-width: 0; }
.order-title h1 { margin: 0; color: #172033; font-size: 16px; line-height: 1.5; overflow-wrap: anywhere; }
.detail-header-actions { display: flex; flex: 0 0 auto; gap: 8px; }
.order-title h1 { margin: 0; color: #172033; font-size: var(--admin-font-title, 16px); line-height: 1.5; overflow-wrap: anywhere; }
.detail-header-actions { display: flex; flex: 0 0 auto; gap: var(--admin-space-3, 8px); }
.detail-header-actions :deep(.el-button + .el-button) { margin-left: 0; }
.detail-load-error { min-height: 280px; }
.order-detail-content { min-width: 0; }
.detail-overview { display: flex; flex-wrap: wrap; gap: 6px 24px; margin: 0; padding: 8px 12px; border: 1px solid var(--cc-border); border-top: 0; background: #fff; }
.detail-tabs { display: flex; overflow-x: auto; overflow-y: hidden; border: 1px solid var(--cc-border); border-block: 0; background: #fff; scrollbar-width: thin; }
.detail-tabs button { position: relative; display: inline-flex; min-width: max-content; min-height: 38px; flex: 0 0 auto; align-items: center; justify-content: center; gap: 6px; padding: 0 16px; border: 0; background: transparent; color: #475467; font-size: 13px; font-weight: 500; cursor: pointer; }
.detail-tab-icon { display: inline-flex; width: 18px; height: 18px; flex: 0 0 18px; align-items: center; justify-content: center; font-family: "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", sans-serif; font-size: 17px; font-weight: 400; line-height: 1; }
.detail-tabs button:last-child { border-right: 0; }
.detail-tabs button::after { position: absolute; right: 12px; bottom: 0; left: 12px; height: 2px; background: transparent; content: ''; }
.detail-tabs button:hover { background: #fafbff; color: #344054; }
.detail-tabs button.active { background: #f7f8ff; color: var(--cc-primary); font-weight: 600; }
.detail-tabs button.active::after { background: var(--cc-primary); }
.detail-panel { min-width: 0; padding: 12px; border: 1px solid var(--cc-border); border-radius: 0 0 4px 4px; background: #fff; }
.panel-title { display: flex; align-items: center; justify-content: space-between; gap: 12px; min-height: 28px; margin-bottom: 8px; }
.panel-title h2 { margin: 0; color: #344054; font-size: 14px; font-weight: 600; }
.panel-title p { margin: 0; color: var(--cc-muted); font-size: 12px; line-height: 1.5; }
.detail-overview { display: flex; flex-wrap: wrap; gap: var(--admin-space-2, 6px) var(--admin-space-7, 24px); margin: 0; padding: var(--admin-space-3, 8px) var(--admin-space-5, 12px); border: 1px solid var(--cc-border); border-top: 0; background: var(--admin-surface, #fff); }
.detail-panel { min-width: 0; padding: var(--admin-space-5, 12px); border: 1px solid var(--cc-border); border-radius: 0 0 4px 4px; background: var(--admin-surface, #fff); }
.panel-title { display: flex; align-items: center; justify-content: space-between; gap: var(--admin-space-5, 12px); min-height: var(--admin-action-height, 28px); margin-bottom: var(--admin-space-3, 8px); }
.panel-title h2 { margin: 0; color: #344054; font-size: var(--admin-font-section, 14px); font-weight: 600; }
.panel-title p { margin: 0; color: var(--cc-muted); font-size: var(--admin-font-caption, 12px); line-height: 1.5; }
.panel-title > svg { color: var(--cc-primary); }
.measurement-title-actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 8px; }
.measurement-title-actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: var(--admin-space-3, 8px); }
.measurement-title-actions :deep(.el-button) { margin-left: 0; }
.fee-confirm-form { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 22px; }
.fee-confirm-price-alert { margin-bottom: 14px; }
@@ -2954,84 +2909,81 @@ onBeforeUnmount(() => {
.fee-confirm-number { width: 100%; }
.fee-confirm-number :deep(.el-input__wrapper) { padding-right: 92px; }
.fee-confirm-unit { position: absolute; top: 50%; right: 12px; z-index: 1; transform: translateY(-50%); color: var(--cc-muted); pointer-events: none; }
.fee-confirm-reference-title { margin: 8px 0 12px; color: var(--cc-text); font-size: 14px; }
.fee-confirm-reference-title { margin: var(--admin-space-3, 8px) 0 var(--admin-space-5, 12px); color: var(--cc-text); font-size: var(--admin-font-section, 14px); }
.fee-confirm-price-reason { grid-column: 1 / -1; }
.fee-confirm-price-summary { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); margin: 0 0 16px; border: 1px solid var(--cc-border); background: #f8fafc; }
.fee-confirm-price-summary > div { min-width: 0; padding: 10px 12px; }
.fee-confirm-price-summary { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); margin: 0 0 var(--admin-space-6, 16px); border: 1px solid var(--cc-border); background: var(--admin-label-bg, #f8fafc); }
.fee-confirm-price-summary > div { min-width: 0; padding: var(--admin-space-4, 10px) var(--admin-space-5, 12px); }
.fee-confirm-price-summary > div + div { border-left: 1px solid var(--cc-border); }
.fee-confirm-price-summary dt { color: var(--cc-muted); font-size: 11px; }
.fee-confirm-price-summary dd { margin: 4px 0 0; color: var(--cc-text); font-size: 13px; font-weight: 650; }
.fee-confirm-price-summary dt { color: var(--cc-muted); font-size: var(--admin-font-caption, 11px); }
.fee-confirm-price-summary dd { margin: var(--admin-space-1, 4px) 0 0; color: var(--cc-text); font-size: var(--admin-font-body, 13px); font-weight: 650; }
@media (max-width: 680px) { .fee-confirm-form { grid-template-columns: 1fr; } .fee-confirm-unit { right: 12px; } }
.fee-section-title { margin: 12px 0 8px; color: #344054; font-size: 14px; line-height: 1.4; }
.fee-section-title { margin: var(--admin-space-5, 12px) 0 var(--admin-space-3, 8px); color: #344054; font-size: var(--admin-font-section, 14px); line-height: 1.4; }
.fee-overview { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); margin: 0; border: 1px solid var(--cc-border); }
.fee-overview > div { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 12px; padding: 8px 10px; }
.fee-overview > div { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--admin-space-1, 4px) var(--admin-space-5, 12px); padding: var(--admin-space-3, 8px) var(--admin-space-4, 10px); }
.fee-overview > div + div { border-left: 1px solid var(--cc-border); }
.fee-overview dt, .fee-overview small { color: var(--cc-muted); font-size: 12px; }
.fee-overview dd { margin: 0; font-size: 16px; font-weight: 600; font-variant-numeric: tabular-nums; }
.fee-overview dt, .fee-overview small { color: var(--cc-muted); font-size: var(--admin-font-caption, 12px); }
.fee-overview dd { margin: 0; font-size: var(--admin-font-title, 16px); font-weight: 600; font-variant-numeric: tabular-nums; }
.fee-overview small { margin-left: auto; }
.fee-section-title--secondary { margin-top: 12px; padding-top: 10px; border-top: 1px solid var(--cc-border); }
.order-info-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 0; margin: 0; overflow: hidden; border-top: 1px solid var(--cc-border); border-left: 1px solid var(--cc-border); background: #fff; }
.order-info-grid > div, .measurement-grid > div { display: grid; grid-template-columns: 112px minmax(0, 1fr); min-width: 0; min-height: 36px; align-items: stretch; gap: 0; border-right: 1px solid var(--cc-border); border-bottom: 1px solid var(--cc-border); background: #fff; }
.fee-section-title--secondary { margin-top: var(--admin-space-5, 12px); padding-top: var(--admin-space-4, 10px); border-top: 1px solid var(--cc-border); }
.order-info-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); margin: 0; }
.order-info-grid > div.wide { grid-column: span 2; }
.order-info-grid dt, .measurement-grid dt { margin: 0; padding: 8px; color: #667085; background: #f8fafc; font-size: 12px; line-height: 20px; }
.order-info-grid dd, .measurement-grid dd { min-width: 0; margin: 0; padding: 8px; color: #25324a; font-size: 13px; font-weight: 400; line-height: 20px; overflow-wrap: anywhere; }
.detail-table-wrap { overflow-x: auto; }
.detail-table-wrap :deep(.el-table) { min-width: 720px; --el-table-header-bg-color: #f8fafc; }
.cargo-image { width: 40px; height: 32px; border: 1px solid var(--cc-border); border-radius: 2px; background: #f8fafc; cursor: zoom-in; }
.measurement-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 1px; margin: 0; border: 1px solid var(--cc-border); background: var(--cc-border); }
.measurement-section { margin-top: 12px; }
.measurement-section-title { display: flex; width: 100%; min-height: 36px; align-items: center; gap: 12px; padding: 4px 10px; border: 1px solid var(--cc-border); border-bottom: 0; background: #f8fafc; color: var(--cc-text); }
.detail-table-wrap :deep(.el-table) { min-width: 720px; --el-table-header-bg-color: var(--admin-label-bg, #f8fafc); }
.cargo-image { width: 40px; height: var(--admin-control-height, 32px); border: 1px solid var(--cc-border); border-radius: 2px; background: var(--admin-label-bg, #f8fafc); cursor: zoom-in; }
.measurement-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); margin: 0; }
.measurement-section { margin-top: var(--admin-space-5, 12px); }
.measurement-section-title { display: flex; width: 100%; min-height: var(--admin-row-height, 36px); align-items: center; gap: var(--admin-space-5, 12px); padding: var(--admin-space-1, 4px) var(--admin-space-4, 10px); border: 1px solid var(--cc-border); border-bottom: 0; background: var(--admin-label-bg, #f8fafc); color: var(--cc-text); }
.measurement-section-title:hover { background: #f2f6fb; }
.measurement-section-title h3 { flex: 0 0 auto; margin: 0; color: var(--cc-text); font-size: 13px; font-weight: 600; }
.measurement-section-summary { display: flex; min-width: 0; flex: 1; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: 4px 16px; color: var(--cc-muted); font-size: 12px; }
.measurement-section-toggle { display: flex; min-width: 0; min-height: 28px; flex: 1; align-items: center; gap: 16px; padding: 0; border: 0; background: transparent; color: inherit; font: inherit; text-align: left; cursor: pointer; }
.measurement-section-title h3 { flex: 0 0 auto; margin: 0; color: var(--cc-text); font-size: var(--admin-font-body, 13px); font-weight: 600; }
.measurement-section-summary { display: flex; min-width: 0; flex: 1; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: var(--admin-space-1, 4px) var(--admin-space-6, 16px); color: var(--cc-muted); font-size: var(--admin-font-caption, 12px); }
.measurement-section-toggle { display: flex; min-width: 0; min-height: var(--admin-action-height, 28px); flex: 1; align-items: center; gap: var(--admin-space-6, 16px); padding: 0; border: 0; background: transparent; color: inherit; font: inherit; text-align: left; cursor: pointer; }
.measurement-section-toggle:focus-visible { outline: 2px solid var(--cc-primary); outline-offset: 2px; }
.measurement-section-toggle > svg { flex: 0 0 auto; margin-left: auto; color: var(--cc-muted); }
.measurement-section-mode { flex: 0 0 190px; }
.measurement-section-title > .measurement-section-mode { display: none; }
.measurement-rule-selector { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; margin: 0; padding: 8px 10px; border: 1px solid var(--cc-border); border-top: 0; background: #fff; }
.measurement-rule-selector__label { color: var(--cc-text); font-size: 12px; font-weight: 650; white-space: nowrap; }
.measurement-rule-selector { display: flex; flex-wrap: wrap; align-items: center; gap: var(--admin-space-4, 10px); margin: 0; padding: var(--admin-space-3, 8px) var(--admin-space-4, 10px); border: 1px solid var(--cc-border); border-top: 0; background: var(--admin-surface, #fff); }
.measurement-rule-selector__label { color: var(--cc-text); font-size: var(--admin-font-caption, 12px); font-weight: 650; white-space: nowrap; }
.measurement-rule-selector__control { width: 270px; }
.measurement-rule-selector__hint { color: var(--cc-muted); font-size: 12px; }
.measurement-rule-selector__hint { color: var(--cc-muted); font-size: var(--admin-font-caption, 12px); }
.measurement-outer-dimension-input { width: 128px; max-width: 100%; }
.measurement-total { display: flex; min-height: 32px; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: 4px 20px; padding: 6px 10px; border: 1px solid var(--cc-border); border-top: 0; background: #f8fafc; color: #475467; font-size: 12px; font-variant-numeric: tabular-nums; }
.measurement-total { display: flex; min-height: var(--admin-control-height, 32px); flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: var(--admin-space-1, 4px) 20px; padding: var(--admin-space-2, 6px) var(--admin-space-4, 10px); border: 1px solid var(--cc-border); border-top: 0; background: var(--admin-label-bg, #f8fafc); color: #475467; font-size: var(--admin-font-caption, 12px); font-variant-numeric: tabular-nums; }
.measurement-total strong { margin-right: auto; color: var(--cc-text); font-size: 13px; }
.weight-evidence { margin-top: 12px; padding: 10px 0 0; border-top: 1px solid var(--cc-border); background: #fff; }
.weight-evidence__heading { display: flex; align-items: center; justify-content: space-between; gap: 16px; margin-bottom: 8px; }
.weight-evidence__heading h4 { margin: 0; color: var(--cc-text); font-size: 14px; }
.weight-evidence__heading p { margin: 5px 0 0; color: var(--cc-muted); font-size: 12px; }
.measurement-total strong { margin-right: auto; color: var(--cc-text); font-size: var(--admin-font-body, 13px); }
.weight-evidence { margin-top: var(--admin-space-5, 12px); padding: var(--admin-space-4, 10px) 0 0; border-top: 1px solid var(--cc-border); background: var(--admin-surface, #fff); }
.weight-evidence__heading { display: flex; align-items: center; justify-content: space-between; gap: var(--admin-space-6, 16px); margin-bottom: var(--admin-space-3, 8px); }
.weight-evidence__heading h4 { margin: 0; color: var(--cc-text); font-size: var(--admin-font-section, 14px); }
.weight-evidence__heading p { margin: 5px 0 0; color: var(--cc-muted); font-size: var(--admin-font-caption, 12px); }
.weight-evidence__heading dl { margin: 0; text-align: right; }
.weight-evidence__heading dt { color: var(--cc-muted); font-size: 11px; }
.weight-evidence__heading dd { margin: 0; color: var(--cc-text); font-size: 13px; font-weight: 600; }
.weight-evidence-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 8px; }
.weight-evidence-card { display: grid; grid-template-columns: 96px minmax(0, 1fr); align-items: center; min-width: 0; padding: 8px; border: 1px solid var(--cc-border); background: #fff; }
.weight-evidence-card > dl { display: grid; gap: 4px; margin: 0; padding: 0 0 0 8px; }
.weight-evidence-card > dl > div { display: flex; min-width: 0; justify-content: space-between; gap: 12px; }
.weight-evidence-card dt { color: var(--cc-muted); font-size: 11px; }
.weight-evidence-card dd { min-width: 0; margin: 0; color: var(--cc-text); font-size: 12px; font-weight: 650; text-align: right; overflow-wrap: anywhere; }
.weight-evidence__heading dt { color: var(--cc-muted); font-size: var(--admin-font-caption, 11px); }
.weight-evidence__heading dd { margin: 0; color: var(--cc-text); font-size: var(--admin-font-body, 13px); font-weight: 600; }
.weight-evidence-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: var(--admin-space-3, 8px); }
.weight-evidence-card { display: grid; grid-template-columns: 96px minmax(0, 1fr); align-items: center; min-width: 0; padding: var(--admin-space-3, 8px); border: 1px solid var(--cc-border); background: var(--admin-surface, #fff); }
.weight-evidence-card > dl { display: grid; gap: var(--admin-space-1, 4px); margin: 0; padding: 0 0 0 var(--admin-space-3, 8px); }
.weight-evidence-card > dl > div { display: flex; min-width: 0; justify-content: space-between; gap: var(--admin-space-5, 12px); }
.weight-evidence-card dt { color: var(--cc-muted); font-size: var(--admin-font-caption, 11px); }
.weight-evidence-card dd { min-width: 0; margin: 0; color: var(--cc-text); font-size: var(--admin-font-caption, 12px); font-weight: 650; text-align: right; overflow-wrap: anywhere; }
.measurement-original-preview { display: block; width: 100%; max-height: min(72vh, 760px); object-fit: contain; border-radius: 8px; background: #f2f4f7; }
.hold-section { padding-bottom: 12px; border-bottom: 1px solid var(--cc-border); }
.other-notes-section { padding-top: 10px; }
.hold-summary { display: flex; align-items: center; justify-content: space-between; gap: 16px; padding: 8px 10px; border: 1px solid var(--cc-border); background: #fff; }
.hold-summary dl { display: grid; min-width: 0; flex: 1; grid-template-columns: 210px minmax(280px, 1fr); gap: 12px; margin: 0; }
.hold-summary dl > div { display: flex; align-items: baseline; gap: 10px; min-width: 0; }
.hold-summary dt { flex: 0 0 auto; margin: 0; color: var(--cc-muted); font-size: 12px; }
.hold-summary dd { margin: 0; color: var(--cc-text); font-size: 13px; overflow-wrap: anywhere; }
.hold-actions { display: flex; flex: 0 0 auto; gap: 8px; }
.hold-section { padding-bottom: var(--admin-space-5, 12px); border-bottom: 1px solid var(--cc-border); }
.other-notes-section { padding-top: var(--admin-space-4, 10px); }
.hold-summary { display: flex; align-items: center; justify-content: space-between; gap: var(--admin-space-6, 16px); padding: var(--admin-space-3, 8px) var(--admin-space-4, 10px); border: 1px solid var(--cc-border); background: var(--admin-surface, #fff); }
.hold-summary dl { display: grid; min-width: 0; flex: 1; grid-template-columns: 210px minmax(280px, 1fr); gap: var(--admin-space-5, 12px); margin: 0; }
.hold-summary dl > div { display: flex; align-items: baseline; gap: var(--admin-space-4, 10px); min-width: 0; }
.hold-summary dt { flex: 0 0 auto; margin: 0; color: var(--cc-muted); font-size: var(--admin-font-caption, 12px); }
.hold-summary dd { margin: 0; color: var(--cc-text); font-size: var(--admin-font-body, 13px); overflow-wrap: anywhere; }
.hold-actions { display: flex; flex: 0 0 auto; gap: var(--admin-space-3, 8px); }
.hold-actions :deep(.el-button) { margin-left: 0; }
.hold-block-reason { margin: 12px 0 0; padding: 9px 12px; border: 1px solid var(--el-color-warning-light-5); border-radius: 4px; background: var(--el-color-warning-light-9); color: var(--el-color-warning-dark-2); font-size: 12px; }
.hold-history-title { margin: 16px 0 8px; color: #344054; font-size: 14px; }
.hold-block-reason { margin: var(--admin-space-5, 12px) 0 0; padding: 9px var(--admin-space-5, 12px); border: 1px solid var(--el-color-warning-light-5); border-radius: var(--admin-radius, 4px); background: var(--el-color-warning-light-9); color: var(--el-color-warning-dark-2); font-size: var(--admin-font-caption, 12px); }
.hold-history-title { margin: var(--admin-space-6, 16px) 0 var(--admin-space-3, 8px); color: #344054; font-size: var(--admin-font-section, 14px); }
.hold-history-table { min-width: 0; overflow-x: auto; }
.hold-command-form { margin-top: 16px; }
.hold-evidence-hint { margin: -4px 0 0; color: var(--cc-muted); font-size: 12px; }
.hold-command-form { margin-top: var(--admin-space-6, 16px); }
.hold-evidence-hint { margin: -var(--admin-space-1, 4px) 0 0; color: var(--cc-muted); font-size: var(--admin-font-caption, 12px); }
.notes-list { display: grid; gap: 0; margin: 0; padding: 0; list-style: none; }
.notes-list p { margin: 4px 0 0; color: var(--cc-muted); font-size: 12px; line-height: 1.55; overflow-wrap: anywhere; }
.notes-list p { margin: var(--admin-space-1, 4px) 0 0; color: var(--cc-muted); font-size: var(--admin-font-caption, 12px); line-height: 1.55; overflow-wrap: anywhere; }
.notes-list li { padding: 14px 0; border-bottom: 1px solid #edf0f4; }
.notes-list li:first-child { padding-top: 0; }
.notes-list li:last-child { padding-bottom: 0; border-bottom: 0; }
.notes-list li > p { margin: 0; color: var(--cc-text); font-size: 13px; }
.notes-list span { display: block; margin-top: 5px; color: var(--cc-muted); font-size: 12px; }
.notes-list li > p { margin: 0; color: var(--cc-text); font-size: var(--admin-font-body, 13px); }
.notes-list span { display: block; margin-top: 5px; color: var(--cc-muted); font-size: var(--admin-font-caption, 12px); }
@media (max-width: 960px) { .order-info-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } .measurement-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 720px) { .page-intro { align-items: stretch; flex-direction: column; } .page-intro :deep(.el-button) { width: 100%; min-height: 44px; } .status-tab { min-height: 46px; padding: 0 14px; } .table-actions :deep(.el-button) { width: 100%; min-height: 40px; } .row-actions { flex-wrap: wrap; } .order-detail-header { align-items: stretch; flex-direction: column; } .order-detail-heading { flex-wrap: wrap; } .detail-header-actions { display: grid; grid-template-columns: 1fr; } .detail-header-actions :deep(.el-button) { min-height: 40px; } .order-info-grid, .measurement-grid { grid-template-columns: 1fr; } .order-info-grid > div.wide { grid-column: auto; } .detail-tabs button { padding: 0 18px; } .detail-panel { padding: 16px; } .measurement-section-title { align-items: flex-start; flex-wrap: wrap; padding: 12px; } .measurement-section-summary { width: 100%; flex-basis: calc(100% - 34px); flex-wrap: wrap; justify-content: flex-start; gap: 6px 16px; white-space: normal; } .measurement-section-title > svg { margin-left: auto; } .measurement-total { flex-wrap: wrap; justify-content: flex-start; gap: 6px 16px; } .measurement-total strong { width: 100%; } }
@media (max-width: 720px) { .weight-evidence__heading { flex-direction: column; } .weight-evidence__heading dl { text-align: left; } }
@@ -4,30 +4,27 @@
border-top: 0;
}
.unified-orders-page .inbound-fee-panel .fee-total,
.unified-orders-page .inbound-fee-panel .fee-summary-item { padding: 8px 10px; gap: 4px; }
.unified-orders-page .inbound-fee-panel .fee-summary-item { padding: var(--admin-space-3, 8px) var(--admin-space-4, 10px); gap: var(--admin-space-1, 4px); }
.unified-orders-page .inbound-fee-panel .fee-summary span,
.unified-orders-page .inbound-fee-panel .fee-summary small { font-size: 12px; }
.unified-orders-page .inbound-fee-panel .fee-summary small { font-size: var(--admin-font-caption, 12px); }
.unified-orders-page .inbound-fee-panel .fee-total strong { font-size: 17px; }
.unified-orders-page .inbound-fee-panel .fee-summary-item strong { font-size: 14px; }
.unified-orders-page .inbound-fee-panel .calculation-flow > div { min-height: 36px; padding: 6px 10px; }
.unified-orders-page .inbound-fee-panel .calculation-flow span { font-size: 12px; }
.unified-orders-page .inbound-fee-panel .fee-detail-grid > section { padding: 10px 12px 10px 0; }
.unified-orders-page .inbound-fee-panel .fee-detail-grid > section + section { padding-right: 0; padding-left: 12px; }
.unified-orders-page .inbound-fee-panel .fee-detail-grid dl { margin-top: 8px; }
.unified-orders-page .inbound-fee-panel .fee-detail-grid dl > div { padding: 6px 0; }
.unified-orders-page .inbound-fee-panel .fee-timeline ol { margin-top: 8px; }
.unified-orders-page .inbound-fee-panel .fee-advanced > summary { padding: 8px 2px; }
.unified-orders-page .fee-detail-manager { gap: 8px; }
.unified-orders-page .fulfillment-tree-panel { gap: 8px; }
.unified-orders-page .inbound-fee-panel .fee-summary-item strong { font-size: var(--admin-font-section, 14px); }
.unified-orders-page .inbound-fee-panel .calculation-flow > div { min-height: var(--admin-row-height, 36px); padding: var(--admin-space-2, 6px) var(--admin-space-4, 10px); }
.unified-orders-page .inbound-fee-panel .calculation-flow span { font-size: var(--admin-font-caption, 12px); }
.unified-orders-page .inbound-fee-panel .fee-detail-grid > section { padding: var(--admin-space-4, 10px) var(--admin-space-5, 12px) var(--admin-space-4, 10px) 0; }
.unified-orders-page .inbound-fee-panel .fee-detail-grid > section + section { padding-right: 0; padding-left: var(--admin-space-5, 12px); }
.unified-orders-page .inbound-fee-panel .fee-detail-grid dl { margin-top: var(--admin-space-3, 8px); }
.unified-orders-page .inbound-fee-panel .fee-detail-grid dl > div { padding: var(--admin-space-2, 6px) 0; }
.unified-orders-page .inbound-fee-panel .fee-timeline ol { margin-top: var(--admin-space-3, 8px); }
.unified-orders-page .inbound-fee-panel .fee-advanced > summary { padding: var(--admin-space-3, 8px) 2px; }
.unified-orders-page .fee-detail-manager { gap: var(--admin-space-3, 8px); }
.unified-orders-page .fulfillment-tree-panel { gap: var(--admin-space-3, 8px); }
.unified-orders-page .fulfillment-tree-panel .tree-summary {
display: flex; flex-wrap: wrap; gap: 6px 28px; padding: 8px 10px;
border-radius: 0; background: #fff;
display: flex; flex-wrap: wrap; gap: var(--admin-space-2, 6px) 28px; padding: var(--admin-space-3, 8px) var(--admin-space-4, 10px);
border-radius: 0; background: var(--admin-surface, #fff);
}
.unified-orders-page .fulfillment-tree-panel .tree-summary > div { display: flex; align-items: baseline; gap: 8px; }
.unified-orders-page .fulfillment-tree-panel .tree-summary strong { overflow: visible; white-space: normal; overflow-wrap: anywhere; font-size: 13px; font-weight: 600; }
.unified-orders-page .detail-panel .el-empty { padding: 12px 0; }
.unified-orders-page .detail-panel .el-empty__image { display: none; }
.unified-orders-page .detail-panel .el-empty__description { margin-top: 0; }
.unified-orders-page .fulfillment-tree-panel .tree-summary > div { display: flex; align-items: baseline; gap: var(--admin-space-3, 8px); }
.unified-orders-page .fulfillment-tree-panel .tree-summary strong { overflow: visible; white-space: normal; overflow-wrap: anywhere; font-size: var(--admin-font-body, 13px); font-weight: 600; }
.admin-density-dialog.el-dialog .batch-approval-table .cell { white-space: normal; overflow-wrap: anywhere; }
.admin-density-dialog.el-dialog .batch-approval-table .el-tag { height: auto; min-height: 24px; white-space: normal; line-height: 20px; }
@@ -144,24 +144,26 @@ async function selectScope(wrapper: VueWrapper) {
enableAutoUnmount(afterEach)
afterAll(() => vi.unstubAllGlobals())
const densitySource = readFileSync(resolve(process.cwd(), 'src/styles/adminDensity.css'), 'utf8')
describe('LocationsView', () => {
it('uses bordered striped tables and explicit semantic row actions', () => {
expect(locationsSource).toContain('border')
expect(locationsSource).toContain('stripe')
expect(locationsSource).toMatch(/size="small"\s+plain\s+type="primary"/)
expect(locationsSource).toMatch(/size="small"\s+plain\s+:type=/)
expect(locationsSource).toMatch(/size="small"\s+plain\s+type="danger"/)
expect(locationsSource).toMatch(/size="small"\s+class="density-action"/)
expect(locationsSource).toContain(':data-tone="(row as PlatformLocationResource).status === \'AVAILABLE\' ? \'danger\' : \'success\'"')
expect(locationsSource).toContain('class="density-action" data-tone="danger"')
expect(locationsSource).not.toContain('<Eye :size="15" />')
expect(locationsSource).toContain('<Pencil :size="15" />')
expect(locationsSource).toContain('<Power :size="15" />')
expect(locationsSource).toContain('<Trash2 :size="15" />')
expect(locationsSource).not.toContain('<Pencil :size="15" />')
expect(locationsSource).not.toContain('<Power :size="15" />')
expect(locationsSource).not.toContain('<Trash2 :size="15" />')
expect(locationsSource).toContain('@media (max-width: 720px)')
})
it('uses the prototype-sized centered editor without a detail drawer or section cards', () => {
expect(locationsSource).not.toContain('AppDrawer')
expect(locationsSource).not.toContain('FormSection')
expect(locationsSource).toContain('width="min(560px, calc(100vw - 28px))"')
expect(locationsSource).toContain(':global(.location-editor-dialog .el-dialog__body) { min-height: 0; overflow-y: auto; }')
expect(densitySource).toContain('.admin-density-dialog.el-dialog .el-dialog__body { min-height: 0; overflow-y: auto;')
expect(locationsSource).toContain('.editor-field-grid { grid-template-columns: minmax(0, 1fr); }')
})
beforeEach(() => {
@@ -2,7 +2,7 @@
import '@/styles/adminDensity.css'
import { computed, onMounted, reactive, ref, watch } from 'vue'
import { ElMessage, ElMessageBox, type FormInstance, type FormRules } from 'element-plus'
import { MapPin, Pencil, Plus, Power, Save, Trash2 } from '@lucide/vue'
import { MapPin, Plus, Save } from '@lucide/vue'
import { useAccessPreviewStore } from '@/core/access/accessPreview'
import { useUnsavedWorkRegistration } from '@/core/unsaved-work/useUnsavedWorkRegistration'
import { getApiErrorDetails, type ApiErrorDetails } from '@/shared/api/http'
@@ -530,32 +530,30 @@ onMounted(() => {
>
<template #toolbar-actions><el-button v-if="access.can('platform:location:create')" type="primary" @click="openCreate"><Plus :size="16" />新增库位</el-button></template>
<template #actions="{ row }">
<div class="table-actions location-actions">
<div class="location-actions density-actions">
<el-button
v-if="canEdit(row as PlatformLocationResource)"
size="small"
plain
type="primary"
class="density-action"
:loading="editorLoading"
:disabled="loading || editorLoading"
@click="openEdit(row as PlatformLocationResource)"
><Pencil :size="15" />编辑</el-button>
>编辑</el-button>
<el-button
v-if="canChangeStatus(row as PlatformLocationResource)"
size="small"
plain
:type="(row as PlatformLocationResource).status === 'AVAILABLE' ? 'danger' : 'success'"
class="density-action"
:data-tone="(row as PlatformLocationResource).status === 'AVAILABLE' ? 'danger' : 'success'"
:disabled="loading || actionLoadingIds.has(String((row as PlatformLocationResource).id))"
@click="toggleStatus(row as PlatformLocationResource)"
><Power :size="15" />{{ (row as PlatformLocationResource).status === 'AVAILABLE' ? '停用' : '启用' }}</el-button>
>{{ (row as PlatformLocationResource).status === 'AVAILABLE' ? '停用' : '启用' }}</el-button>
<el-button
v-if="canDelete(row as PlatformLocationResource)"
size="small"
plain
type="danger"
class="density-action" data-tone="danger"
:disabled="loading || actionLoadingIds.has(String((row as PlatformLocationResource).id))"
@click="removeLocation(row as PlatformLocationResource)"
><Trash2 :size="15" />删除</el-button>
>删除</el-button>
</div>
</template>
<template #location="{ row }">
@@ -678,29 +676,25 @@ onMounted(() => {
</template>
<style scoped>
.state-alert p { margin: 6px 0 0; line-height: 1.55; }
.state-alert .el-button { margin-top: 10px; }
.location-actions { display: flex; align-items: center; gap: 4px; white-space: nowrap; }
.location-actions :deep(.el-button) { margin-left: 0; }
.location-cell { display: flex; min-width: 0; align-items: center; gap: 10px; }
.state-alert p { margin: var(--admin-space-2, 6px) 0 0; line-height: 1.55; }
.state-alert .el-button { margin-top: var(--admin-space-4, 10px); }
.location-actions { white-space: nowrap; }
.location-cell { display: flex; min-width: 0; align-items: center; gap: var(--admin-space-4, 10px); }
.location-cell > span { display: grid; width: 20px; height: 20px; flex: 0 0 20px; place-items: center; border-radius: 7px; background: #eef3fa; color: var(--cc-primary); }
.location-cell > div, .stacked-value { display: flex; min-width: 0; flex-direction: column; gap: 3px; }
.location-cell strong, .stacked-value strong { overflow-wrap: anywhere; }
.location-cell small, .stacked-value small { color: var(--cc-muted); font-size: 12px; }
.location-cell small, .stacked-value small { color: var(--cc-muted); font-size: var(--admin-font-caption, 12px); }
.remark-cell { display: -webkit-box; overflow: hidden; line-height: 1.5; -webkit-box-orient: vertical; -webkit-line-clamp: 2; }
:global(.location-editor-dialog) { max-height: min(760px, calc(100vh - 32px)); display: flex; flex-direction: column; }
:global(.location-editor-dialog .el-dialog__body) { min-height: 0; overflow-y: auto; }
:global(.location-editor-dialog .el-dialog__footer) { flex: 0 0 auto; }
.editor-form { padding: 0; }
.editor-field-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 14px; }
.editor-form :deep(.el-input-number), .editor-form :deep(.el-select) { width: 100%; }
.editor-form :deep(.el-form-item) { margin-bottom: 14px; }
.editor-form :deep(.el-form-item__label) { padding-bottom: 4px; color: #344054; font-size: 12px; font-weight: 650; line-height: 1.4; }
.editor-form :deep(.el-form-item__label) { padding-bottom: var(--admin-space-1, 4px); color: #344054; font-size: var(--admin-font-caption, 12px); font-weight: 650; line-height: 1.4; }
.editor-remark-field { margin-bottom: 0 !important; }
.editor-footer { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.editor-footer > span { color: var(--cc-muted); font-size: 12px; }
.editor-footer { display: flex; align-items: center; justify-content: space-between; gap: var(--admin-space-3, 8px); }
.editor-footer > span { color: var(--cc-muted); font-size: var(--admin-font-caption, 12px); }
.editor-footer > span i { color: var(--el-color-danger); font-style: normal; }
.editor-footer > div { display: flex; gap: 8px; }
.editor-footer > div { display: flex; gap: var(--admin-space-3, 8px); }
.editor-footer :deep(.el-button + .el-button) { margin-left: 0; }
@media (max-width: 720px) {
@@ -115,22 +115,24 @@ async function loadRows(wrapper: VueWrapper, warehouseId = '') {
enableAutoUnmount(afterEach)
afterAll(() => vi.unstubAllGlobals())
const densitySource = readFileSync(resolve(process.cwd(), 'src/styles/adminDensity.css'), 'utf8')
describe('PalletsView', () => {
it('uses bordered striped tables and explicit semantic row actions', () => {
expect(palletsSource).toContain('border')
expect(palletsSource).toContain('stripe')
expect(palletsSource).toContain('size="small" plain type="primary"')
expect(palletsSource).toMatch(/size="small"\s+plain\s+type="danger"/)
expect(palletsSource).toContain('class="density-action" size="small" title="编辑托盘"')
expect(palletsSource).toContain('class="density-action" data-tone="danger"')
expect(palletsSource).not.toContain('<Eye :size="15" />')
expect(palletsSource).toContain('<Pencil :size="15" />')
expect(palletsSource).toContain('<Trash2 :size="15" />')
expect(palletsSource).not.toContain('<Pencil :size="15" />')
expect(palletsSource).not.toContain('<Trash2 :size="15" />')
expect(palletsSource).toContain('@media (max-width: 720px)')
})
it('uses the prototype-sized centered editor without an extra detail drawer', () => {
expect(palletsSource).not.toContain('<AppDrawer')
expect(palletsSource).toContain('<AppDialog v-model="editorOpen" :title="editorTitle" width="min(560px, calc(100vw - 28px))"')
expect(palletsSource).not.toContain('<el-drawer v-model="editorOpen"')
expect(palletsSource).toContain(':global(.pallet-editor-dialog .el-dialog__body) { min-height: 0; overflow-y: auto; }')
expect(densitySource).toContain('.admin-density-dialog.el-dialog .el-dialog__body { min-height: 0; overflow-y: auto;')
expect(palletsSource).toContain('.editor-field-grid { grid-template-columns: minmax(0, 1fr); }')
})
beforeEach(() => {
@@ -2,7 +2,7 @@
import '@/styles/adminDensity.css'
import { computed, onMounted, reactive, ref, watch } from 'vue'
import { ElMessage, ElMessageBox, type FormInstance, type FormRules } from 'element-plus'
import { Boxes, Pencil, Plus, Save, Trash2, Warehouse as WarehouseIcon } from '@lucide/vue'
import { Boxes, Plus, Save, Warehouse as WarehouseIcon } from '@lucide/vue'
import { useAccessPreviewStore } from '@/core/access/accessPreview'
import { useUnsavedWorkRegistration } from '@/core/unsaved-work/useUnsavedWorkRegistration'
import { listAllPlatformWarehouses, type PlatformWarehouseResource } from '@/modules/warehouses'
@@ -397,17 +397,16 @@ onMounted(() => {
>
<template #toolbar-actions><el-button v-if="access.can('platform:pallet:create')" type="primary" @click="openCreate"><Plus :size="16" />新增托盘</el-button></template>
<template #actions="{ row }">
<div class="table-actions pallet-row-actions">
<el-button v-if="access.can('platform:pallet:edit') && isDomesticWarehouse((row as PlatformPalletResource).warehouseId)" size="small" plain type="primary" title="编辑托盘" @click="openEdit(row as PlatformPalletResource)"><Pencil :size="15" />编辑</el-button>
<div class="pallet-row-actions density-actions">
<el-button v-if="access.can('platform:pallet:edit') && isDomesticWarehouse((row as PlatformPalletResource).warehouseId)" class="density-action" size="small" title="编辑托盘" @click="openEdit(row as PlatformPalletResource)">编辑</el-button>
<el-button
v-if="access.can('platform:pallet:delete')"
size="small"
plain
type="danger"
class="density-action" data-tone="danger"
title="删除托盘"
:loading="deletingIds.has(String((row as PlatformPalletResource).id))"
@click="removePallet(row as PlatformPalletResource)"
><Trash2 :size="15" />删除</el-button>
>删除</el-button>
</div>
</template>
<template #pallet="{ row }">
@@ -478,42 +477,38 @@ onMounted(() => {
</template>
<style scoped>
.pallet-page { display: grid; min-width: 0; gap: 16px; }
.summary-grid { display:flex; flex-wrap:wrap; gap:8px 28px; padding:8px 12px; border:1px solid var(--cc-border); background:#fff; }
.summary-grid article { display:flex; align-items:baseline; gap:8px; }
.summary-grid span { color:var(--cc-muted); font-size:12px; }.summary-grid strong { font-size:14px; }.summary-grid .idle { color:var(--el-color-success); }.summary-grid .occupied { color:var(--el-color-warning); }
.surface-panel { overflow: hidden; border: 1px solid var(--cc-border); border-radius: 4px; background: #fff; }
.pallet-page { display: grid; min-width: 0; gap: var(--admin-space-6, 16px); }
.summary-grid { display:flex; flex-wrap:wrap; gap:var(--admin-space-3, 8px) 28px; padding:var(--admin-space-3, 8px) var(--admin-space-5, 12px); border:1px solid var(--cc-border); background:var(--admin-surface, #fff); }
.summary-grid article { display:flex; align-items:baseline; gap:var(--admin-space-3, 8px); }
.summary-grid span { color:var(--cc-muted); font-size:var(--admin-font-caption, 12px); }.summary-grid strong { font-size:var(--admin-font-section, 14px); }.summary-grid .idle { color:var(--el-color-success); }.summary-grid .occupied { color:var(--el-color-warning); }
.surface-panel { overflow: hidden; border: 1px solid var(--cc-border); border-radius: var(--admin-radius, 4px); background: var(--admin-surface, #fff); }
.table-panel { min-width: 0; }
.context-empty { padding: 16px; color: var(--cc-muted); text-align: center; }
.pallet-row-actions { gap: 8px; white-space: nowrap; }
.pallet-row-actions :deep(.el-button + .el-button) { margin-left: 0; }
.pallet-cell { display: flex; align-items: center; gap: 10px; }
.context-empty { padding: var(--admin-space-6, 16px); color: var(--cc-muted); text-align: center; }
.pallet-row-actions { white-space: nowrap; }
.pallet-cell { display: flex; align-items: center; gap: var(--admin-space-4, 10px); }
.stacked-value { display: grid; min-width: 0; gap: 3px; }
.stacked-value strong, .stacked-value small { overflow-wrap: anywhere; }
.stacked-value small { color: var(--cc-muted); font-size: 12px; }
.stacked-value small { color: var(--cc-muted); font-size: var(--admin-font-caption, 12px); }
.pallet-cell > span { display: inline-flex; align-items: center; justify-content: center; flex: 0 0 auto; border-radius: 8px; background: var(--el-color-primary-light-9); color: var(--cc-primary); }
.pallet-cell > span { width: 20px; height: 20px; }
.remark-text { display: -webkit-box; overflow: hidden; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-height: 1.55; }
:global(.pallet-editor-dialog) { max-height: min(760px, calc(100vh - 32px)); display: flex; flex-direction: column; }
:global(.pallet-editor-dialog .el-dialog__body) { min-height: 0; overflow-y: auto; }
:global(.pallet-editor-dialog .el-dialog__footer) { flex: 0 0 auto; }
.editor-form { padding: 0; }
.editor-field-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 14px; }
.editor-form :deep(.el-input-number), .editor-form :deep(.el-select) { width: 100%; }
.editor-form :deep(.el-form-item) { margin-bottom: 14px; }
.editor-form :deep(.el-form-item__label) { padding-bottom: 4px; color: #344054; font-size: 12px; font-weight: 650; line-height: 1.4; }
.editor-form :deep(.el-form-item__label) { padding-bottom: var(--admin-space-1, 4px); color: #344054; font-size: var(--admin-font-caption, 12px); font-weight: 650; line-height: 1.4; }
@media (max-width: 720px) {
.summary-grid { grid-template-columns:1fr; }
.editor-field-grid { grid-template-columns: minmax(0, 1fr); }
}
.editor-remark-field { margin-bottom: 0 !important; }
.field-help { margin: 6px 0 0; color: var(--cc-muted); font-size: 12px; line-height: 1.5; }
.drawer-footer { display: flex; justify-content: flex-end; gap: 8px; }
.field-help { margin: var(--admin-space-2, 6px) 0 0; color: var(--cc-muted); font-size: var(--admin-font-caption, 12px); line-height: 1.5; }
.drawer-footer { display: flex; justify-content: flex-end; gap: var(--admin-space-3, 8px); }
.drawer-footer :deep(.el-button + .el-button) { margin-left: 0; }
.pallet-editor-footer { align-items: center; justify-content: space-between; }
.pallet-editor-footer > span { color: var(--cc-muted); font-size: 12px; }
.pallet-editor-footer > span { color: var(--cc-muted); font-size: var(--admin-font-caption, 12px); }
.pallet-editor-footer > span i { color: var(--el-color-danger); font-style: normal; }
.pallet-editor-footer > div { display: flex; gap: 8px; }
.pallet-editor-footer > div { display: flex; gap: var(--admin-space-3, 8px); }
.pallet-editor-footer > div :deep(.el-button + .el-button) { margin-left: 0; }
@media (max-width: 720px) {