This commit is contained in:
wujinliang
2026-09-18 11:40:41 +08:00
parent e3cca80ff2
commit 8fbefcf657
5 changed files with 17 additions and 12 deletions
+9 -4
View File
@@ -144,6 +144,7 @@ export async function apiRequest<T>(config: AxiosRequestConfig) {
export async function apiBlobRequest(config: AxiosRequestConfig) { export async function apiBlobRequest(config: AxiosRequestConfig) {
try { try {
const response = await transport.request<Blob>({ ...config, responseType: 'blob' }) const response = await transport.request<Blob>({ ...config, responseType: 'blob' })
await throwBlobApiError(response.data, response.status)
return response.data return response.data
} catch (error) { } catch (error) {
await throwBlobApiError(error) await throwBlobApiError(error)
@@ -154,6 +155,7 @@ export async function apiBlobRequest(config: AxiosRequestConfig) {
export async function apiBlobResponseRequest(config: AxiosRequestConfig) { export async function apiBlobResponseRequest(config: AxiosRequestConfig) {
try { try {
const response = await transport.request<Blob>({ ...config, responseType: 'blob' }) const response = await transport.request<Blob>({ ...config, responseType: 'blob' })
await throwBlobApiError(response.data, response.status)
return { blob: response.data, headers: response.headers } return { blob: response.data, headers: response.headers }
} catch (error) { } catch (error) {
await throwBlobApiError(error) await throwBlobApiError(error)
@@ -161,16 +163,19 @@ export async function apiBlobResponseRequest(config: AxiosRequestConfig) {
} }
} }
async function throwBlobApiError(error: unknown) { async function throwBlobApiError(error: unknown, responseStatus?: number) {
if (!axios.isAxiosError(error) || !(error.response?.data instanceof Blob)) return const blob = error instanceof Blob
? error
: axios.isAxiosError(error) && error.response?.data instanceof Blob ? error.response.data : null
if (!blob || !blob.type.toLowerCase().includes('json')) return
try { try {
const body = JSON.parse(await error.response.data.text()) as unknown const body = JSON.parse(await blob.text()) as unknown
if (!isApiEnvelope(body)) return if (!isApiEnvelope(body)) return
const fieldErrors = asFieldErrors(body.data) const fieldErrors = asFieldErrors(body.data)
throw new ApiError( throw new ApiError(
apiErrorMessage(body.message, fieldErrors), apiErrorMessage(body.message, fieldErrors),
body.code, body.code,
error.response.status, responseStatus ?? (axios.isAxiosError(error) ? error.response?.status : undefined),
fieldErrors, fieldErrors,
typeof body.traceId === 'string' ? body.traceId : null, typeof body.traceId === 'string' ? body.traceId : null,
typeof body.requestId === 'string' ? body.requestId : null, typeof body.requestId === 'string' ? body.requestId : null,
@@ -6,6 +6,8 @@ const source = readFileSync(new URL('./InboundCodeAssociationDialog.vue', import
test('loads only the customer-scoped server association and fails closed', () => { test('loads only the customer-scoped server association and fails closed', () => {
assert.match(source, /getMerchantInboundCodeAssociation\(inboundCode\)/) assert.match(source, /getMerchantInboundCodeAssociation\(inboundCode\)/)
assert.match(source, /getFirstLegStatusLabel\(association\.orderStatus\)/)
assert.doesNotMatch(source, /FIRST_LEG_ORDER_STATUSES/)
assert.match(source, /association\.value = null/) assert.match(source, /association\.value = null/)
assert.match(source, /关联摘要加载失败/) assert.match(source, /关联摘要加载失败/)
assert.match(source, /@click="loadAssociation"/) assert.match(source, /@click="loadAssociation"/)
@@ -1,11 +1,11 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed, ref, watch } from 'vue' import { computed, ref, watch } from 'vue'
import { AlertTriangle, Link2, RefreshCw } from '@lucide/vue' import { AlertTriangle, Link2, RefreshCw } from '@lucide/vue'
import { FIRST_LEG_ORDER_STATUSES } from '@chongchong/shared'
import { import {
getMerchantInboundCodeAssociation, getMerchantInboundCodeAssociation,
type MerchantInboundCodeAssociation, type MerchantInboundCodeAssociation,
} from '@/modules/first-leg/api/inboundCodeAssociations' } from '@/modules/first-leg/api/inboundCodeAssociations'
import { getFirstLegStatusLabel } from '@/modules/first-leg/utils/firstLegOrder'
const props = defineProps<{ const props = defineProps<{
modelValue: boolean modelValue: boolean
@@ -30,10 +30,6 @@ function displayValue(value: string | null | undefined) {
return value?.trim() || '—' return value?.trim() || '—'
} }
function statusLabel(value: string) {
return FIRST_LEG_ORDER_STATUSES.find((item) => item.code === value)?.label ?? displayValue(value)
}
function destinationLabel(value: MerchantInboundCodeAssociation) { function destinationLabel(value: MerchantInboundCodeAssociation) {
return [value.destinationWarehouseCode, value.destinationWarehouseName].filter(Boolean).join(' · ') || '—' return [value.destinationWarehouseCode, value.destinationWarehouseName].filter(Boolean).join(' · ') || '—'
} }
@@ -87,7 +83,7 @@ watch(() => [props.modelValue, props.inboundCode] as const, ([open]) => {
</header> </header>
<dl class="inbound-association-grid"> <dl class="inbound-association-grid">
<div><dt>订单号</dt><dd>{{ displayValue(association.orderNo) }}</dd></div> <div><dt>订单号</dt><dd>{{ displayValue(association.orderNo) }}</dd></div>
<div><dt>订单状态</dt><dd>{{ statusLabel(association.orderStatus) }}</dd></div> <div><dt>订单状态</dt><dd>{{ getFirstLegStatusLabel(association.orderStatus) }}</dd></div>
<div><dt>客户名称</dt><dd>{{ displayValue(association.customerName) }}</dd></div> <div><dt>客户名称</dt><dd>{{ displayValue(association.customerName) }}</dd></div>
<div><dt>目的国家</dt><dd>{{ displayValue(association.destinationCountryCode) }}</dd></div> <div><dt>目的国家</dt><dd>{{ displayValue(association.destinationCountryCode) }}</dd></div>
<div class="is-wide"><dt>目的仓</dt><dd>{{ destinationLabel(association) }}</dd></div> <div class="is-wide"><dt>目的仓</dt><dd>{{ destinationLabel(association) }}</dd></div>
@@ -209,6 +209,7 @@ test('distinguishes first-leg draft, review, active handling and completion', ()
assert.equal(getFirstLegStatusTone('LOADED'), 'success') assert.equal(getFirstLegStatusTone('LOADED'), 'success')
assert.equal(getFirstLegStatusTone('DELIVERED'), 'success') assert.equal(getFirstLegStatusTone('DELIVERED'), 'success')
assert.equal(getFirstLegStatusTone('COMPLETED'), 'success') assert.equal(getFirstLegStatusTone('COMPLETED'), 'success')
assert.equal(getFirstLegStatusLabel('SUBMITTED'), '\u5f85\u5ba1\u6838')
assert.equal(getFirstLegStatusLabel('COMPLETED'), '已完成') assert.equal(getFirstLegStatusLabel('COMPLETED'), '已完成')
assert.equal(getFirstLegStatusLabel('UNRECOGNIZED'), '未知状态') assert.equal(getFirstLegStatusLabel('UNRECOGNIZED'), '未知状态')
assert.equal(getFirstLegStatusTone('UNRECOGNIZED'), 'info') assert.equal(getFirstLegStatusTone('UNRECOGNIZED'), 'info')
+3 -2
View File
@@ -14,8 +14,9 @@ const transportModeLabels = new Map<FirstLegTransportMode, string>(
FIRST_LEG_TRANSPORT_MODES.map((item) => [item.code, item.label]), FIRST_LEG_TRANSPORT_MODES.map((item) => [item.code, item.label]),
) )
export function getFirstLegStatusLabel(status: FirstLegOrderStatus) { export function getFirstLegStatusLabel(status: string) {
return statusLabels.get(status) ?? '未知状态' const canonicalStatus = status === 'SUBMITTED' ? 'PENDING_REVIEW' : status
return statusLabels.get(canonicalStatus as FirstLegOrderStatus) ?? '未知状态'
} }
export function getFirstLegTransportModeLabel(mode: FirstLegTransportMode | null) { export function getFirstLegTransportModeLabel(mode: FirstLegTransportMode | null) {