diff --git a/AGENTS.md b/AGENTS.md index da6bea4..71eb0a5 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -53,7 +53,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`;退货预报另见本节独立入口;贴标申请先选国外中转仓和有库存历史入仓单,换标类型为只换 SKU、只换箱唛、SKU+箱唛。涉及箱唛换标时,整张贴标申请只选择一个 FBA 目的仓,全部箱级明细统一提交该目的仓,模板和明细不再逐行选择目的仓。箱唛换标明细一行固定代表一箱,箱数不可编辑,提交也固定为 1。货物明细外层始终提供“SKU 关联”“箱号关联”“SKU 混箱换标”三套模板的下载、上传,不随换标类型切换;上传只进入待导入关系区,客户可勾选部分或全选后导入本次明细,只有进入明细的库存才随申请提交和冻结。明细也支持手动新增编辑,组合换标以仓库唛头连接旧/新 SKU 和新 FBA 箱唛关系。涉及箱唛换标时必须选择卡车、自提或快递;快递可选择打印面单并上传 PDF/图片。PDA 逐箱换标、上传照片并由仓库复核后,客户详情展示完工照片和“换标完成确认”;客户确认前不得继续出库,确认后卡车进入预排车,自提/快递进入换标出库待创建。历史库存位置不能解释成箱唛与具体托盘快照,客户页面不暴露托盘、库位和内部库存 ID | diff --git a/Jenkinsfile b/Jenkinsfile index 8cc4b3f..88d2dda 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,12 +1,15 @@ -def sendProductionWeComNotification(String status, String color, String detail) { +def sendProductionWeComNotification(String title, String status, String color, String detail) { withCredentials([string(credentialsId: 'wecom-prod-webhook', variable: 'WECOM_WEBHOOK')]) { - String sourceVersion = env.SOURCE_TAG ?: env.GIT_COMMIT ?: '—' - String content = """## 生产部署通知 -> 项目:${env.JOB_NAME} + String sourceVersion = env.SOURCE_TAG ?: env.GIT_COMMIT ?: '未解析' + String duration = currentBuild.durationString?.replace(' and counting', '') ?: '—' + String content = """## ${title} +> 应用:客户工作台 +> Job:${env.JOB_NAME} > 构建:[#${env.BUILD_NUMBER}](${env.BUILD_URL}) > 提交:${sourceVersion} > 状态:${status} -> 详情:${detail} +${detail} +> 耗时:${duration} > 时间:${new Date().format('yyyy-MM-dd HH:mm:ss', TimeZone.getTimeZone('Asia/Shanghai'))} """ String payloadFile = 'wecom-production-notification.json' @@ -123,13 +126,6 @@ pipeline { } } - stage('确认部署生产前端') { - when { expression { env.IS_PRODUCTION == 'true' } } - steps { - input message: "确认将 main 提交 ${env.SOURCE_TAG} 部署到 app.buggoglobal.com?", ok: '确认部署' - } - } - stage('部署生产前端') { when { expression { env.IS_PRODUCTION == 'true' } } steps { @@ -199,7 +195,12 @@ pipeline { script { if (env.JOB_BASE_NAME?.endsWith('-prod')) { try { - sendProductionWeComNotification('部署成功', 'info', 'Client 生产前端已完成发布和部署检查') + sendProductionWeComNotification( + '✅ 生产部署成功', + '成功', + 'info', + '> 结果:Client 生产前端已完成发布和部署流程' + ) } catch (Exception error) { echo "企业微信通知失败,不影响原构建结果:${error.message}" } @@ -219,7 +220,12 @@ pipeline { script { if (env.JOB_BASE_NAME?.endsWith('-prod')) { try { - sendProductionWeComNotification('部署失败', 'warning', '请打开 Jenkins 构建日志查看失败阶段') + sendProductionWeComNotification( + '❌ 生产发布失败', + '失败', + 'warning', + '> 说明:请打开 Jenkins 构建日志查看失败阶段' + ) } catch (Exception error) { echo "企业微信通知失败,不影响原构建结果:${error.message}" } @@ -230,7 +236,12 @@ pipeline { script { if (env.JOB_BASE_NAME?.endsWith('-prod')) { try { - sendProductionWeComNotification('部署已取消', 'comment', '生产部署流程被人工取消或中止') + sendProductionWeComNotification( + '⏹ 生产部署已取消', + '已取消', + 'comment', + '> 说明:生产部署流程被人工取消或中止' + ) } catch (Exception error) { echo "企业微信通知失败,不影响原构建结果:${error.message}" } diff --git a/public/customer-recharge-qr.png b/public/customer-recharge-qr.png new file mode 100644 index 0000000..8bcdde6 Binary files /dev/null and b/public/customer-recharge-qr.png differ diff --git a/src/components/ClientSidebarMenu.vue b/src/components/ClientSidebarMenu.vue index 9fb3e6e..4c66e7f 100644 --- a/src/components/ClientSidebarMenu.vue +++ b/src/components/ClientSidebarMenu.vue @@ -22,8 +22,6 @@ const activeParentKeys = computed(() => new Set(getNavigationParentKeys(visibleN const openedKeys = ref>(new Set(activeParentKeys.value)) function handleToggle(key: string) { - if (activeParentKeys.value.has(key)) return - const next = new Set(openedKeys.value) if (next.has(key)) next.delete(key) else next.add(key) diff --git a/src/components/common/DataTable.test.mjs b/src/components/common/DataTable.test.mjs new file mode 100644 index 0000000..4466aa9 --- /dev/null +++ b/src/components/common/DataTable.test.mjs @@ -0,0 +1,11 @@ +import assert from 'node:assert/strict' +import { readFileSync } from 'node:fs' +import test from 'node:test' + +const source = readFileSync(new URL('./DataTable.vue', import.meta.url), 'utf8') + +test('keeps action-only table toolbars on the left', () => { + assert.match(source, /'data-table__toolbar--actions-only': !\$slots\.toolbar/) + assert.match(source, /v-if="\$slots\.toolbar" class="data-table__toolbar-heading"/) + assert.match(source, /\.data-table__toolbar--actions-only \{ justify-content: flex-start; \}/) +}) diff --git a/src/components/common/DataTable.vue b/src/components/common/DataTable.vue index 815b751..3ef382c 100644 --- a/src/components/common/DataTable.vue +++ b/src/components/common/DataTable.vue @@ -143,8 +143,8 @@ function setPage(value: number) {