优化
This commit is contained in:
@@ -51,7 +51,7 @@
|
||||
| 装柜完成后的退税报关资料 | `src/modules/first-leg/views/FirstLegOrdersView.vue` 的“报关资料”、`components/CustomsDocumentsDialog.vue`、`api/customs.ts` | 只显示登录客户在同柜的退税入仓码;报关开始前可上传、替换、调整同一资料覆盖的一个或多个入仓码及删除误传,全部写入携带幂等键和版本;开始后只读冻结资料,整柜完成后才能下载平台回传的一对一完成文件。Backend `firstleg/customs/container/`、`customs-v1.yaml`,二进制归 `file/` |
|
||||
| 统一订单展示 | `src/modules/order-management/` | `src/modules/first-leg/` 与对应后端订单契约 |
|
||||
| 客户财务账单、入仓码联查、收款、未收余额与客户确认 | `src/modules/finance/`;确认与明细入口 `views/FinanceBillsView.vue`、`api/finance.ts`,入仓码通过 `src/modules/first-leg/public.ts` 复用 `InboundCodeAssociationDialog` | Backend `finance/`;账单行只显示服务端 `inboundCode` 并按登录客户查询业务关系,缺失显示“—”,不得回退内部 ID。收款方式由服务端枚举映射中文。出账或已出账调整后为 `PENDING_CONFIRMATION`,客户确认提交登录成员、`expectedVersion` 和幂等键 |
|
||||
| 客户资金账户与充值申请 | `src/modules/finance/views/FinanceAccountView.vue`、`api/account.ts` | 显示当前企业 CNY 实际、冻结、可用余额和不可变流水;充值只展示服务端启用的微信/支付宝静态收款码,客户上传 JPG/PNG 付款截图后进入待审核。客户范围只取登录态,无收款码时禁止提交,不模拟支付成功 |
|
||||
| 客户资金账户、授信与充值申请 | `src/modules/finance/views/FinanceAccountView.vue`、`api/account.ts` | 显示当前企业 CNY 实际余额、冻结金额、授信额度、已用授信、冻结授信、总可用余额和不可变流水;数据加载前显示“—”,不得先伪装为零。充值只展示服务端启用的微信/支付宝静态收款码,客户上传 JPG/PNG 付款截图后进入待审核。客户范围只取登录态,无收款码时禁止提交,不模拟支付成功 |
|
||||
| 海外采购、美国本土卡与美本土散板 | `src/modules/cross-border/routes.ts`、`components/BusinessOrdersView.vue`、`api/businessOrders.ts` | 三个路由均为 `active`,客户范围只取 Token;Backend `lastleg-service/` 的 `procurement/`、`trucking/`、`looseboard/` 客户接口 |
|
||||
| 海外仓入库订单 | `src/modules/order-management/routes.ts`、`views/InboundOrdersView.vue`、`api/inboundPlans.ts` | `orders/inbound` 为只读列表/详情,权限 `merchant:warehouse:inbound:list`;不展示或提交内部 ID |
|
||||
| 海外仓贴标、仓储转运、货物废弃、退货换标 | `src/modules/overseas-warehouse/routes.ts`、`views/WarehouseServiceView.vue`、`api/warehouseServices.ts` | 四个路由均为 `active`;Backend `lastleg-service/` 的 `relabel/`、`transfer/`、`discard/`,请求统一经 `src/api/overseasHttp.ts` 与 `gateway-overseas/` |
|
||||
|
||||
+11
-2
@@ -39,14 +39,23 @@ export async function overseasApiRequest<T>(config: AxiosRequestConfig) {
|
||||
try {
|
||||
const response = await transport.request<ApiEnvelope<T>>(config)
|
||||
if (!isApiEnvelope(response.data) || response.data.code !== '200') {
|
||||
throw new ApiError(isApiEnvelope(response.data) ? response.data.message : '海外接口响应格式错误', isApiEnvelope(response.data) ? response.data.code : 'INVALID_RESPONSE')
|
||||
throw new ApiError(
|
||||
isApiEnvelope(response.data) ? response.data.message : '海外接口响应格式错误',
|
||||
isApiEnvelope(response.data) ? response.data.code : 'INVALID_RESPONSE',
|
||||
undefined,
|
||||
undefined,
|
||||
isApiEnvelope(response.data) ? response.data.traceId : null,
|
||||
isApiEnvelope(response.data) ? response.data.requestId : null,
|
||||
)
|
||||
}
|
||||
return response.data.data
|
||||
} catch (error) {
|
||||
if (error instanceof ApiError) throw error
|
||||
if (axios.isAxiosError(error)) {
|
||||
const body = error.response?.data
|
||||
if (isApiEnvelope(body)) throw new ApiError(body.message, body.code, error.response?.status)
|
||||
if (isApiEnvelope(body)) {
|
||||
throw new ApiError(body.message, body.code, error.response?.status, undefined, body.traceId, body.requestId)
|
||||
}
|
||||
throw new ApiError(error.code === 'ERR_NETWORK' ? '无法连接海外服务' : error.message, error.code || 'OVERSEAS_HTTP_ERROR', error.response?.status)
|
||||
}
|
||||
throw new ApiError(error instanceof Error ? error.message : '海外接口请求失败')
|
||||
|
||||
@@ -17,8 +17,8 @@ export interface InboundPlanSummary {
|
||||
planNo: string
|
||||
inboundCode: string
|
||||
orderType: string | null
|
||||
warehouseCode: string | null
|
||||
warehouseName: string | null
|
||||
overseasWarehouseCode: string | null
|
||||
overseasWarehouseName: string | null
|
||||
fbaWarehouseCode: string | null
|
||||
packageCount: number
|
||||
pickedPackageCount: number
|
||||
@@ -52,6 +52,8 @@ export interface InboundPlanPallet {
|
||||
export interface InboundPlanDetail {
|
||||
planNo: string
|
||||
containerNo: string | null
|
||||
overseasWarehouseCode: string | null
|
||||
overseasWarehouseName: string | null
|
||||
status: string
|
||||
lineCount: number
|
||||
palletCount: number
|
||||
|
||||
@@ -5,6 +5,7 @@ import test from 'node:test'
|
||||
const api = readFileSync(new URL('./api/inboundPlans.ts', import.meta.url), 'utf8')
|
||||
const routes = readFileSync(new URL('./routes.ts', import.meta.url), 'utf8')
|
||||
const view = readFileSync(new URL('./views/InboundOrdersView.vue', import.meta.url), 'utf8')
|
||||
const overseasHttp = readFileSync(new URL('../../api/overseasHttp.ts', import.meta.url), 'utf8')
|
||||
|
||||
test('activates the customer-scoped overseas inbound list and detail', () => {
|
||||
assert.match(routes, /path: 'orders\/inbound'[\s\S]*?release: 'active'[\s\S]*?permission: 'merchant:warehouse:inbound:list'/)
|
||||
@@ -23,3 +24,21 @@ test('keeps actions first and exposes filters loading errors empty state paginat
|
||||
assert.match(view, /detail\.planNo/)
|
||||
assert.doesNotMatch(view, /planId|orderId|warehouseId|customerId/)
|
||||
})
|
||||
|
||||
test('shows only the server-provided overseas receiving warehouse code and name', () => {
|
||||
assert.match(api, /overseasWarehouseCode: string \| null/)
|
||||
assert.match(api, /overseasWarehouseName: string \| null/)
|
||||
assert.doesNotMatch(api, /overseasWarehouseId/)
|
||||
assert.match(view, /label="海外接柜仓"/)
|
||||
assert.match(view, /overseasWarehouseLabel\(scope\.row\.overseasWarehouseCode, scope\.row\.overseasWarehouseName\)/)
|
||||
assert.match(view, /overseasWarehouseLabel\(detail\.overseasWarehouseCode, detail\.overseasWarehouseName\)/)
|
||||
assert.match(view, /if \(typeof code !== 'string' \|\| !code\.trim\(\) \|\| typeof name !== 'string' \|\| !name\.trim\(\)\) return '—'/)
|
||||
assert.doesNotMatch(view, /warehouseName \|\| warehouseCode|destinationWarehouse(?:Id|Code|Name).*overseasWarehouse/i)
|
||||
})
|
||||
|
||||
test('preserves overseas API diagnostic metadata without rendering it as a business identifier', () => {
|
||||
assert.match(overseasHttp, /response\.data\.traceId/)
|
||||
assert.match(overseasHttp, /response\.data\.requestId/)
|
||||
assert.match(overseasHttp, /body\.traceId, body\.requestId/)
|
||||
assert.doesNotMatch(view, /requestId|traceId/)
|
||||
})
|
||||
|
||||
@@ -14,6 +14,10 @@ const query = reactive({
|
||||
})
|
||||
|
||||
function text(value: unknown) { return value === null || value === undefined || value === '' ? '—' : String(value) }
|
||||
function overseasWarehouseLabel(code: unknown, name: unknown) {
|
||||
if (typeof code !== 'string' || !code.trim() || typeof name !== 'string' || !name.trim()) return '—'
|
||||
return `${code.trim()} · ${name.trim()}`
|
||||
}
|
||||
function dateTime(value: unknown) { return value ? new Date(String(value)).toLocaleString('zh-CN', { hour12: false }) : '—' }
|
||||
function statusLabel(value: unknown) {
|
||||
return ({ PENDING: '待处理', PROCESSING: '处理中', COMPLETED: '已完成', CANCELLED: '已取消', UNAPPOINTED: '未预约', SUCCESS: '预约成功', FAILED: '预约失败', SCHEDULED: '已排车', UNSCHEDULED: '未排车' } as Record<string, string>)[String(value)] || text(value)
|
||||
@@ -82,7 +86,7 @@ onMounted(() => void load())
|
||||
<el-table-column label="操作" width="90" fixed="left"><template #default="scope"><el-button link type="primary" @click="openDetail(scope.row)">查看</el-button></template></el-table-column>
|
||||
<el-table-column prop="planNo" label="计划号" min-width="180" show-overflow-tooltip />
|
||||
<el-table-column prop="inboundCode" label="入仓码" min-width="170" show-overflow-tooltip />
|
||||
<el-table-column label="海外仓" min-width="160" show-overflow-tooltip><template #default="scope">{{ text(scope.row.warehouseName || scope.row.warehouseCode) }}</template></el-table-column>
|
||||
<el-table-column label="海外接柜仓" min-width="220" show-overflow-tooltip><template #default="scope">{{ overseasWarehouseLabel(scope.row.overseasWarehouseCode, scope.row.overseasWarehouseName) }}</template></el-table-column>
|
||||
<el-table-column label="目的仓" min-width="150" show-overflow-tooltip><template #default="scope">{{ text(scope.row.fbaWarehouseCode) }}</template></el-table-column>
|
||||
<el-table-column prop="packageCount" label="计划箱数" width="100" />
|
||||
<el-table-column prop="pickedPackageCount" label="已拣箱数" width="100" />
|
||||
@@ -100,6 +104,7 @@ onMounted(() => void load())
|
||||
<el-card shadow="never">
|
||||
<dl class="detail-grid">
|
||||
<div><dt>计划号</dt><dd>{{ detail.planNo }}</dd></div><div><dt>集装箱号</dt><dd>{{ text(detail.containerNo) }}</dd></div>
|
||||
<div><dt>海外接柜仓</dt><dd>{{ overseasWarehouseLabel(detail.overseasWarehouseCode, detail.overseasWarehouseName) }}</dd></div>
|
||||
<div><dt>状态</dt><dd>{{ statusLabel(detail.status) }}</dd></div><div><dt>入仓码数</dt><dd>{{ detail.lineCount }}</dd></div>
|
||||
<div><dt>托盘数</dt><dd>{{ detail.palletCount }}</dd></div><div><dt>总体积(m³)</dt><dd>{{ detail.totalVolumeCbm }}</dd></div>
|
||||
<div><dt>计划箱数</dt><dd>{{ detail.expectedPackageCount }}</dd></div><div><dt>已拣箱数</dt><dd>{{ detail.pickedPackageCount }}</dd></div>
|
||||
|
||||
Reference in New Issue
Block a user