diff --git a/AGENTS.md b/AGENTS.md index 87e05ae..848a593 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -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;采购创建的 `customerName` 只取当前认证会话企业名称,不允许自由输入或冒用其他客户。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`;换标申请必须提交正数 CNY `chargeAmount`,页面展示服务端钱包冻结/结算/释放状态及错误,不本地推导余额结果。Backend `lastleg-service/` 的 `relabel/`、`transfer/`、`discard/`,请求统一经 `src/api/overseasHttp.ts` 与 `gateway-overseas/` | diff --git a/src/api/overseasHttp.ts b/src/api/overseasHttp.ts index cfcaadc..72e4a1c 100644 --- a/src/api/overseasHttp.ts +++ b/src/api/overseasHttp.ts @@ -39,14 +39,23 @@ export async function overseasApiRequest(config: AxiosRequestConfig) { try { const response = await transport.request>(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 : '海外接口请求失败') diff --git a/src/components.d.ts b/src/components.d.ts index 723bdea..eb92a23 100644 --- a/src/components.d.ts +++ b/src/components.d.ts @@ -54,6 +54,7 @@ declare module 'vue' { QueryToolbar: typeof import('./components/common/QueryToolbar.vue')['default'] RouterLink: typeof import('vue-router')['RouterLink'] RouterView: typeof import('vue-router')['RouterView'] + SingleLineText: typeof import('./components/common/SingleLineText.vue')['default'] WorkspaceTabs: typeof import('./components/WorkspaceTabs.vue')['default'] } export interface GlobalDirectives { diff --git a/src/components/common/DataTable.vue b/src/components/common/DataTable.vue index 6965929..6a6016f 100644 --- a/src/components/common/DataTable.vue +++ b/src/components/common/DataTable.vue @@ -7,6 +7,9 @@ export interface DataTableColumn { columnKey?: string slotName?: string headerSlotName?: string + singleLine?: boolean + showOverflowTooltip?: boolean + className?: string [attribute: string]: unknown } @@ -88,10 +91,34 @@ watch([page, pageCount], ([value, count]) => { if (value !== nextValue) page.value = nextValue }, { immediate: true }) -function columnAttrs({ key, slotName, headerSlotName, ...attrs }: DataTableColumn) { +function columnAttrs({ key, slotName, headerSlotName, singleLine, ...attrs }: DataTableColumn) { return attrs } +function systemColumn(column: DataTableColumn) { + return column.type === 'selection' + || column.type === 'index' + || column.type === 'expand' + || column.slotName === 'actions' + || column.label === '操作' +} + +function singleLineColumn(column: DataTableColumn) { + return column.singleLine !== false && !systemColumn(column) +} + +function columnOverflowTooltip(column: DataTableColumn) { + return singleLineColumn(column) ? column.showOverflowTooltip ?? true : column.showOverflowTooltip +} + +function columnClassName(column: DataTableColumn) { + return [ + column.className, + systemColumn(column) ? 'data-table__system-cell' : '', + singleLineColumn(column) ? 'data-table__single-line-cell' : '', + ].filter(Boolean).join(' ') +} + function columnRenderKey(column: DataTableColumn, index: number) { return String(column.key ?? column.columnKey ?? column.prop ?? column.property ?? index) } @@ -119,6 +146,8 @@ function setPage(value: number) { v-for="(column, index) in columns" :key="columnRenderKey(column, index)" v-bind="columnAttrs(column)" + :class-name="columnClassName(column)" + :show-overflow-tooltip="columnOverflowTooltip(column)" > @@ -239,19 +255,19 @@ async function openDetail(value: Record) { - + - + - - + +

货物明细

添加行清空

模板每行代表一箱;上传后直接回显到下方明细。明细目的仓优先,留空时使用顶部统一目的海外仓。

-
商品 SKU *中文品名 *可用箱数{{ props.service === 'labeling' ? 'FBA 货件编号 *' : 'FNSKU / 箱唛' }}换标新 SKU *{{ props.service === 'disposal' ? '本次废弃箱数' : props.service === 'relabel' ? '退件数量' : '箱数' }} *操作
{{ row.availableQuantity }} 箱
{{ props.service === 'disposal' ? '请先选择入库单,系统将自动带出 SKU 和可用箱数' : '暂无货物明细,请点击“添加行”' }}
+
商品 SKU *中文品名 *可用箱数{{ props.service === 'labeling' ? 'FBA 货件编号 *' : 'FNSKU / 箱唛' }}换标新 SKU *{{ props.service === 'disposal' ? '本次废弃箱数' : props.service === 'relabel' ? '退件数量' : '箱数' }} *操作
{{ row.availableQuantity }} 箱
{{ props.service === 'disposal' ? '请先选择入库单,系统将自动带出 SKU 和可用箱数' : '暂无货物明细,请点击“添加行”' }}
重置提交{{ config.title }}
@@ -260,7 +276,7 @@ async function openDetail(value: Record) {