优化
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
import ElementPlus from 'element-plus'
|
||||
import { readFileSync } from 'node:fs'
|
||||
import { resolve } from 'node:path'
|
||||
import { createPinia, setActivePinia } from 'pinia'
|
||||
import { enableAutoUnmount, flushPromises, mount } from '@vue/test-utils'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
@@ -83,7 +85,8 @@ describe('InboundFeePanel', () => {
|
||||
expect(document.body.textContent).toContain('¥8.50 / KG')
|
||||
expect(document.body.textContent).toContain('RULE-FBA-KG · V3')
|
||||
expect(document.body.textContent).toContain('RATE-LAX · V5')
|
||||
expect(document.body.textContent).toContain('发布给客户')
|
||||
expect(document.body.textContent).toContain('时间记录')
|
||||
expect(document.body.textContent).toContain('发布')
|
||||
expect(document.body.textContent).not.toContain('重新试算')
|
||||
expect(document.body.textContent).not.toContain('发送费用')
|
||||
})
|
||||
@@ -220,6 +223,15 @@ describe('InboundFeePanel', () => {
|
||||
expect(wrapper.emitted('changed')?.[0]?.[0]).toMatchObject({ feeVersion: 3 })
|
||||
})
|
||||
|
||||
it('keeps adjustment units on one line in a three-column desktop layout', () => {
|
||||
const source = readFileSync(resolve('src/modules/inbounds/fee/InboundFeePanel.vue'), 'utf8')
|
||||
|
||||
expect(source).toContain('width="min(760px, calc(100vw - 28px))"')
|
||||
expect(source).toContain('grid-template-columns: repeat(3, minmax(0, 1fr))')
|
||||
expect(source).toContain('class="adjustment-unit"')
|
||||
expect(source).toContain('white-space: nowrap')
|
||||
})
|
||||
|
||||
it('refreshes the latest snapshot after a conflict while retaining the dialog', async () => {
|
||||
vi.mocked(adjustPublishedInboundFee).mockRejectedValue(
|
||||
new ApiResponseError('费用已被其他操作员修改,请刷新后重试', 'A0409', 409),
|
||||
|
||||
@@ -233,7 +233,7 @@ defineExpose({ reload: loadFee })
|
||||
<span class="fee-heading-icon" aria-hidden="true"><CircleDollarSign :size="20" /></span>
|
||||
<div>
|
||||
<h3 id="inbound-fee-title">入库费用快照</h3>
|
||||
<p>展示审核完成后锁定的计费依据和对客费用,不在此重复触发试算或发布。</p>
|
||||
<p>测量审核后的头程费用,人工确认后才发布。</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="fee-heading-actions">
|
||||
@@ -317,41 +317,44 @@ defineExpose({ reload: loadFee })
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="fee-detail-grid">
|
||||
<section aria-labelledby="rule-title">
|
||||
<div class="section-title"><div><h4 id="rule-title">客户计费规则</h4><p>{{ snapshot.ruleCode }} · V{{ snapshot.chargeRuleVersion }}</p></div></div>
|
||||
<dl>
|
||||
<div><dt>客户</dt><dd>{{ snapshot.customerName }}</dd></div>
|
||||
<div><dt>业务类型</dt><dd>{{ firstLegBusinessTypeLabel(snapshot.businessType) }}</dd></div>
|
||||
<div><dt>计费口径</dt><dd>{{ chargeModeLabel }}</dd></div>
|
||||
<div><dt>舍入规则</dt><dd>保留 {{ snapshot.roundingScale }} 位,{{ roundingModeLabel(snapshot.roundingMode) }}</dd></div>
|
||||
</dl>
|
||||
</section>
|
||||
<section aria-labelledby="rate-title">
|
||||
<div class="section-title"><div><h4 id="rate-title">渠道价格</h4><p>{{ snapshot.rateCode }} · V{{ snapshot.carrierRateVersion }}</p></div></div>
|
||||
<dl>
|
||||
<div><dt>起运仓</dt><dd>{{ snapshot.originWarehouseName }}</dd></div>
|
||||
<div><dt>渠道</dt><dd>{{ snapshot.channelCode }}</dd></div>
|
||||
<div><dt>FBA 目的仓</dt><dd>{{ snapshot.destinationCode }}</dd></div>
|
||||
<div><dt>价格有效期</dt><dd>{{ formatDateTime(snapshot.rateEffectiveFrom) }} 至 {{ formatDateTime(snapshot.rateEffectiveTo) }}</dd></div>
|
||||
</dl>
|
||||
</section>
|
||||
</div>
|
||||
<details class="fee-advanced">
|
||||
<summary>查看价格来源与时间记录</summary>
|
||||
<div class="fee-detail-grid">
|
||||
<section v-if="snapshot.ruleCode" aria-labelledby="rule-title">
|
||||
<div class="section-title"><div><h4 id="rule-title">客户计费规则</h4><p>{{ snapshot.ruleCode }} · V{{ snapshot.chargeRuleVersion }}</p></div></div>
|
||||
<dl>
|
||||
<div><dt>客户</dt><dd>{{ snapshot.customerName }}</dd></div>
|
||||
<div><dt>业务类型</dt><dd>{{ firstLegBusinessTypeLabel(snapshot.businessType) }}</dd></div>
|
||||
<div><dt>计费口径</dt><dd>{{ chargeModeLabel }}</dd></div>
|
||||
<div><dt>舍入规则</dt><dd>{{ snapshot.roundingScale == null ? '—' : `保留 ${snapshot.roundingScale} 位,${roundingModeLabel(snapshot.roundingMode)}` }}</dd></div>
|
||||
</dl>
|
||||
</section>
|
||||
<section aria-labelledby="rate-title">
|
||||
<div class="section-title"><div><h4 id="rate-title">渠道价格</h4><p>{{ snapshot.rateCode }} · V{{ snapshot.carrierRateVersion }}</p></div></div>
|
||||
<dl>
|
||||
<div><dt>起运仓</dt><dd>{{ snapshot.originWarehouseName }}</dd></div>
|
||||
<div><dt>渠道</dt><dd>{{ snapshot.channelCode }}</dd></div>
|
||||
<div><dt>FBA 目的仓</dt><dd>{{ snapshot.destinationCode }}</dd></div>
|
||||
<div><dt>价格有效期</dt><dd>{{ formatDateTime(snapshot.rateEffectiveFrom) }} 至 {{ formatDateTime(snapshot.rateEffectiveTo) }}</dd></div>
|
||||
</dl>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<section class="fee-timeline" aria-labelledby="fee-timeline-title">
|
||||
<div class="section-title"><div><h4 id="fee-timeline-title">快照时间</h4><p>每个节点均由后端状态机记录。</p></div></div>
|
||||
<ol>
|
||||
<li><span>试算</span><strong>{{ formatDateTime(snapshot.calculatedAt) }}</strong></li>
|
||||
<li :class="{ pending: !snapshot.confirmedAt }"><span>确认</span><strong>{{ formatDateTime(snapshot.confirmedAt) }}</strong></li>
|
||||
<li :class="{ pending: !snapshot.sentAt }"><span>发布给客户</span><strong>{{ formatDateTime(snapshot.sentAt) }}</strong></li>
|
||||
</ol>
|
||||
</section>
|
||||
<section class="fee-timeline" aria-labelledby="fee-timeline-title">
|
||||
<div class="section-title"><div><h4 id="fee-timeline-title">时间记录</h4></div></div>
|
||||
<ol>
|
||||
<li><span>试算</span><strong>{{ formatDateTime(snapshot.calculatedAt) }}</strong></li>
|
||||
<li :class="{ pending: !snapshot.confirmedAt }"><span>确认</span><strong>{{ formatDateTime(snapshot.confirmedAt) }}</strong></li>
|
||||
<li :class="{ pending: !snapshot.sentAt }"><span>发布</span><strong>{{ formatDateTime(snapshot.sentAt) }}</strong></li>
|
||||
</ol>
|
||||
</section>
|
||||
</details>
|
||||
</div>
|
||||
|
||||
<AppDialog
|
||||
v-model="adjustOpen"
|
||||
title="调整入库费用"
|
||||
width="min(560px, calc(100vw - 28px))"
|
||||
width="min(760px, calc(100vw - 28px))"
|
||||
confirm-text="确认调整并生成新版本"
|
||||
confirm-type="warning"
|
||||
:confirm-loading="adjusting"
|
||||
@@ -371,15 +374,15 @@ defineExpose({ reload: loadFee })
|
||||
<div class="adjustment-amounts">
|
||||
<el-form-item label="调整后计费量" prop="chargeableQuantity" required>
|
||||
<el-input-number v-model="adjustmentForm.chargeableQuantity" aria-label="调整后计费量" :min="0.0001" :max="999999999999.9999" :precision="4" :step="1" controls-position="right" />
|
||||
<span>{{ chargeUnitLabel }}</span>
|
||||
<span class="adjustment-unit">{{ chargeUnitLabel }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="调整后单价" prop="unitPrice" required>
|
||||
<el-input-number v-model="adjustmentForm.unitPrice" aria-label="调整后单价" :min="0" :max="999999999999.9999" :precision="4" :step="0.1" controls-position="right" />
|
||||
<span>{{ snapshot?.currency }} / {{ chargeUnitLabel }}</span>
|
||||
<span class="adjustment-unit">{{ snapshot?.currency }} / {{ chargeUnitLabel }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="调整后附加费" prop="additionalAmount" required>
|
||||
<el-input-number v-model="adjustmentForm.additionalAmount" aria-label="调整后附加费" :min="0" :max="999999999999.99" :precision="2" :step="10" controls-position="right" />
|
||||
<span>{{ snapshot?.currency }}</span>
|
||||
<span class="adjustment-unit">{{ snapshot?.currency }}</span>
|
||||
</el-form-item>
|
||||
</div>
|
||||
<div class="adjustment-total"><span>当前服务端计算金额</span><strong>{{ formatMoney(snapshot?.totalAmount, snapshot?.currency) }}</strong><small>提交后以服务端返回的新版本金额为准</small></div>
|
||||
@@ -406,24 +409,24 @@ defineExpose({ reload: loadFee })
|
||||
.inbound-fee-panel :deep(.el-alert .el-button) { margin-top: 8px; }
|
||||
.empty-note { margin: 0; color: var(--cc-muted); font-size: 12px; }
|
||||
.fee-content { border-top: 1px solid var(--cc-border); }
|
||||
.fee-summary { display: grid; grid-template-columns: minmax(260px, 1.35fr) repeat(2, minmax(180px, 1fr)); border-bottom: 1px solid var(--cc-border); background: #f8fafc; }
|
||||
.fee-total, .fee-summary-item { display: flex; min-width: 0; flex-direction: column; justify-content: center; gap: 6px; padding: 20px; }
|
||||
.fee-summary { display: grid; grid-template-columns: minmax(240px, 1.25fr) repeat(2, minmax(160px, 1fr)); border-bottom: 1px solid var(--cc-border); }
|
||||
.fee-total, .fee-summary-item { display: flex; min-width: 0; flex-direction: column; justify-content: center; gap: 3px; padding: 11px 14px; }
|
||||
.fee-summary-item { border-left: 1px solid var(--cc-border); }
|
||||
.fee-summary span, .fee-summary small { color: var(--cc-muted); font-size: 11px; }
|
||||
.fee-summary strong { font-size: 18px; line-height: 1.25; overflow-wrap: anywhere; }
|
||||
.fee-total strong { color: #0f5c49; font-size: 27px; letter-spacing: -.02em; }
|
||||
.fee-total strong { color: #0f5c49; font-size: 21px; letter-spacing: -.01em; }
|
||||
.version-status { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
|
||||
.calculation-section, .fee-timeline { padding: 22px 0; border-bottom: 1px solid var(--cc-border); }
|
||||
.calculation-section, .fee-timeline { padding: 14px 0; border-bottom: 1px solid var(--cc-border); }
|
||||
.section-title { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; }
|
||||
.section-title h4 { font-size: 14px; }
|
||||
.calculation-flow { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); margin-top: 16px; border: 1px solid var(--cc-border); border-radius: 12px; overflow: hidden; }
|
||||
.calculation-flow > div { display: flex; min-width: 0; min-height: 88px; flex-direction: column; justify-content: center; gap: 6px; padding: 14px 16px; border-right: 1px solid var(--cc-border); }
|
||||
.calculation-flow { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); margin-top: 10px; border-top: 1px solid var(--cc-border); border-bottom: 1px solid var(--cc-border); }
|
||||
.calculation-flow > div { display: flex; min-width: 0; min-height: 58px; flex-direction: column; justify-content: center; gap: 3px; padding: 9px 12px; border-right: 1px solid var(--cc-border); }
|
||||
.calculation-flow > div:last-child { border-right: 0; }
|
||||
.calculation-flow span, .calculation-flow small { color: var(--cc-muted); font-size: 11px; }
|
||||
.calculation-flow strong { font-size: 15px; overflow-wrap: anywhere; }
|
||||
.calculation-flow .is-chargeable { background: #f0fdf9; }
|
||||
.calculation-flow .is-chargeable { background: #f7fbfa; }
|
||||
.calculation-flow .is-chargeable strong { color: #087d70; }
|
||||
.amount-equation { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; margin-top: 12px; padding: 12px 14px; border-radius: 8px; background: #f8fafc; color: #475467; font-size: 12px; }
|
||||
.amount-equation { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-top: 8px; padding: 7px 0; color: #475467; font-size: 12px; }
|
||||
.amount-equation i { color: #98a2b3; font-style: normal; }
|
||||
.amount-equation strong { color: var(--cc-text); font-size: 15px; }
|
||||
.fee-detail-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); border-bottom: 1px solid var(--cc-border); }
|
||||
@@ -442,15 +445,21 @@ defineExpose({ reload: loadFee })
|
||||
.fee-timeline li.pending::before { background: #98a2b3; }
|
||||
.fee-timeline span { color: var(--cc-muted); font-size: 11px; }
|
||||
.fee-timeline strong { font-size: 12px; overflow-wrap: anywhere; }
|
||||
.fee-advanced { border-top: 1px solid var(--cc-border); }
|
||||
.fee-advanced > summary { padding: 10px 2px; color: var(--cc-primary); font-size: 12px; font-weight: 600; cursor: pointer; }
|
||||
.fee-advanced:not([open]) > summary { border-bottom: 0; }
|
||||
.fee-advanced .fee-detail-grid > section:only-child { grid-column: 1 / -1; }
|
||||
.adjustment-alert { margin-top: 12px; }
|
||||
.adjustment-form { margin-top: 18px; }
|
||||
.adjustment-amounts { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; }
|
||||
.adjustment-amounts :deep(.el-form-item__content) { flex-wrap: nowrap; }
|
||||
.adjustment-amounts { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 18px; }
|
||||
.adjustment-amounts :deep(.el-form-item) { min-width: 0; margin-bottom: 18px; }
|
||||
.adjustment-amounts :deep(.el-form-item__content) { display: grid; grid-template-columns: minmax(0, 1fr) auto; flex-wrap: nowrap; column-gap: 8px; }
|
||||
.adjustment-amounts :deep(.el-input-number) { width: 100%; }
|
||||
.adjustment-amounts :deep(.el-form-item__content > span) { margin-left: 8px; color: var(--cc-muted); font-size: 12px; }
|
||||
.adjustment-total { display: flex; align-items: baseline; justify-content: space-between; gap: 16px; margin: 0 0 18px; padding: 13px 14px; border-radius: 8px; background: #f8fafc; }
|
||||
.adjustment-unit { min-width: max-content; color: var(--cc-muted); font-size: 12px; white-space: nowrap; }
|
||||
.adjustment-total { display: grid; grid-template-columns: auto minmax(150px, 1fr) auto; align-items: baseline; gap: 18px; margin: 0 0 18px; padding: 13px 16px; border-radius: 8px; background: #f8fafc; }
|
||||
.adjustment-total span { color: var(--cc-muted); font-size: 12px; }
|
||||
.adjustment-total strong { font-size: 18px; }
|
||||
.adjustment-total strong { justify-self: center; font-size: 18px; white-space: nowrap; }
|
||||
.adjustment-total small { color: var(--cc-muted); font-size: 11px; text-align: right; }
|
||||
.sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; }
|
||||
|
||||
@media (max-width: 900px) {
|
||||
@@ -466,7 +475,7 @@ defineExpose({ reload: loadFee })
|
||||
.fee-heading { align-items: stretch; flex-direction: column; }
|
||||
.fee-heading-actions { justify-content: flex-start; }
|
||||
.fee-heading-actions .el-button:not(.is-circle) { flex: 1; min-height: 44px; }
|
||||
.fee-summary, .calculation-flow, .fee-detail-grid, .fee-timeline ol, .adjustment-amounts { grid-template-columns: 1fr; }
|
||||
.fee-summary, .calculation-flow, .fee-detail-grid, .fee-timeline ol { grid-template-columns: 1fr; }
|
||||
.fee-summary-item { border-top: 1px solid var(--cc-border); border-left: 0; }
|
||||
.calculation-flow > div { min-height: 76px; border-right: 0; border-bottom: 1px solid var(--cc-border); }
|
||||
.calculation-flow > div:last-child { border-bottom: 0; }
|
||||
@@ -475,6 +484,16 @@ defineExpose({ reload: loadFee })
|
||||
.fee-timeline ol { gap: 16px; }
|
||||
.fee-timeline li { padding-left: 20px; }
|
||||
.fee-timeline li::after { display: none; }
|
||||
.adjustment-amounts { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
||||
.adjustment-total { grid-template-columns: auto 1fr; }
|
||||
.adjustment-total strong { justify-self: end; }
|
||||
.adjustment-total small { grid-column: 1 / -1; text-align: left; }
|
||||
}
|
||||
|
||||
@media (max-width: 520px) {
|
||||
.adjustment-amounts { grid-template-columns: 1fr; gap: 0; }
|
||||
.adjustment-total { grid-template-columns: 1fr; gap: 6px; }
|
||||
.adjustment-total strong { justify-self: start; }
|
||||
}
|
||||
|
||||
@media (max-width: 360px) {
|
||||
|
||||
Reference in New Issue
Block a user