Test #1

Merged
admin merged 256 commits from test into main 2026-09-28 16:01:16 +08:00
58 changed files with 36816 additions and 0 deletions
Showing only changes of commit 84598f98eb - Show all commits
+1
View File
@@ -14,6 +14,7 @@
- [二期产品待确认事项(发产品填写)](二期产品待确认事项.md)
- [二期开发进度表](二期开发进度表.md)
- [二期开发进度表(Excel)](虫虫出海二期开发进度表.xlsx)
- [二期前端页面清单与多对话并行任务表](二期前端页面清单与多对话并行任务表.md)
- [二期需求确认清单:产品与业务确认](二期需求确认清单.md)
- [二期需求确认清单(会议填写 Excel)](二期需求确认清单.xlsx)
- [规划依据:头程物流流程图](参考资料/头程物流流程图.svg)
@@ -0,0 +1,228 @@
# 二期前端页面清单与多对话并行任务表
> 盘点日期:2026-08-04
> 适用项目:`apps/admin-web/`、`apps/client-web/`
> 后端范围:只读核对 `baiyue-firstleg/`,本任务不修改后端
> 当前基线:以后端现有 Controller、DTO、权限和状态机为可联调事实;V2 待确认项不擅自补成前端规则
本轮不存在“等待后端提供接口文档”的前置任务。每个前端对话直接只读当前工作区 `baiyue-firstleg/` 中的 Controller、DTO、Service、Repository、OpenAPI 和权限 SQL;发现契约问题只登记,不修改后端。
## 一、结论
二期前端应围绕已经落地的“入库测量、OCR、测量审核、FBA 计费、托盘库位、客户只读结果”建设,不继续沿用旧 24 项计划直接开工。
- Admin 新增 5 个正式菜单页面:入库单、客户计费规则、船司 FBA 价格、库位管理、托盘管理。
- 入库详情承载测量/OCR、审核、费用快照、托盘库位结果和历史,不再新增独立“接收审核”页面。
- 正常流程由“测量审核通过”触发后端自动计费、发布费用并完成入库;前端不把 `calculate → confirm → send` 串成常规人工流程。
- Client 不新增独立入库菜单或列表,只在现有头程订单详情中增加“入库与费用”只读区。
- 到仓预报、预约、签到、分批收货、标签、冲正、差异通知、上架任务、位置历史和通用费用项目当前没有匹配的公开后端接口,暂不拆前端实现任务。
## 二、事实来源和冲突处理
当前 V2 确认清单的 C1-C8、T1-T3 仍是待确认状态,但后端已经形成一套更收敛的可联调实现。因此本轮按以下顺序执行:
1. 用户本轮要求:只整理和开发前端,不修改后端,可以直接只读后端代码。
2. 后端真实 Controller、DTO、权限 SQL、状态枚举和服务编排。
3. `二期需求优化版-v2-测量计费入库主流程.md` 的范围边界。
4. Admin 原型仅用于布局、字段和交互参考,不补造后端没有的业务能力。
已确认的实现差异:
| 主题 | 旧规划或 V2 描述 | 当前后端事实 | 前端处理 |
|---|---|---|---|
| 正常入库动作 | 人工试算、确认、发送费用后入库 | 测量审核通过后自动计费、发布费用并完成入库 | 审核成功后刷新详情和费用,不串行调用三个计费接口 |
| 托盘库位 | 操作员选择托盘和候选库位 | 测量提交/审核编排自动准备和占用,平台无人工分配公开接口 | 详情只读展示分配结果 |
| 接收审核 | 独立页面 | 无独立节点 | 不建设;相关结果并入详情和审核区 |
| Client 到仓预报 | 规划中存在 | 无商户公开接口 | 不建设 |
| 通用费用项目 | V2 C6 白名单待确认 | 无独立费用项目 Controller | 不建设费用项目页 |
| 入库列表路径 | V2 示例使用 `/api/platform/inbounds` | Controller 确实无 `v1`,其他入库接口多为 `/api/platform/v1/...` | API 封装保留真实多前缀,不自行统一 |
## 三、Admin 页面和变更清单
### 3.1 正式菜单页面
| 页面 | 建议路由 | 主要内容 | 主要权限 | 优先级 |
|---|---|---|---|---|
| 入库单 | `/inbounds` | 企业选择、四个后端页签、多条件筛选、服务端分页、详情入口 | `platform:firstleg:inbound:list` | P0 |
| 入库详情 | `/inbounds/:inboundId` | 概览、运输快照、内部备注、文件、状态历史、测量、审核、费用、托盘库位 | `platform:firstleg:inbound:view` 及各操作权限 | P0 |
| 客户计费规则 | `/first-leg-pricing/customer-rules` | 规则查询、详情、新版本、停用;KG/CBM、换算系数、舍入和有效期 | `firstleg:fba-pricing:rule:view/edit` | P0 |
| 船司 FBA 价格 | `/first-leg-pricing/carrier-rates` | 价格查询、详情、新版本、停用;起运仓、渠道、船司、FBA 仓、单价和附加费 | `firstleg:fba-pricing:rate:view/edit` | P0 |
| 库位管理 | `/master-data/locations` | 企业和仓库联动、筛选、分页、新增、编辑、启停、删除、可用库位状态 | `platform:location:*` | P0 |
| 托盘管理 | `/master-data/pallets` | 企业和仓库联动、筛选、分页、新增、编辑、删除 | `platform:pallet:*` | P0 |
所有业务表格的“操作”列放在第一列,并检查窄屏退化。
### 3.2 入库详情内部功能
入库详情建议作为一个路由页,以页签或分区承载以下能力,不为每一步增加侧边栏菜单:
| 分区 | 能力 | 交互边界 |
|---|---|---|
| 概览与历史 | 入仓码、订单/客户/仓库快照、申报与实测汇总、文件、内部备注、状态历史 | 切换企业后清空全部上下文 |
| 测量与 OCR | 第三方箱规、测量草稿、称重图片上传/预览/OCR 重试、OCR 原值、置信度、人工修正、确认、提交审核 | 汇总重量和体积以服务端结果为准;不覆盖 OCR 原值 |
| 测量审核 | 原单、第三方测量、OCR 三组数据对比;通过、驳回、审核历史、异常放行 | 通过/驳回/放行携带幂等键;放行必须有专属权限和原因 |
| 费用快照 | 最新费用、规则版本、价格版本、实际量、折算量、计费量、单价、附加费、总额、发送状态 | 正常流程只读;已发布费用可按权限调整且必须填写原因 |
| 托盘库位 | 托盘号、分配体积/箱数、库位编码和占用结果 | 只读,不让前端拼托盘号、选择或占用库位 |
### 3.3 全局装配变更
- `src/app/router/index.ts`:装配二期模块路由。
- `src/core/navigation/adminNavigation.ts`:在“国内仓”下增加入库单、库位、托盘;新增“头程计费”分组。
- 路由和导航测试同步更新。
- IAM 权限页面无需业务改造;后端返回二期权限资源后沿用现有展示和授权能力。
- 不恢复“接收审核”“费用项目”“完整财务管理”等原型菜单。
## 四、Client 页面变更清单
Client 只修改现有 `FirstLegOrdersView.vue` 的订单详情抽屉,新增“入库与费用”只读区,不改路由、导航或模块注册。
展示内容:
- 入库结果:入仓码、入库状态、完成时间、平台收货仓。
- 实测汇总:实际箱数、实重、净重、实测体积、材积重、计费重量/体积。
- 托盘库位:托盘数、托盘号、分配体积、箱数和库位编码。
- 费用结果:计费模式、换算系数、实际量、折算量、最终计费量、单价、小计、附加费、总额、币种、规则/价格版本和发送时间。
- 只展示公开备注;不展示内部备注、原始操作历史、内部原因和操作人 ID。
状态处理:
- 草稿订单不请求入库费用接口,显示“订单提交后可查看入库结果”。
- 后端业务码 `A0404` 作为中性待处理态,显示“入库及费用结果尚未发布”。
- 其他失败只影响此区块,保留订单详情并提供区块级重试。
- 快速切换订单时使用请求序号或 `orderId` 校验,旧响应不得覆盖新订单。
- 不增加客户确认、调整、审核、发送费用或照片下载动作。
## 五、真实接口域
前端实现直接读取相邻 Controller/DTO,不需要另行准备后端接口文档。首批任务按以下接口域联调:
| 接口域 | Controller 入口 | 前端用途 |
|---|---|---|
| 入库查询 | `firstleg/.../PlatformInboundOrderController.java` | Admin 入库列表和聚合详情 |
| 测量/OCR | `firstleg/.../PlatformInboundMeasurementController.java` | 测量草稿、提交、OCR 登记/修正/确认 |
| 测量审核 | `firstleg/.../PlatformInboundMeasurementReviewController.java` | 通过、驳回、异常放行、审核历史 |
| FBA 基础资料 | `firstleg/.../PlatformFbaMasterDataController.java` | 客户规则和船司价格版本维护 |
| FBA 费用 | `firstleg/.../PlatformFbaPricingController.java` | 最新费用、异常恢复接口、已发布费用调整 |
| 称重图片 | `file/.../PlatformMeasurementImageController.java` | 上传、OCR 重试和鉴权查看 |
| 托盘 | `warehouse/.../PlatformPalletController.java` | 托盘 CRUD |
| 库位 | `warehouse/.../PlatformLocationController.java` | 库位 CRUD、状态和可用库位查询 |
| 商户结果 | `firstleg/.../MerchantInboundBillingController.java` | Client 订单入库与费用只读展示 |
共同约定:
- 浏览器代码使用现有 `/api` 基地址,不硬编码 Gateway 地址。
- 普通业务 HTTP 200 后仍需判断响应体 `code === "200"`。
- Java Long 在 JSON 中按字符串处理,前端不得转为 `Number`。
- Admin 当前接口显式需要目标 `tenantId`;切换企业必须清空仓库、详情、编辑、上传和幂等上下文。
- 409 表示版本或并发冲突,页面应刷新最新详情,不静默覆盖。
- 审核通过、驳回、异常放行和费用写操作按 Controller 要求携带 `Idempotency-Key`。
## 六、多对话并行任务表
### 6.0 实施状态(2026-08-04)
- `P2-ADM-01` 至 `P2-ADM-11`、`P2-CLI-01`、`P2-QA-01`、`P2-QA-02` 均已完成。
- Admin 已通过 `pnpm test`(58 个文件、262 个测试)、`pnpm typecheck` 和 `pnpm build`。
- Client 已通过 `pnpm test`(135 个测试)、`pnpm typecheck` 和 `pnpm build`。
- Admin 二期新增模块的 Impeccable detector 结果为 `[]`,模块依赖边界测试已通过。
- Admin 二期页面已再次按平台后台原型校正搜索条件、表格字段、字段顺序和详情分区;视觉组件统一复用现有 `QueryToolbar`、`DataTable`、`AppDrawer` 及状态组件。
- 原型字段只在当前后端 Controller/DTO 支持时落地;未恢复接收审核、客户确认、报关、智能装柜、托盘状态/启停、尺寸/承重及无接口的导入导出。
- Client 入库费用区沿用既有订单详情视觉,补充换算系数、舍入精度和规则/价格版本,保持只读并继续使用“入仓码”业务术语。
- 本轮未修改 `baiyue-firstleg/`;8.1 节的 Client 服务端响应数据暴露风险仍是上线阻断,不影响本轮前端任务完成状态。
### 6.1 任务定义
下表中未写应用前缀的 Admin 路径均相对 `apps/admin-web/src/`。
| ID | 对话任务 | 负责范围和文件所有权 | 交付与验收 | 依赖 | 波次 |
|---|---|---|---|---|---|
| `P2-ADM-01` | 入库列表与聚合查询 | 独占 `modules/inbounds/core/**`、`pages/InboundOrdersView.vue` 及测试 | 本域 types/API;企业选择、四页签、筛选、分页、操作列第一列、概览/历史只读组件、加载/空/错/权限态 | 只读入库查询 Controller/DTO | W1 |
| `P2-ADM-02` | 测量草稿与箱规 | 独占 `modules/inbounds/measurement/**` 及测试 | 本域 types/API/组件;第三方箱规、测量草稿、保存、提交审核、版本冲突和未保存保护 | 只读测量 Controller/DTO | W1 |
| `P2-ADM-03` | 称重图片与 OCR | 独占 `modules/inbounds/weight/**` 及测试 | 本域 types/API/组件;上传、预览、OCR 重试、原值/置信度、人工修正和确认 | 只读 File、测量 Controller/DTO | W1 |
| `P2-ADM-04` | 测量审核 | 独占 `modules/inbounds/review/**` 及测试 | 本域 types/API/组件;三组数据对比、历史、通过、驳回、异常放行、幂等和权限 | 只读审核 Controller/DTO/Service | W1 |
| `P2-ADM-05` | 入库费用快照 | 独占 `modules/inbounds/fee/**` 及测试 | 本域 types/API/组件;最新费用、完整计价过程、状态和已发布费用调整;不串常规 calculate/confirm/send | 只读 FBA 费用 Controller/DTO/Service | W1 |
| `P2-ADM-06` | 客户计费规则 | 独占 `modules/fba-pricing/customer-rules/**` 及测试 | 查询、详情、新版本、停用、权限和未保存保护 | 只读 `PlatformFbaMasterDataController` 客户规则契约 | W1 |
| `P2-ADM-07` | 船司 FBA 价格 | 独占 `modules/fba-pricing/carrier-rates/**` 及测试 | 查询、详情、新版本、停用、起运仓/渠道/船司/FBA 仓/价格和有效期 | 只读 `PlatformFbaMasterDataController` 价格契约 | W1 |
| `P2-ADM-08` | 库位管理 | 独占 `modules/warehouse-resources/locations/**` 及测试 | 企业/仓库级联、筛选分页、CRUD、启停、乐观锁、状态限制、响应式 | 现有企业选择器和仓库 API | W1 |
| `P2-ADM-09` | 托盘管理 | 独占 `modules/warehouse-resources/pallets/**` 及测试 | 企业/仓库级联、筛选分页、CRUD;不虚构状态或启停 | 现有企业选择器和仓库 API | W1 |
| `P2-ADM-10` | 入库详情工作台组装 | 独占 `modules/inbounds/pages/InboundOrderDetailView.vue`、`components/InboundWorkflowWorkspace.vue` 及测试 | 将 ADM-01 至 ADM-05 组件组装为详情页;统一刷新、租户切换清理、操作成功回读和托盘库位只读结果 | ADM-01 至 ADM-05 | W2 |
| `P2-ADM-11` | Admin 全局装配 | 独占各模块 `routes.ts`、`app/router/index.ts`、`core/navigation/adminNavigation.ts` 及对应测试 | 接入全部 Admin 页面、菜单和权限;首个可访问路由正确;不恢复接收审核/费用项目原型菜单 | ADM-06 至 ADM-10 | W3 |
| `P2-CLI-01` | 商户订单入库费用只读展示 | `apps/client-web/src/modules/first-leg/api/inboundBilling.ts`、`types/inboundBilling.ts`、`components/InboundBillingPanel.vue`、`views/FirstLegOrdersView.vue` 及测试 | 成功/待发布/失败/重试、快速切单、320/720/桌面;无写动作,不消费内部字段 | 只读 `MerchantInboundBillingController` 及 DTO/Service | W1 |
| `P2-QA-01` | Admin 跨模块回归 | 以新增集成测试和联调记录为主,不重构业务实现 | 租户切换、401/403、业务码、409、幂等、重复提交、图片异常、空/加载/失败、权限按钮、窄屏;运行 test/typecheck/build | `P2-ADM-11` | W4 |
| `P2-QA-02` | Client 安全与展示验收 | 只读审查和补测试,避免与 CLI-01 同时改订单页 | 不展示内部备注/历史;A0404 中性态;响应隔离;test/typecheck/build | `P2-CLI-01` | W2 |
### 6.2 推荐并行波次
```text
W1(10 个并行开发对话):
ADM-01 + ADM-02 + ADM-03 + ADM-04 + ADM-05
ADM-06 + ADM-07 + ADM-08 + ADM-09 + CLI-01
↓
W2(2 个并行对话): ADM-10 + QA-02
↓
W3(1 个集成对话): ADM-11
↓
W4: QA-01 跨模块联调、构建和回归
```
W1 的 10 个对话没有代码依赖,可以一次全部创建。真正的关键路径是 `ADM-01 至 ADM-05 → ADM-10 → ADM-11 → QA-01`;基础资料、仓库资源和 Client 与该链路完全并行。
### 6.3 多对话协作规则
- 每个对话只修改任务表声明的文件边界;发现需要改公共文件时留给 `P2-ADM-11`。
- `router/index.ts`、`adminNavigation.ts` 和对应测试只能由集成任务修改。
- 库位和托盘任务不得各自创建或修改同一个 `warehouse-resources/routes.ts`。
- 入库各业务域分别维护本域 `types.ts` 和 API,不创建共享巨型 DTO 文件;跨域只传 `tenantId: string`、`inboundId: string`、`readonly` 数据和 `changed` 事件。
- `ADM-01` 至 `ADM-05` 不创建或修改 `InboundOrderDetailView.vue`;详情页面只由 `ADM-10` 组装。
- `ADM-06`、`ADM-07` 不创建共同的 `fba-pricing/routes.ts`;路由只由 `ADM-11` 创建。
- Client 任务保持单对话实现,因为现有订单页超过一千行,拆成多个写任务会集中冲突。
- 每个任务完成后运行直接相关的测试和 typecheck;最终集成任务再运行全项目测试和构建。
- 后端契约疑问直接只读 Controller、DTO、Service、Repository/OpenAPI;本轮不得修改 `baiyue-firstleg/`。
## 七、当前后端源码未落地、暂不开发的范围
| 能力 | 本轮处理 | 原因 |
|---|---|---|
| 到仓预报和线下预约结果 | 暂不开发 | 无商户/平台公开接口 |
| 签到、分批收货、处理单元 | 暂不开发 | 无匹配 Controller |
| 标签打印和换标 | 暂不开发 | Print 服务仍是骨架,无业务公开接口 |
| 收货冲正 | 暂不开发 | 无匹配 Controller |
| 申报实测差异通知 | 暂不开发 | 无匹配 Controller |
| 上架任务和位置历史 | 暂不开发 | 当前只有托盘/库位和自动占用,无平台作业接口 |
| 通用费用项目白名单 | 暂不开发 | C6 未确认且无独立 Controller |
| 通知失败人工重试 | P1 候选 | 当前无明确平台公开接口 |
| 入库后换位 | P1 候选 | 当前无平台公开接口 |
| 测量视频、单票导出 | P1 候选 | 当前无明确契约 |
| 客户照片查看 | 暂不开发 | 当前无商户文件下载接口 |
## 八、上线阻断和风险
### 8.1 Client 数据暴露风险(上线阻断)
当前 `MerchantInboundBillingService` 直接返回平台侧 `InboundOrderDetail`,其中可能包含全部 `internalNotes` 和未按商户可见性过滤的 `files`。即使 Client 页面不渲染,这些字段仍会下发到浏览器。
前端必须做到:
- 商户 DTO 不声明、不读取、不渲染内部备注和原始内部历史。
- 不记录或展示原始响应对象。
但前端无法消除网络响应中的数据暴露,因此正式上线前仍需后端改为商户专用 DTO 或服务端字段过滤。该项不在本轮后端修改授权范围内,只登记为上线阻断。
### 8.2 需求和实现风险
- V2 C1-C8 尚未正式确认;若产品推翻当前自动计费入库流程,需要重新评估页面动作和任务边界。
- Admin 多个接口仍要求前端显式传目标 `tenantId`,与 V2“不相信前端 tenantId”的文字描述不同;前端按现有 Controller 调用,最终数据范围仍由服务端校验。
- 正常审核通过会自动完成后续动作,属于高影响写操作;页面确认文案必须明确结果,并防止重复点击。
- 计费规则和船司价格接口当前返回列表而非分页对象;页面不要先假设服务端分页。
- 托盘接口没有状态和启停能力;库位才有 `AVAILABLE/RESERVED/OCCUPIED/DISABLED` 状态。
## 九、每个任务的统一完成标准
- 严格按真实权限控制路由、菜单和按钮;前端权限只控制展示,服务端继续最终授权。
- 覆盖加载、空态、失败、无权限、业务冲突、重复操作和数据过期。
- 所有业务表格操作列在第一列,桌面端可固定左侧;窄屏不依赖表格末端操作。
- 所有 ID 保持字符串,金额、体积、重量和换算系数不硬编码、不自行推导为业务事实。
- 写操作成功后刷新服务端状态;409 或版本冲突提示用户并重载,不静默覆盖。
- Admin 运行 `pnpm test`、`pnpm typecheck`、`pnpm build`。
- Client 运行 `pnpm test`、`pnpm typecheck`、`pnpm build`。
- 不修改后端、不启动本地 Java/Nacos/数据库;联调默认通过测试 Gateway 的 `/api` 代理。
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large Load Diff
+225
View File
@@ -0,0 +1,225 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 1100" width="1600" height="1100" role="img" aria-labelledby="title desc">
<title id="title">跨境物流全链路流程图</title>
<desc id="desc">从头程下单、到仓计费、出仓及装船发运,到尾程到港前、到港、到仓、出仓和签收的完整业务流程。</desc>
<defs>
<linearGradient id="pageBg" x1="0" y1="0" x2="1" y2="1">
<stop offset="0" stop-color="#F5F9FF"/>
<stop offset="1" stop-color="#F7FBFA"/>
</linearGradient>
<linearGradient id="firstMile" x1="0" y1="0" x2="1" y2="0">
<stop offset="0" stop-color="#155EEF"/>
<stop offset="1" stop-color="#2878FF"/>
</linearGradient>
<linearGradient id="lastMile" x1="0" y1="0" x2="1" y2="0">
<stop offset="0" stop-color="#087E8B"/>
<stop offset="1" stop-color="#16A085"/>
</linearGradient>
<linearGradient id="transit" x1="0" y1="0" x2="1" y2="0">
<stop offset="0" stop-color="#EAF2FF"/>
<stop offset="0.5" stop-color="#DDF5F1"/>
<stop offset="1" stop-color="#EAF2FF"/>
</linearGradient>
<filter id="shadow" x="-15%" y="-15%" width="130%" height="140%">
<feDropShadow dx="0" dy="8" stdDeviation="10" flood-color="#16324F" flood-opacity="0.09"/>
</filter>
<filter id="softShadow" x="-20%" y="-20%" width="140%" height="150%">
<feDropShadow dx="0" dy="4" stdDeviation="6" flood-color="#0F3057" flood-opacity="0.10"/>
</filter>
<marker id="arrowBlue" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="7" markerHeight="7" orient="auto">
<path d="M1 1L8 5L1 9" fill="none" stroke="#7AA7ED" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/>
</marker>
<marker id="arrowGreen" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="7" markerHeight="7" orient="auto">
<path d="M1 1L8 5L1 9" fill="none" stroke="#62B8AD" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/>
</marker>
<style>
text{font-family:"PingFang SC","Microsoft YaHei","Noto Sans SC",Arial,sans-serif}
.title{font-size:34px;font-weight:700;fill:#102A43;letter-spacing:1px}
.subtitle{font-size:15px;fill:#627D98}
.lane-title{font-size:22px;font-weight:700;fill:#fff}
.lane-en{font-size:11px;font-weight:600;fill:#fff;opacity:.72;letter-spacing:1.7px}
.phase-title{font-size:18px;font-weight:700;fill:#102A43}
.phase-no{font-size:11px;font-weight:700;fill:#fff;letter-spacing:.5px}
.step{font-size:13px;fill:#334E68}
.step-small{font-size:12px;fill:#334E68}
.step-no{font-size:10px;font-weight:700;fill:#fff}
.summary-label{font-size:11px;font-weight:700;fill:#627D98;letter-spacing:1px}
.summary-value{font-size:15px;font-weight:700;fill:#243B53}
</style>
</defs>
<rect width="1600" height="1100" fill="url(#pageBg)"/>
<circle cx="1490" cy="50" r="180" fill="#D9F1ED" opacity=".42"/>
<circle cx="90" cy="1080" r="220" fill="#DFEBFF" opacity=".48"/>
<!-- 标题与概览 -->
<text x="52" y="58" class="title">跨境物流全链路流程</text>
<text x="52" y="88" class="subtitle">从客户下单到海外签收 · 头程与尾程全节点协同</text>
<g transform="translate(1195 28)">
<rect width="112" height="60" rx="14" fill="#fff" stroke="#D9E2EC"/>
<text x="18" y="22" class="summary-label">业务阶段</text><text x="18" y="45" class="summary-value">8 个阶段</text>
<rect x="124" width="112" height="60" rx="14" fill="#fff" stroke="#D9E2EC"/>
<text x="142" y="22" class="summary-label">关键动作</text><text x="142" y="45" class="summary-value">36 个节点</text>
<rect x="248" width="112" height="60" rx="14" fill="#fff" stroke="#D9E2EC"/>
<text x="266" y="22" class="summary-label">最终结果</text><text x="266" y="45" class="summary-value">完成签收</text>
</g>
<!-- 头程泳道 -->
<rect x="30" y="120" width="1540" height="430" rx="24" fill="#FFFFFF" stroke="#D7E4F4" filter="url(#shadow)"/>
<path d="M54 120H1546A24 24 0 0 1 1570 144V194H30V144A24 24 0 0 1 54 120Z" fill="url(#firstMile)"/>
<g transform="translate(58 142)">
<circle cx="22" cy="22" r="22" fill="#fff" opacity=".16"/>
<path d="M10 23h24M14 23v11h16V23M17 19v-6h10v6M21 27h3" fill="none" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<text x="58" y="18" class="lane-title">头程物流</text>
<text x="58" y="38" class="lane-en">FIRST-MILE LOGISTICS · 国内集货与出口</text>
</g>
<g transform="translate(1306 145)">
<rect width="210" height="42" rx="21" fill="#fff" opacity=".15"/>
<text x="105" y="26" text-anchor="middle" font-size="13" font-weight="600" fill="#fff">下单 → 到仓 → 出仓</text>
</g>
<!-- 头程阶段连接 -->
<path d="M435 365H474" stroke="#7AA7ED" stroke-width="2" marker-end="url(#arrowBlue)"/>
<path d="M850 365H889" stroke="#7AA7ED" stroke-width="2" marker-end="url(#arrowBlue)"/>
<!-- 一、下单 -->
<g transform="translate(55 220)">
<rect width="380" height="300" rx="18" fill="#F8FBFF" stroke="#C9DBF7"/>
<circle cx="32" cy="32" r="17" fill="#155EEF"/><text x="32" y="36" text-anchor="middle" class="phase-no">01</text>
<text x="60" y="39" class="phase-title">下单</text>
<text x="326" y="37" font-size="11" fill="#829AB1">4 个动作</text>
<line x1="20" y1="64" x2="360" y2="64" stroke="#D9E8FA"/>
<g transform="translate(20 82)">
<rect width="340" height="42" rx="10" fill="#fff" stroke="#DFEAF8"/><circle cx="22" cy="21" r="11" fill="#3B82F6"/><text x="22" y="25" text-anchor="middle" class="step-no">1</text><text x="44" y="26" class="step">客户下单</text>
<rect y="52" width="340" height="42" rx="10" fill="#fff" stroke="#DFEAF8"/><circle cx="22" cy="73" r="11" fill="#3B82F6"/><text x="22" y="77" text-anchor="middle" class="step-no">2</text><text x="44" y="78" class="step">委托我们下单</text>
<rect y="104" width="340" height="42" rx="10" fill="#fff" stroke="#DFEAF8"/><circle cx="22" cy="125" r="11" fill="#3B82F6"/><text x="22" y="129" text-anchor="middle" class="step-no">3</text><text x="44" y="130" class="step">快速下单(只有唛头)</text>
<rect y="156" width="340" height="42" rx="10" fill="#fff" stroke="#DFEAF8"/><circle cx="22" cy="177" r="11" fill="#3B82F6"/><text x="22" y="181" text-anchor="middle" class="step-no">4</text><text x="44" y="182" class="step">换标下单(中转、退货)</text>
</g>
</g>
<!-- 二、到仓 -->
<g transform="translate(470 220)">
<rect width="380" height="300" rx="18" fill="#F8FBFF" stroke="#C9DBF7"/>
<circle cx="32" cy="32" r="17" fill="#155EEF"/><text x="32" y="36" text-anchor="middle" class="phase-no">02</text>
<text x="60" y="39" class="phase-title">到仓</text>
<text x="326" y="37" font-size="11" fill="#829AB1">6 个动作</text>
<line x1="20" y1="64" x2="360" y2="64" stroke="#D9E8FA"/>
<g transform="translate(20 76)">
<rect width="340" height="32" rx="8" fill="#fff" stroke="#DFEAF8"/><circle cx="20" cy="16" r="10" fill="#3B82F6"/><text x="20" y="20" text-anchor="middle" class="step-no">1</text><text x="40" y="21" class="step">预约取货</text>
<rect y="36" width="340" height="32" rx="8" fill="#fff" stroke="#DFEAF8"/><circle cx="20" cy="52" r="10" fill="#3B82F6"/><text x="20" y="56" text-anchor="middle" class="step-no">2</text><text x="40" y="57" class="step">送货上门</text>
<rect y="72" width="340" height="32" rx="8" fill="#fff" stroke="#DFEAF8"/><circle cx="20" cy="88" r="10" fill="#3B82F6"/><text x="20" y="92" text-anchor="middle" class="step-no">3</text><text x="40" y="93" class="step">入库测量(称重计费)</text>
<rect y="108" width="340" height="32" rx="8" fill="#fff" stroke="#DFEAF8"/><circle cx="20" cy="124" r="10" fill="#3B82F6"/><text x="20" y="128" text-anchor="middle" class="step-no">4</text><text x="40" y="129" class="step">调整差异核对</text>
<rect y="144" width="340" height="32" rx="8" fill="#fff" stroke="#DFEAF8"/><circle cx="20" cy="160" r="10" fill="#3B82F6"/><text x="20" y="164" text-anchor="middle" class="step-no">5</text><text x="40" y="165" class="step">仓库作业(指定库位)</text>
<rect y="180" width="340" height="32" rx="8" fill="#EAF2FF" stroke="#BFD3F5"/><circle cx="20" cy="196" r="10" fill="#155EEF"/><text x="20" y="200" text-anchor="middle" class="step-no">6</text><text x="40" y="201" class="step" font-weight="600">计费</text>
</g>
</g>
<!-- 三、出仓 -->
<g transform="translate(885 220)">
<rect width="660" height="300" rx="18" fill="#F8FBFF" stroke="#C9DBF7"/>
<circle cx="32" cy="32" r="17" fill="#155EEF"/><text x="32" y="36" text-anchor="middle" class="phase-no">03</text>
<text x="60" y="39" class="phase-title">出仓</text>
<text x="604" y="37" font-size="11" fill="#829AB1">9 个动作</text>
<line x1="20" y1="64" x2="640" y2="64" stroke="#D9E8FA"/>
<g transform="translate(20 76)">
<rect width="300" height="36" rx="9" fill="#fff" stroke="#DFEAF8"/><circle cx="20" cy="18" r="10" fill="#3B82F6"/><text x="20" y="22" text-anchor="middle" class="step-no">1</text><text x="40" y="23" class="step-small">智能排柜</text>
<rect y="42" width="300" height="36" rx="9" fill="#fff" stroke="#DFEAF8"/><circle cx="20" cy="60" r="10" fill="#3B82F6"/><text x="20" y="64" text-anchor="middle" class="step-no">2</text><text x="40" y="65" class="step-small">订舱</text>
<rect y="84" width="300" height="36" rx="9" fill="#fff" stroke="#DFEAF8"/><circle cx="20" cy="102" r="10" fill="#3B82F6"/><text x="20" y="106" text-anchor="middle" class="step-no">3</text><text x="40" y="107" class="step-small">预约托车</text>
<rect y="126" width="300" height="36" rx="9" fill="#fff" stroke="#DFEAF8"/><circle cx="20" cy="144" r="10" fill="#3B82F6"/><text x="20" y="148" text-anchor="middle" class="step-no">4</text><text x="40" y="149" class="step-small">PDA 拣货</text>
<rect y="168" width="300" height="36" rx="9" fill="#fff" stroke="#DFEAF8"/><circle cx="20" cy="186" r="10" fill="#3B82F6"/><text x="20" y="190" text-anchor="middle" class="step-no">5</text><text x="40" y="191" class="step-small">拍货柜及铅封照上传</text>
<rect x="320" width="300" height="36" rx="9" fill="#fff" stroke="#DFEAF8"/><circle cx="340" cy="18" r="10" fill="#3B82F6"/><text x="340" y="22" text-anchor="middle" class="step-no">6</text><text x="360" y="23" class="step-small">客户上传报关资料</text>
<rect x="320" y="42" width="300" height="36" rx="9" fill="#fff" stroke="#DFEAF8"/><circle cx="340" cy="60" r="10" fill="#3B82F6"/><text x="340" y="64" text-anchor="middle" class="step-no">7</text><text x="360" y="65" class="step-small">整理给到报关代理报关</text>
<rect x="320" y="84" width="300" height="36" rx="9" fill="#fff" stroke="#DFEAF8"/><circle cx="340" cy="102" r="10" fill="#3B82F6"/><text x="340" y="106" text-anchor="middle" class="step-no">8</text><text x="360" y="107" class="step-small">回执上传系统供客户下载</text>
<rect x="320" y="126" width="300" height="36" rx="9" fill="#fff" stroke="#DFEAF8"/><circle cx="340" cy="144" r="10" fill="#3B82F6"/><text x="340" y="148" text-anchor="middle" class="step-no">9</text><text x="360" y="149" class="step-small">查港务信息(预计开船、到港等)</text>
<g transform="translate(320 171)"><rect width="300" height="32" rx="16" fill="#E7F0FF"/><circle cx="24" cy="16" r="8" fill="#155EEF"/><path d="M20 16h8M24 12v8" stroke="#fff" stroke-width="1.5"/><text x="44" y="21" font-size="12" font-weight="600" fill="#155EEF">完成国内出仓,等待装船</text></g>
</g>
</g>
<!-- 海运衔接带 -->
<g transform="translate(350 570)" filter="url(#softShadow)">
<rect width="900" height="74" rx="37" fill="url(#transit)" stroke="#B9D8DF"/>
<path d="M0 37H286M614 37H900" stroke="#78AFC1" stroke-width="1.5" stroke-dasharray="7 7"/>
<g transform="translate(302 12)">
<circle cx="25" cy="25" r="25" fill="#fff"/>
<path d="M11 29h28l-4 7H16l-5-7Zm5-3h19l-3-11H19l-3 11Zm8-11V9h4v6" fill="none" stroke="#087E8B" stroke-width="2" stroke-linejoin="round"/>
<text x="68" y="23" font-size="16" font-weight="700" fill="#0B6070">装船发运 · 国际海运在途</text>
<text x="68" y="43" font-size="12" fill="#527C86">头程完成,进入海外清关与尾程履约</text>
</g>
</g>
<!-- 尾程泳道 -->
<rect x="30" y="670" width="1540" height="400" rx="24" fill="#FFFFFF" stroke="#CFE7E2" filter="url(#shadow)"/>
<path d="M54 670H1546A24 24 0 0 1 1570 694V744H30V694A24 24 0 0 1 54 670Z" fill="url(#lastMile)"/>
<g transform="translate(58 692)">
<circle cx="22" cy="22" r="22" fill="#fff" opacity=".16"/>
<path d="M10 27h7l3-10h10l3 10h3M15 27a4 4 0 1 0 0 8 4 4 0 0 0 0-8Zm20 0a4 4 0 1 0 0 8 4 4 0 0 0 0-8Z" fill="none" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<text x="58" y="18" class="lane-title">尾程物流</text>
<text x="58" y="38" class="lane-en">LAST-MILE LOGISTICS · 海外清关、入仓与派送</text>
</g>
<g transform="translate(1185 695)">
<rect width="330" height="42" rx="21" fill="#fff" opacity=".15"/>
<text x="165" y="26" text-anchor="middle" font-size="13" font-weight="600" fill="#fff">到港前 → 到港 → 到仓 → 出仓 → 签收</text>
</g>
<!-- 尾程连接线 -->
<path d="M328 900H352" stroke="#62B8AD" stroke-width="2" marker-end="url(#arrowGreen)"/>
<path d="M633 900H657" stroke="#62B8AD" stroke-width="2" marker-end="url(#arrowGreen)"/>
<path d="M938 900H962" stroke="#62B8AD" stroke-width="2" marker-end="url(#arrowGreen)"/>
<path d="M1243 900H1267" stroke="#62B8AD" stroke-width="2" marker-end="url(#arrowGreen)"/>
<!-- 尾程阶段卡片 -->
<g transform="translate(48 770)">
<rect width="280" height="268" rx="16" fill="#F7FCFB" stroke="#CBE7E1"/>
<circle cx="28" cy="29" r="16" fill="#087E8B"/><text x="28" y="33" text-anchor="middle" class="phase-no">01</text><text x="54" y="35" class="phase-title">到港前</text><text x="54" y="53" font-size="11" fill="#648A87">船在海上</text>
<g transform="translate(16 72)">
<rect width="248" height="36" rx="9" fill="#fff" stroke="#D8EBE7"/><circle cx="18" cy="18" r="10" fill="#13A18D"/><text x="18" y="22" text-anchor="middle" class="step-no">1</text><text x="38" y="23" class="step-small">FBA 仓预约</text>
<rect y="42" width="248" height="44" rx="9" fill="#fff" stroke="#D8EBE7"/><circle cx="18" cy="64" r="10" fill="#13A18D"/><text x="18" y="68" text-anchor="middle" class="step-no">2</text><text x="38" y="61" class="step-small">BOL 预清货</text><text x="38" y="77" font-size="10" fill="#829AB1">时间、费用、目的仓</text>
<rect y="92" width="248" height="42" rx="9" fill="#fff" stroke="#D8EBE7"/><circle cx="18" cy="113" r="10" fill="#13A18D"/><text x="18" y="117" text-anchor="middle" class="step-no">3</text><text x="38" y="110" class="step-small">清关资料上传到</text><text x="38" y="126" class="step-small">对应货柜</text>
<rect y="140" width="248" height="36" rx="9" fill="#fff" stroke="#D8EBE7"/><circle cx="18" cy="158" r="10" fill="#13A18D"/><text x="18" y="162" text-anchor="middle" class="step-no">4</text><text x="38" y="163" class="step-small">明细备注货物中转等</text>
</g>
</g>
<g transform="translate(353 770)">
<rect width="280" height="268" rx="16" fill="#F7FCFB" stroke="#CBE7E1"/>
<circle cx="28" cy="29" r="16" fill="#087E8B"/><text x="28" y="33" text-anchor="middle" class="phase-no">02</text><text x="54" y="35" class="phase-title">到港</text>
<g transform="translate(16 82)">
<rect width="248" height="50" rx="9" fill="#fff" stroke="#D8EBE7"/><circle cx="18" cy="25" r="10" fill="#13A18D"/><text x="18" y="29" text-anchor="middle" class="step-no">1</text><text x="38" y="30" class="step-small">清关放行</text>
<rect y="60" width="248" height="64" rx="9" fill="#fff" stroke="#D8EBE7"/><circle cx="18" cy="92" r="10" fill="#13A18D"/><text x="18" y="96" text-anchor="middle" class="step-no">2</text><text x="38" y="88" class="step-small">DO 或 PU 邮发</text><text x="38" y="106" class="step-small">通知码头车队提柜</text>
<rect y="136" width="248" height="38" rx="19" fill="#E3F6F2"/><text x="124" y="160" text-anchor="middle" font-size="12" font-weight="600" fill="#087E8B">完成提柜 · 运往海外仓</text>
</g>
</g>
<g transform="translate(658 770)">
<rect width="280" height="268" rx="16" fill="#F7FCFB" stroke="#CBE7E1"/>
<circle cx="28" cy="29" r="16" fill="#087E8B"/><text x="28" y="33" text-anchor="middle" class="phase-no">03</text><text x="54" y="35" class="phase-title">到仓</text>
<g transform="translate(16 82)">
<rect width="248" height="42" rx="9" fill="#fff" stroke="#D8EBE7"/><circle cx="18" cy="21" r="10" fill="#13A18D"/><text x="18" y="25" text-anchor="middle" class="step-no">1</text><text x="38" y="26" class="step-small">开柜拍照比对</text>
<rect y="50" width="248" height="42" rx="9" fill="#fff" stroke="#D8EBE7"/><circle cx="18" cy="71" r="10" fill="#13A18D"/><text x="18" y="75" text-anchor="middle" class="step-no">2</text><text x="38" y="76" class="step-small">根据 PDA 需求打托作业</text>
<rect y="100" width="248" height="42" rx="9" fill="#fff" stroke="#D8EBE7"/><circle cx="18" cy="121" r="10" fill="#13A18D"/><text x="18" y="125" text-anchor="middle" class="step-no">3</text><text x="38" y="126" class="step-small">派送车队筛选预约</text>
</g>
</g>
<g transform="translate(963 770)">
<rect width="280" height="268" rx="16" fill="#F7FCFB" stroke="#CBE7E1"/>
<circle cx="28" cy="29" r="16" fill="#087E8B"/><text x="28" y="33" text-anchor="middle" class="phase-no">04</text><text x="54" y="35" class="phase-title">出仓</text>
<g transform="translate(16 72)">
<rect width="248" height="34" rx="9" fill="#fff" stroke="#D8EBE7"/><circle cx="18" cy="17" r="9" fill="#13A18D"/><text x="18" y="21" text-anchor="middle" class="step-no">1</text><text x="36" y="22" class="step-small">预排车转 BOL 送货</text>
<rect y="40" width="248" height="34" rx="9" fill="#fff" stroke="#D8EBE7"/><circle cx="18" cy="57" r="9" fill="#13A18D"/><text x="18" y="61" text-anchor="middle" class="step-no">2</text><text x="36" y="62" class="step-small">PDA 拣货装车</text>
<rect y="80" width="248" height="34" rx="9" fill="#fff" stroke="#D8EBE7"/><circle cx="18" cy="97" r="9" fill="#13A18D"/><text x="18" y="101" text-anchor="middle" class="step-no">3</text><text x="36" y="102" class="step-small">派送车队到仓装车</text>
<rect y="120" width="248" height="34" rx="9" fill="#fff" stroke="#D8EBE7"/><circle cx="18" cy="137" r="9" fill="#13A18D"/><text x="18" y="141" text-anchor="middle" class="step-no">4</text><text x="36" y="142" class="step-small">整柜派送</text>
<rect y="160" width="248" height="34" rx="9" fill="#fff" stroke="#D8EBE7"/><circle cx="18" cy="177" r="9" fill="#13A18D"/><text x="18" y="181" text-anchor="middle" class="step-no">5</text><text x="36" y="182" class="step-small">自提出仓(一件代发)</text>
</g>
</g>
<g transform="translate(1268 770)">
<rect width="284" height="268" rx="16" fill="#F7FCFB" stroke="#CBE7E1"/>
<circle cx="28" cy="29" r="16" fill="#087E8B"/><text x="28" y="33" text-anchor="middle" class="phase-no">05</text><text x="54" y="35" class="phase-title">签收</text>
<g transform="translate(16 82)">
<rect width="252" height="42" rx="9" fill="#fff" stroke="#D8EBE7"/><circle cx="18" cy="21" r="10" fill="#13A18D"/><text x="18" y="25" text-anchor="middle" class="step-no">1</text><text x="38" y="26" class="step-small">私人仓签收回执上传</text>
<rect y="50" width="252" height="42" rx="9" fill="#fff" stroke="#D8EBE7"/><circle cx="18" cy="71" r="10" fill="#13A18D"/><text x="18" y="75" text-anchor="middle" class="step-no">2</text><text x="38" y="76" class="step-small">FBA 仓签收回执上传</text>
<rect y="100" width="252" height="42" rx="9" fill="#fff" stroke="#D8EBE7"/><circle cx="18" cy="121" r="10" fill="#13A18D"/><text x="18" y="125" text-anchor="middle" class="step-no">3</text><text x="38" y="126" class="step-small">送仓异常处理</text>
<g transform="translate(0 156)"><rect width="252" height="38" rx="19" fill="#087E8B"/><circle cx="24" cy="19" r="9" fill="#fff" opacity=".2"/><path d="M20 19l3 3 6-7" fill="none" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><text x="132" y="24" text-anchor="middle" font-size="13" font-weight="700" fill="#fff">全流程完成 · 已签收</text></g>
</g>
</g>
<text x="800" y="1088" text-anchor="middle" font-size="11" fill="#829AB1">HEAD-HAUL &amp; LAST-MILE LOGISTICS PROCESS · 全程节点可追踪</text>
</svg>

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 881 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 549 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 835 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 492 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

File diff suppressed because it is too large Load Diff
+266
View File
@@ -0,0 +1,266 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>%s</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@300;400;500;600;700;900&family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<script src="https://cdn.tailwindcss.com"></script>
<script>
tailwind.config = { theme: { extend: { colors: { brand: { green:'#4CAF50', greenDark:'#2E7D32', blue:'#1565C0', blueDark:'#0D47A1', teal:'#00838F', light:'#F0F7FF', dark:'#0A1628' } }, fontFamily: { sans:['Inter','Noto Sans SC','system-ui','sans-serif'] } } } };
</script>
<style>
:root { --primary:#1565C0; --primary-dark:#0D47A1; --accent:#4CAF50; --accent-dark:#2E7D32; }
* { scroll-behavior:smooth; }
body { font-family:'Inter','Noto Sans SC',system-ui,sans-serif; overflow-x:hidden; }
.gradient-text { background:linear-gradient(135deg,#1565C0 0%,#4CAF50 100%); -webkit-background-clip:text; -webkit-text-fill-color:transparent; background-clip:text; }
.gradient-bg { background:linear-gradient(135deg,#0A1628 0%,#1565C0 50%,#00838F 100%); }
.btn-primary { background:linear-gradient(135deg,#1565C0 0%,#0D47A1 100%); transition:all .3s ease; }
.btn-primary:hover { transform:translateY(-2px); box-shadow:0 10px 30px rgba(21,101,192,.35); }
.btn-accent { background:linear-gradient(135deg,#4CAF50 0%,#2E7D32 100%); transition:all .3s ease; }
.btn-accent:hover { transform:translateY(-2px); box-shadow:0 10px 30px rgba(76,175,80,.35); }
.input-field { transition:all .3s ease; }
.input-field:focus { border-color:#1565C0; box-shadow:0 0 0 4px rgba(21,101,192,.1); }
.auth-logo { display:block; filter:brightness(0) saturate(100%) invert(20%) sepia(84%) saturate(1926%) hue-rotate(202deg) brightness(94%) contrast(93%); }
.lang-switcher { display:flex; align-items:center; background:rgba(21,101,192,.08); border-radius:9999px; padding:3px; border:1px solid rgba(21,101,192,.15); }
.lang-switcher button { font-size:12px; font-weight:600; padding:4px 9px; border-radius:9999px; color:#0A1628; transition:all .25s ease; white-space:nowrap; }
.lang-switcher button:hover { color:#1565C0; }
.lang-switcher button.active { background:#1565C0; color:#fff; }
</style>
</head>
<body class="text-gray-800 antialiased bg-gray-50">
<!-- 顶部栏 -->
<header class="fixed top-0 inset-x-0 z-50 bg-white/95 backdrop-blur border-b border-gray-100">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 h-16 flex items-center justify-between">
<a href="index.html" class="flex items-center" aria-label="返回官网">
<img src="assets/logo.png" alt="柏悦物流" class="auth-logo" style="width:161px;height:60px;object-fit:contain;">
</a>
<div class="lang-switcher" id="langSwitcher">
<button data-lang="zh" class="active" title="中文">中</button>
<button data-lang="en" title="English">EN</button>
<button data-lang="es" title="Español (México)">🇲🇽</button>
</div>
</div>
</header>
<section id="loginSection" class="min-h-screen pt-24 pb-12 bg-gray-50 flex items-center">
<div class="w-full max-w-6xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="bg-white rounded-3xl shadow-2xl overflow-hidden">
<div class="grid lg:grid-cols-2">
<!-- 左侧视觉 -->
<div class="hidden lg:block relative">
<img src="https://images.unsplash.com/photo-1494412574643-ff11b0a5c1c3?ixlib=rb-4.0.3&auto=format&fit=crop&w=2070&q=80" alt="物流" class="absolute inset-0 w-full h-full object-cover">
<div class="absolute inset-0 gradient-bg opacity-90"></div>
<div class="relative z-10 p-12 h-full flex flex-col justify-between text-white">
<div>
<img src="assets/logo.png" alt="虫虫出海" style="width:161px;height:60px;object-fit:contain;">
<h2 class="text-3xl font-bold mb-4" data-i18n="login_welcome">欢迎回来</h2>
<p class="text-white/80" data-i18n="login_desc">登录虫虫出海平台,实时追踪货物轨迹,管理订单与库存。</p>
</div>
<div class="space-y-4">
<div class="flex items-center space-x-3">
<i class="fas fa-check-circle text-brand-green"></i>
<span data-i18n="login_feat1">全程可视化追踪</span>
</div>
<div class="flex items-center space-x-3">
<i class="fas fa-check-circle text-brand-green"></i>
<span data-i18n="login_feat2">智能订单管理</span>
</div>
<div class="flex items-center space-x-3">
<i class="fas fa-check-circle text-brand-green"></i>
<span data-i18n="login_feat3">在线询价下单</span>
</div>
</div>
</div>
</div>
<!-- 右侧表单 -->
<div class="p-8 sm:p-12 lg:p-16">
<div class="max-w-md mx-auto">
<h2 class="text-3xl font-bold text-gray-900 mb-2" data-i18n="login_title">账号登录</h2>
<p class="text-gray-600 mb-8"><span data-i18n="login_no_account">还没有账号?</span><button onclick="window.location.href='register.html'" class="text-brand-blue font-semibold hover:underline" data-i18n="login_register_link">立即注册</button></p>
<form id="loginForm" class="space-y-5">
<div>
<label class="block text-sm font-medium text-gray-700 mb-2" data-i18n="login_account_label">邮箱 / 手机号</label>
<div class="relative">
<i class="fas fa-user absolute left-4 top-1/2 -translate-y-1/2 text-gray-400"></i>
<input type="text" name="account" required placeholder="请输入邮箱或手机号" data-i18n-ph="login_account_ph" class="input-field w-full pl-11 pr-4 py-3.5 border border-gray-200 rounded-xl outline-none">
</div>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-2" data-i18n="login_password_label">密码</label>
<div class="relative">
<i class="fas fa-lock absolute left-4 top-1/2 -translate-y-1/2 text-gray-400"></i>
<input type="password" name="password" required placeholder="请输入密码" data-i18n-ph="login_password_ph" class="input-field w-full pl-11 pr-12 py-3.5 border border-gray-200 rounded-xl outline-none">
<button type="button" class="toggle-password absolute right-4 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-600">
<i class="fas fa-eye"></i>
</button>
</div>
</div>
<div class="flex items-center justify-between text-sm">
<label class="flex items-center text-gray-600 cursor-pointer">
<input type="checkbox" class="w-4 h-4 text-brand-blue rounded border-gray-300 mr-2">
<span data-i18n="login_remember">记住我</span>
</label>
<button type="button" onclick="showToast(t('toast_resetpw'))" class="text-brand-blue hover:underline" data-i18n="login_forgot">忘记密码?</button>
</div>
<button type="submit" class="btn-primary w-full text-white font-bold py-4 rounded-xl text-lg" data-i18n="login_submit">
登 录
</button>
</form>
<div class="mt-8">
<div class="relative">
<div class="absolute inset-0 flex items-center">
<div class="w-full border-t border-gray-200"></div>
</div>
<div class="relative flex justify-center text-sm">
<span class="px-2 bg-white text-gray-500" data-i18n="login_other">其他方式登录</span>
</div>
</div>
<div class="flex justify-center space-x-4 mt-6">
<button onclick="showToast('微信登录即将上线')" class="w-12 h-12 rounded-full border border-gray-200 flex items-center justify-center text-green-600 hover:bg-green-50 transition-colors">
<i class="fab fa-weixin text-xl"></i>
</button>
<button onclick="showToast('企业微信登录即将上线')" class="w-12 h-12 rounded-full border border-gray-200 flex items-center justify-center text-blue-600 hover:bg-blue-50 transition-colors">
<i class="fas fa-briefcase text-xl"></i>
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section> <!-- Toast 提示 -->
<div id="toast" class="fixed top-24 left-1/2 -translate-x-1/2 z-[60] opacity-0 pointer-events-none transition-all duration-300 transform -translate-y-4">
<div class="bg-gray-900 text-white px-6 py-3 rounded-full shadow-2xl flex items-center space-x-2">
<i class="fas fa-info-circle text-brand-green"></i>
<span id="toastMessage">提示信息</span>
</div>
</div>
<script>
let currentLang = localStorage.getItem('cc_lang') || 'zh';
function t(key) {
return (i18n[currentLang] && i18n[currentLang][key] !== undefined) ? i18n[currentLang][key] : key;
}
const i18n = {
zh: {
login_welcome: "欢迎回来",
login_desc: "登录虫虫出海平台,实时追踪货物轨迹,管理订单与库存。",
login_feat1: "全程可视化追踪",
login_feat2: "智能订单管理",
login_feat3: "在线询价下单",
login_title: "账号登录",
login_no_account: "还没有账号?",
login_register_link: "立即注册",
login_account_label: "邮箱 / 手机号",
login_account_ph: "请输入邮箱或手机号",
login_password_label: "密码",
login_password_ph: "请输入密码",
login_remember: "记住我",
login_forgot: "忘记密码?",
login_submit: "登 录",
login_other: "其他方式登录",
toast_resetpw: "请联系客服重置密码",
toast_login_ok: "登录成功!欢迎回来"
},
en: {
login_welcome: "Welcome Back",
login_desc: "Log in to track shipments in real time and manage orders and inventory.",
login_feat1: "Full Visual Tracking",
login_feat2: "Smart Order Management",
login_feat3: "Online Quoting & Ordering",
login_title: "Account Login",
login_no_account: "No account yet?",
login_register_link: "Sign Up Now",
login_account_label: "Email / Phone",
login_account_ph: "Enter email or phone",
login_password_label: "Password",
login_password_ph: "Enter password",
login_remember: "Remember Me",
login_forgot: "Forgot Password?",
login_submit: "Log In",
login_other: "Other Sign-In Methods",
toast_resetpw: "Please contact support to reset your password",
toast_login_ok: "Login successful! Welcome back"
},
es: {
login_welcome: "Bienvenido de nuevo",
login_desc: "Inicia sesión para rastrear envíos en tiempo real y gestionar pedidos e inventario.",
login_feat1: "Seguimiento visual completo",
login_feat2: "Gestión inteligente de pedidos",
login_feat3: "Cotización y pedidos en línea",
login_title: "Inicio de sesión",
login_no_account: "¿Aún no tienes cuenta?",
login_register_link: "Regístrate ahora",
login_account_label: "Correo / Teléfono",
login_account_ph: "Ingresa correo o teléfono",
login_password_label: "Contraseña",
login_password_ph: "Ingresa tu contraseña",
login_remember: "Recuérdame",
login_forgot: "¿Olvidó su contraseña?",
login_submit: "Iniciar sesión",
login_other: "Otros métodos de acceso",
toast_resetpw: "Contacta a soporte para restablecer tu contraseña",
toast_login_ok: "¡Inicio de sesión exitoso! Bienvenido"
}
};
function applyLanguage(lang) {
if (!i18n[lang]) lang = 'zh';
currentLang = lang;
const langAttr = lang === 'zh' ? 'zh-CN' : (lang === 'es' ? 'es-MX' : 'en');
document.documentElement.setAttribute('lang', langAttr);
document.querySelectorAll('[data-i18n]').forEach(el => {
const k = el.getAttribute('data-i18n');
const v = i18n[lang][k];
if (v !== undefined) { if (v.indexOf('<br>') !== -1) el.innerHTML = v; else el.textContent = v; }
});
document.querySelectorAll('[data-i18n-ph]').forEach(el => {
const k = el.getAttribute('data-i18n-ph');
if (i18n[lang][k] !== undefined) el.setAttribute('placeholder', i18n[lang][k]);
});
document.querySelectorAll('[data-lang]').forEach(b => {
b.classList.toggle('active', b.getAttribute('data-lang') === lang);
});
try { localStorage.setItem('cc_lang', lang); } catch (e) {}
}
document.querySelectorAll('#langSwitcher [data-lang]').forEach(b => {
b.addEventListener('click', () => applyLanguage(b.getAttribute('data-lang')));
});
function showToast(message) {
const toast = document.getElementById('toast');
const toastMessage = document.getElementById('toastMessage');
toastMessage.textContent = message;
toast.classList.remove('opacity-0', 'pointer-events-none', '-translate-y-4');
toast.classList.add('opacity-100', 'translate-y-0');
setTimeout(() => {
toast.classList.add('opacity-0', 'pointer-events-none', '-translate-y-4');
toast.classList.remove('opacity-100', 'translate-y-0');
}, 3000);
}
function updateAuthUI() {}
document.querySelectorAll('.toggle-password').forEach(button => {
button.addEventListener('click', function() {
const input = this.previousElementSibling;
const icon = this.querySelector('i');
if (input.type === 'password') { input.type = 'text'; icon.classList.remove('fa-eye'); icon.classList.add('fa-eye-slash'); }
else { input.type = 'password'; icon.classList.remove('fa-eye-slash'); icon.classList.add('fa-eye'); }
});
});
document.getElementById('loginForm').addEventListener('submit', function(e) {
e.preventDefault();
showToast(t('toast_login_ok'));
setTimeout(() => window.location.href = 'workbench.html', 900);
});
applyLanguage(currentLang);
</script>
</body>
</html>
Binary file not shown.

After

Width:  |  Height:  |  Size: 764 KiB

+286
View File
@@ -0,0 +1,286 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>%s</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@300;400;500;600;700;900&family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<script src="https://cdn.tailwindcss.com"></script>
<script>
tailwind.config = { theme: { extend: { colors: { brand: { green:'#4CAF50', greenDark:'#2E7D32', blue:'#1565C0', blueDark:'#0D47A1', teal:'#00838F', light:'#F0F7FF', dark:'#0A1628' } }, fontFamily: { sans:['Inter','Noto Sans SC','system-ui','sans-serif'] } } } };
</script>
<style>
:root { --primary:#1565C0; --primary-dark:#0D47A1; --accent:#4CAF50; --accent-dark:#2E7D32; }
* { scroll-behavior:smooth; }
body { font-family:'Inter','Noto Sans SC',system-ui,sans-serif; overflow-x:hidden; }
.gradient-text { background:linear-gradient(135deg,#1565C0 0%,#4CAF50 100%); -webkit-background-clip:text; -webkit-text-fill-color:transparent; background-clip:text; }
.gradient-bg { background:linear-gradient(135deg,#0A1628 0%,#1565C0 50%,#00838F 100%); }
.btn-primary { background:linear-gradient(135deg,#1565C0 0%,#0D47A1 100%); transition:all .3s ease; }
.btn-primary:hover { transform:translateY(-2px); box-shadow:0 10px 30px rgba(21,101,192,.35); }
.btn-accent { background:linear-gradient(135deg,#4CAF50 0%,#2E7D32 100%); transition:all .3s ease; }
.btn-accent:hover { transform:translateY(-2px); box-shadow:0 10px 30px rgba(76,175,80,.35); }
.input-field { transition:all .3s ease; }
.input-field:focus { border-color:#1565C0; box-shadow:0 0 0 4px rgba(21,101,192,.1); }
.auth-logo { display:block; filter:brightness(0) saturate(100%) invert(20%) sepia(84%) saturate(1926%) hue-rotate(202deg) brightness(94%) contrast(93%); }
.lang-switcher { display:flex; align-items:center; background:rgba(21,101,192,.08); border-radius:9999px; padding:3px; border:1px solid rgba(21,101,192,.15); }
.lang-switcher button { font-size:12px; font-weight:600; padding:4px 9px; border-radius:9999px; color:#0A1628; transition:all .25s ease; white-space:nowrap; }
.lang-switcher button:hover { color:#1565C0; }
.lang-switcher button.active { background:#1565C0; color:#fff; }
</style>
</head>
<body class="text-gray-800 antialiased bg-gray-50">
<!-- 顶部栏 -->
<header class="fixed top-0 inset-x-0 z-50 bg-white/95 backdrop-blur border-b border-gray-100">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 h-16 flex items-center justify-between">
<a href="index.html" class="flex items-center" aria-label="返回官网">
<img src="assets/logo.png" alt="柏悦物流" class="auth-logo" style="width:161px;height:60px;object-fit:contain;">
</a>
<div class="lang-switcher" id="langSwitcher">
<button data-lang="zh" class="active" title="中文">中</button>
<button data-lang="en" title="English">EN</button>
<button data-lang="es" title="Español (México)">🇲🇽</button>
</div>
</div>
</header>
<section id="registerSection" class="min-h-screen pt-24 pb-12 bg-gray-50 flex items-center">
<div class="w-full max-w-6xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="bg-white rounded-3xl shadow-2xl overflow-hidden">
<div class="grid lg:grid-cols-2">
<!-- 左侧视觉 -->
<div class="hidden lg:block relative">
<img src="https://images.unsplash.com/photo-1566576912321-d58ddd7a6088?ixlib=rb-4.0.3&auto=format&fit=crop&w=2070&q=80" alt="物流仓储" class="absolute inset-0 w-full h-full object-cover">
<div class="absolute inset-0 gradient-bg opacity-90"></div>
<div class="relative z-10 p-12 h-full flex flex-col justify-between text-white">
<div>
<img src="assets/logo.png" alt="虫虫出海" style="width:161px;height:60px;object-fit:contain;">
<h2 class="text-3xl font-bold mb-4" data-i18n="reg_welcome">加入我们</h2>
<p class="text-white/80" data-i18n="reg_welcome_desc">注册虫虫出海企业账号,开启高效跨境物流体验。</p>
</div>
<div class="space-y-4">
<div class="flex items-center space-x-3">
<i class="fas fa-check-circle text-brand-green"></i>
<span data-i18n="reg_feat1">新用户专属优惠</span>
</div>
<div class="flex items-center space-x-3">
<i class="fas fa-check-circle text-brand-green"></i>
<span data-i18n="reg_feat2">免费获取物流方案</span>
</div>
<div class="flex items-center space-x-3">
<i class="fas fa-check-circle text-brand-green"></i>
<span data-i18n="reg_feat3">专属客户经理对接</span>
</div>
</div>
</div>
</div>
<!-- 右侧表单 -->
<div class="p-8 sm:p-12 lg:p-16">
<div class="max-w-md mx-auto">
<h2 class="text-3xl font-bold text-gray-900 mb-2" data-i18n="reg_title">企业注册</h2>
<p class="text-gray-600 mb-8"><span data-i18n="reg_have_account">已有账号?</span><button onclick="window.location.href='login.html'" class="text-brand-blue font-semibold hover:underline" data-i18n="reg_login_link">立即登录</button></p>
<form id="registerForm" class="space-y-5">
<div class="grid sm:grid-cols-2 gap-5">
<div>
<label class="block text-sm font-medium text-gray-700 mb-2" data-i18n="reg_company_label">企业名称</label>
<input type="text" name="company" required placeholder="请输入企业名称" data-i18n-ph="reg_company_ph" class="input-field w-full px-4 py-3.5 border border-gray-200 rounded-xl outline-none">
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-2" data-i18n="reg_contact_label">联系人</label>
<input type="text" name="contact" required placeholder="请输入联系人姓名" data-i18n-ph="reg_contact_ph" class="input-field w-full px-4 py-3.5 border border-gray-200 rounded-xl outline-none">
</div>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-2" data-i18n="reg_email_label">邮箱</label>
<div class="relative">
<i class="fas fa-envelope absolute left-4 top-1/2 -translate-y-1/2 text-gray-400"></i>
<input type="email" name="email" required placeholder="请输入企业邮箱" data-i18n-ph="reg_email_ph" class="input-field w-full pl-11 pr-4 py-3.5 border border-gray-200 rounded-xl outline-none">
</div>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-2" data-i18n="reg_phone_label">手机号</label>
<div class="relative">
<i class="fas fa-phone absolute left-4 top-1/2 -translate-y-1/2 text-gray-400"></i>
<input type="tel" name="phone" required placeholder="请输入手机号" data-i18n-ph="reg_phone_ph" class="input-field w-full pl-11 pr-4 py-3.5 border border-gray-200 rounded-xl outline-none">
</div>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-2" data-i18n="reg_password_label">密码</label>
<div class="relative">
<i class="fas fa-lock absolute left-4 top-1/2 -translate-y-1/2 text-gray-400"></i>
<input type="password" name="password" required placeholder="请设置8-20位密码" data-i18n-ph="reg_password_ph" class="input-field w-full pl-11 pr-12 py-3.5 border border-gray-200 rounded-xl outline-none">
<button type="button" class="toggle-password absolute right-4 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-600">
<i class="fas fa-eye"></i>
</button>
</div>
</div>
<div class="flex items-start">
<input type="checkbox" id="agree" required class="w-4 h-4 text-brand-blue rounded border-gray-300 mt-1 mr-2">
<label for="agree" class="text-sm text-gray-600">
<span data-i18n="reg_agree">我已阅读并同意</span> <button type="button" onclick="showToast(t('toast_tos'))" class="text-brand-blue hover:underline" data-i18n="reg_agree_tos">《用户服务协议》</button> 和 <button type="button" onclick="showToast(t('toast_pp'))" class="text-brand-blue hover:underline" data-i18n="reg_agree_pp">《隐私政策》</button>
</label>
</div>
<button type="submit" class="btn-accent w-full text-white font-bold py-4 rounded-xl text-lg" data-i18n="reg_submit">
立即注册
</button>
</form>
</div>
</div>
</div>
</div>
</div>
</section> <!-- Toast 提示 -->
<div id="toast" class="fixed top-24 left-1/2 -translate-x-1/2 z-[60] opacity-0 pointer-events-none transition-all duration-300 transform -translate-y-4">
<div class="bg-gray-900 text-white px-6 py-3 rounded-full shadow-2xl flex items-center space-x-2">
<i class="fas fa-info-circle text-brand-green"></i>
<span id="toastMessage">提示信息</span>
</div>
</div>
<script>
let currentLang = localStorage.getItem('cc_lang') || 'zh';
function t(key) {
return (i18n[currentLang] && i18n[currentLang][key] !== undefined) ? i18n[currentLang][key] : key;
}
const i18n = {
zh: {
reg_welcome: "加入我们",
reg_welcome_desc: "注册虫虫出海企业账号,开启高效跨境物流体验。",
reg_feat1: "新用户专属优惠",
reg_feat2: "免费获取物流方案",
reg_feat3: "专属客户经理对接",
reg_title: "企业注册",
reg_have_account: "已有账号?",
reg_login_link: "立即登录",
reg_company_label: "企业名称",
reg_company_ph: "请输入企业名称",
reg_contact_label: "联系人",
reg_contact_ph: "请输入联系人姓名",
reg_email_label: "邮箱",
reg_email_ph: "请输入企业邮箱",
reg_phone_label: "手机号",
reg_phone_ph: "请输入手机号",
reg_password_label: "密码",
reg_password_ph: "请设置8-20位密码",
reg_agree: "我已阅读并同意",
reg_agree_tos: "《用户服务协议》",
reg_agree_pp: "《隐私政策》",
reg_submit: "立即注册",
toast_tos: "服务协议即将上线",
toast_pp: "隐私政策即将上线",
toast_register_ok: "注册成功!欢迎加入虫虫出海"
},
en: {
reg_welcome: "Join Us",
reg_welcome_desc: "Register a Chongchong Global business account and start efficient cross-border logistics.",
reg_feat1: "Exclusive New-User Offers",
reg_feat2: "Free Logistics Plan",
reg_feat3: "Dedicated Account Manager",
reg_title: "Business Registration",
reg_have_account: "Already have an account?",
reg_login_link: "Log In Now",
reg_company_label: "Company Name",
reg_company_ph: "Enter company name",
reg_contact_label: "Contact Person",
reg_contact_ph: "Enter contact name",
reg_email_label: "Email",
reg_email_ph: "Enter company email",
reg_phone_label: "Phone Number",
reg_phone_ph: "Enter phone number",
reg_password_label: "Password",
reg_password_ph: "Set an 8-20 char password",
reg_agree: "I have read and agree to the",
reg_agree_tos: "User Agreement",
reg_agree_pp: "Privacy Policy",
reg_submit: "Sign Up Now",
toast_tos: "Terms of Service coming soon",
toast_pp: "Privacy Policy coming soon",
toast_register_ok: "Registration successful! Welcome to Chongchong Global"
},
es: {
reg_welcome: "Únete a nosotros",
reg_welcome_desc: "Regístrate en la cuenta empresarial de Chongchong Global y comienza una logística eficiente.",
reg_feat1: "Ofertas exclusivas para nuevos usuarios",
reg_feat2: "Plan logístico gratuito",
reg_feat3: "Gerente de cuenta dedicado",
reg_title: "Registro empresarial",
reg_have_account: "¿Ya tienes una cuenta?",
reg_login_link: "Inicia sesión ahora",
reg_company_label: "Nombre de empresa",
reg_company_ph: "Ingresa el nombre de empresa",
reg_contact_label: "Persona de contacto",
reg_contact_ph: "Ingresa el nombre de contacto",
reg_email_label: "Correo electrónico",
reg_email_ph: "Ingresa el correo empresarial",
reg_phone_label: "Número de teléfono",
reg_phone_ph: "Ingresa tu número de teléfono",
reg_password_label: "Contraseña",
reg_password_ph: "Define una contraseña de 8-20 caracteres",
reg_agree: "He leído y acepto los",
reg_agree_tos: "Términos de servicio",
reg_agree_pp: "Política de privacidad",
reg_submit: "Regístrate ahora",
toast_tos: "Términos de servicio próximamente",
toast_pp: "Política de privacidad próximamente",
toast_register_ok: "¡Registro exitoso! Bienvenido a Chongchong Global"
}
};
function applyLanguage(lang) {
if (!i18n[lang]) lang = 'zh';
currentLang = lang;
const langAttr = lang === 'zh' ? 'zh-CN' : (lang === 'es' ? 'es-MX' : 'en');
document.documentElement.setAttribute('lang', langAttr);
document.querySelectorAll('[data-i18n]').forEach(el => {
const k = el.getAttribute('data-i18n');
const v = i18n[lang][k];
if (v !== undefined) { if (v.indexOf('<br>') !== -1) el.innerHTML = v; else el.textContent = v; }
});
document.querySelectorAll('[data-i18n-ph]').forEach(el => {
const k = el.getAttribute('data-i18n-ph');
if (i18n[lang][k] !== undefined) el.setAttribute('placeholder', i18n[lang][k]);
});
document.querySelectorAll('[data-lang]').forEach(b => {
b.classList.toggle('active', b.getAttribute('data-lang') === lang);
});
try { localStorage.setItem('cc_lang', lang); } catch (e) {}
}
document.querySelectorAll('#langSwitcher [data-lang]').forEach(b => {
b.addEventListener('click', () => applyLanguage(b.getAttribute('data-lang')));
});
function showToast(message) {
const toast = document.getElementById('toast');
const toastMessage = document.getElementById('toastMessage');
toastMessage.textContent = message;
toast.classList.remove('opacity-0', 'pointer-events-none', '-translate-y-4');
toast.classList.add('opacity-100', 'translate-y-0');
setTimeout(() => {
toast.classList.add('opacity-0', 'pointer-events-none', '-translate-y-4');
toast.classList.remove('opacity-100', 'translate-y-0');
}, 3000);
}
function updateAuthUI() {}
document.querySelectorAll('.toggle-password').forEach(button => {
button.addEventListener('click', function() {
const input = this.previousElementSibling;
const icon = this.querySelector('i');
if (input.type === 'password') { input.type = 'text'; icon.classList.remove('fa-eye'); icon.classList.add('fa-eye-slash'); }
else { input.type = 'password'; icon.classList.remove('fa-eye-slash'); icon.classList.add('fa-eye'); }
});
});
document.getElementById('registerForm').addEventListener('submit', function(e) {
e.preventDefault();
showToast(t('toast_register_ok'));
setTimeout(() => window.location.href = 'workbench.html', 900);
});
applyLanguage(currentLang);
</script>
</body>
</html>
File diff suppressed because it is too large Load Diff
Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

File diff suppressed because it is too large Load Diff
Binary file not shown.

After

Width:  |  Height:  |  Size: 764 KiB

+49
View File
@@ -0,0 +1,49 @@
const http = require('http');
const fs = require('fs');
const path = require('path');
const PORT = 8081;
const mimeTypes = {
'.html': 'text/html',
'.js': 'text/javascript',
'.css': 'text/css',
'.json': 'application/json',
'.png': 'image/png',
'.jpg': 'image/jpg',
'.gif': 'image/gif',
'.svg': 'image/svg+xml',
'.ico': 'image/x-icon'
};
const server = http.createServer((req, res) => {
console.log(`${req.method} ${req.url}`);
let filePath = '.' + req.url;
if (filePath === './') {
filePath = './index.html';
}
const extname = String(path.extname(filePath)).toLowerCase();
const contentType = mimeTypes[extname] || 'application/octet-stream';
fs.readFile(filePath, (error, content) => {
if (error) {
if (error.code === 'ENOENT') {
res.writeHead(404, { 'Content-Type': 'text/html' });
res.end('<h1>404 Not Found</h1>', 'utf-8');
} else {
res.writeHead(500);
res.end('Server Error: ' + error.code, 'utf-8');
}
} else {
res.writeHead(200, { 'Content-Type': contentType });
res.end(content, 'utf-8');
}
});
});
server.listen(PORT, () => {
console.log(`[TraePreviewUrl]: http://localhost:${PORT}`);
console.log(`Server running at http://localhost:${PORT}/`);
});
@@ -0,0 +1,290 @@
#!/usr/bin/env python3
"""
柏悦WMS - 分阶段脚本
Phase 1: 登录并保存cookies (需要可见浏览器+手动验证码)
Phase 2: 加载cookies, headless模式抓取所有页面
用法:
python3 scrape_wms_final.py login # 阶段1: 登录保存cookie
python3 scrape_wms_final.py scrape # 阶段2: 用cookie抓取
"""
import asyncio, json, sys, os, time
from playwright.async_api import async_playwright
BASE = "http://39.101.149.173"
API_BASE = "https://adtest.bylogistic.cn"
USERNAME = "admin"
PASSWORD = "ndui#Emdfj23123"
COOKIE_FILE = "/tmp/wms_cookies.json"
OUTPUT_DIR = os.path.expanduser("~/Documents/柏悦")
os.makedirs(OUTPUT_DIR, exist_ok=True)
async def phase_login():
"""打开浏览器让用户登录,然后保存cookies"""
async with async_playwright() as p:
browser = await p.chromium.launch(headless=False, args=["--start-maximized"])
context = await browser.new_context(viewport={"width": 1440, "height": 900})
page = await context.new_page()
print("=== 打开登录页 ===")
await page.goto(f"{BASE}/user/login?redirect=%2Fwms%2FByInstoreList", timeout=30000)
await page.wait_for_timeout(2000)
# 自动填充
inputs_text = page.locator("input[type='text']")
input_pwd = page.locator("input[type='password']")
await inputs_text.nth(0).fill(USERNAME)
await input_pwd.fill(PASSWORD)
print("✅ 账号密码已填写")
print("👉 请在浏览器中输入验证码并点击'确定'")
print("⏳ 等待登录成功...")
try:
await page.wait_for_function(
"() => !window.location.pathname.includes('login')",
timeout=300000 # 等5分钟
)
print(f"✅ 登录成功! URL: {page.url}")
except Exception as e:
print(f"❌ 等待超时: {e}")
try:
await browser.close()
except:
pass
return False
# 立即保存cookies(不等页面加载完)
try:
await page.wait_for_timeout(3000)
except:
pass
try:
cookies = await context.cookies()
with open(COOKIE_FILE, "w") as f:
json.dump(cookies, f, indent=2)
print(f"✅ Cookies已保存到 {COOKIE_FILE} ({len(cookies)} 个)")
except Exception as ce:
print(f"⚠️ Cookie保存出错: {ce}")
# 截图当前页
try:
await page.screenshot(path="/tmp/wms_logged_in.png", full_page=True)
print("📸 当前页面截图: /tmp/wms_logged_in.png")
except Exception as se:
print(f"⚠️ 截图出错: {se}")
# 保持浏览器打开
print("\n浏览器将保持10秒后关闭...")
try:
await asyncio.sleep(10)
except:
pass
try:
await browser.close()
except:
pass
return True
async def phase_scrape():
"""加载cookies并headless抓取所有页面"""
# 读取cookies
if not os.path.exists(COOKIE_FILE):
print(f"❌ Cookie文件不存在: {COOKIE_FILE}")
print("请先运行: python3 scrape_wms_final.py login")
return
with open(COOKIE_FILE) as f:
saved_cookies = json.load(f)
print(f"🍪 加载了 {len(saved_cookies)} 个cookies")
async with async_playwright() as p:
browser = await p.chromium.launch(headless=True)
context = await browser.new_context(viewport={"width": 1440, "height": 900})
await context.add_cookies(saved_cookies)
page = await context.new_page()
# 监听API
all_apis = []
page.on("response", lambda r: all_apis.append({
"url": r.url, "status": r.status, "method": r.request.method
}))
# 先访问入库单列表(默认首页)
print("\n=== 访问系统首页 ===")
await page.goto(f"{BASE}/wms/ByInstoreList", timeout=20000)
await page.wait_for_timeout(4000)
if "login" in page.url.lower():
print("❌ Cookies已失效,请重新运行 login 命令")
await browser.close()
return
print(f"✅ 已进入系统: {page.url}")
# ===== 抓取菜单结构 =====
print("\n=== 抓取菜单结构 ===")
menu_structure = {}
# 一级菜单
top_menus = page.locator(".ant-menu-submenu-title")
top_count = await top_menus.count()
for i in range(top_count):
try:
name = (await top_menus.nth(i).inner_text()).strip().split("\n")[0].strip()
if not name or len(name) > 20:
continue
# 展开菜单
await top_menus.nth(i).click()
await page.wait_for_timeout(600)
# 获取子菜单
sub_items = page.locator(".ant-menu-submenu-open .ant-menu-item")
subs = []
for j in range(await sub_items.count()):
try:
sn = (await sub_items.nth(j).inner_text()).strip().split("\n")[0]
if sn and len(sn) < 30:
subs.append(sn)
except:
pass
menu_structure[name] = subs
print(f" 📂 {name}: {subs}")
# 收起
await top_menus.nth(i).click()
await page.wait_for_timeout(300)
except Exception as e:
pass
# 所有要访问的页面路径
pages_to_visit = [
("首页", "/wms/Dashboard"),
("入库单列表", "/wms/ByInstoreList"),
("入库单测量", "/wms/ByInstoreList4Measured"),
("测量数据审核", "/wms/ByInstoreList4MeasuredAudit"),
("接收审核", "/wms/ByInstoreList4ReceiveAudit"),
("客户管理", "/wms/ByCustomerList"),
("出库单列表", "/wms/ByOutstoreList"),
("派送单列表", "/wms/ByDispatchList"),
("货柜管理", "/wms/ByContainerList"),
("库存管理", "/wms/ByInventoryList"),
("财务结算", "/wms/ByFinanceList"),
("托盘管理", "/wms/ByTrayList"),
("BOL管理", "/wms/ByBolList"),
("用户管理", "/system/UserList"),
("角色管理", "/system/RoleList"),
("菜单管理", "/system/MenuList"),
("字典管理", "/system/DictList"),
("参数配置", "/system/ConfigList"),
]
# ===== 逐页抓取 =====
print(f"\n=== 逐页抓取 (共{len(pages_to_visit)}页) ===\n")
pages_data = {}
for idx, (name, path) in enumerate(pages_to_visit, 1):
try:
url = BASE + path
print(f"[{idx}/{len(pages_to_visit)}] {name} -> {path}", end=" ... ")
await page.goto(url, timeout=15000)
await page.wait_for_timeout(2500)
if "login" in page.url.lower():
print("❌ 会话过期!")
break
entry = {
"title": await page.title(),
"url": page.url,
}
# 表格列头
headers = []
ths = page.locator(".ant-table-thead th, thead th, [class*='table'] th")
for i in range(min(await ths.count(), 25)):
h = (await ths.nth(i).inner_text()).strip()
if h and h not in headers:
headers.append(h)
# 搜索条件字段
fields = []
labels = page.locator(".ant-form-item-label label, .query-group label, label[for]")
for i in range(min(await labels.count(), 20)):
t = (await labels.nth(i).inner_text()).strip()
if t and t not in fields and len(t) < 30:
fields.append(t)
# 操作按钮
buttons = []
btns = page.locator(".ant-btn:not(.ant-pagination-item):not(.ant-pagination-options-quick-jumper):not([class*='link'])")
for i in range(min(await btns.count(), 15)):
b = (await btns.nth(i).inner_text()).strip()
if b and b not in buttons and len(b) < 20 and b not in ["刷 新","关 闭"]:
buttons.append(b)
# 统计卡片数字
stat_vals = []
stats = page.locator("[class*='statistic'] [class*='value'], [class*='number-card'] [class*='value']")
for i in range(min(await stats.count(), 10)):
s = (await stats.nth(i).inner_text()).strip()
if s:
stat_vals.append(s)
entry["table_headers"] = headers
entry["search_fields"] = fields
entry["buttons"] = buttons
entry["statistics"] = stat_vals
pages_data[name] = entry
print(f"✅ 表头:{len(headers)} 字段:{len(fields)} 按钮:{len(buttons)}")
# 截图每个页面
safe_name = name.replace("/", "_").replace("\\", "_")
await page.screenshot(path=f"/tmp/wms_page_{safe_name}.png")
except Exception as e:
print(f"❌ {e}")
pages_data[name] = {"error": str(e)}
# 保存完整数据
full_data = {
"system_name": "柏悦物流管理系统 (Baiyue WMS)",
"base_url": BASE,
"api_base": API_BASE,
"scrape_time": time.strftime("%Y-%m-%d %H:%M:%S"),
"menu_structure": menu_structure,
"pages": pages_data,
"api_calls": all_apis[-50:], # 最后50个API调用
}
output_file = os.path.join(OUTPUT_DIR, "wms_system_data.json")
with open(output_file, "w", encoding="utf-8") as f:
json.dump(full_data, f, indent=2, ensure_ascii=False)
print(f"\n{'='*60}")
print(f"✅ 全部完成!")
print(f" - 菜单: {len(menu_structure)} 个一级模块")
print(f" - 页面: {len(pages_data)} 个页面")
print(f" - 输出: {output_file}")
print(f"{'='*60}")
await browser.close()
if __name__ == "__main__":
cmd = sys.argv[1] if len(sys.argv) > 1 else "login"
if cmd == "login":
asyncio.run(phase_login())
elif cmd == "scrape":
asyncio.run(phase_scrape())
else:
print(f"Usage: python3 {sys.argv[0]} [login|scrape]")
@@ -0,0 +1,207 @@
#!/usr/bin/env python3
"""
从可见浏览器中提取token,然后用API方式抓取所有数据
"""
import asyncio, json, os, time
from playwright.async_api import async_playwright
BASE = "http://39.101.149.173"
USERNAME = "admin"
PASSWORD = "ndui#Emdfj23123"
TOKEN_FILE = "/tmp/wms_token.json"
OUTPUT_DIR = os.path.expanduser("~/Documents/柏悦")
async def main():
async with async_playwright() as p:
browser = await p.chromium.launch(headless=False, args=["--start-maximized"])
context = await browser.new_context(viewport={"width": 1440, "height": 900})
page = await context.new_page()
print("=== 打开登录页 ===")
await page.goto(f"{BASE}/user/login?redirect=%2Fwms%2FByInstoreList", timeout=30000)
await page.wait_for_timeout(2000)
# 填充
inputs = page.locator("input[type='text']")
pwd = page.locator("input[type='password']")
await inputs.nth(0).fill(USERNAME)
await pwd.fill(PASSWORD)
print("👉 请在浏览器中输入验证码并点击确定...")
try:
await page.wait_for_function(
"() => !window.location.pathname.includes('login')",
timeout=300000
)
print("✅ 登录成功!")
except:
print("❌ 超时")
await browser.close()
return
await page.wait_for_timeout(3000)
# ========== 抓取完整菜单结构 ==========
print("\n=== 抓取菜单 ===")
menu_structure = {}
page_list = [] # [(display_name, url_path)]
top_menus = page.locator(".ant-menu-submenu-title")
for i in range(await top_menus.count()):
try:
name = (await top_menus.nth(i).inner_text()).strip().split("\n")[0].strip()
if not name or len(name) > 20:
continue
await top_menus.nth(i).click()
await page.wait_for_timeout(500)
subs = []
sub_items = page.locator(".ant-menu-submenu-open .ant-menu-item")
for j in range(await sub_items.count()):
sn = (await sub_items.nth(j).inner_text()).strip().split("\n")[0]
if sn and len(sn) < 30:
subs.append(sn)
menu_structure[name] = subs
print(f" {name}: {subs}")
await top_menus.nth(i).click()
await page.wait_for_timeout(300)
except:
pass
# ========== 逐页截图抓取 ==========
print(f"\n=== 逐页抓取 ===")
# 构建所有要访问的页面
all_pages = [("首页", "/wms/Dashboard")]
for menu_name, subs in menu_structure.items():
for sub in subs:
# 推断URL路径
path_map = {
"入库单列表": "/wms/ByInstoreList",
"入库单测量": "/wms/ByInstoreList4Measured",
"测量数据审核": "/wms/ByInstoreList4MeasuredAudit",
"接收审核": "/wms/ByInstoreList4ReceiveAudit",
"客户管理": "/wms/ByCustomerList",
"出库单列表": "/wms/ByOutstoreList",
"派送单列表": "/wms/ByDispatchList",
"BOL管理": "/wms/ByBolList",
"货柜管理": "/wms/ByContainerList",
"库存管理": "/wms/ByInventoryList",
"财务结算": "/wms/ByFinanceList",
"托盘管理": "/wms/ByTrayList",
"用户管理": "/system/UserList",
"角色管理": "/system/RoleList",
"菜单管理": "/system/MenuList",
"字典管理": "/system/DictList",
"参数配置": "/system/ConfigList",
"通用配置": "/system/ConfigList",
"报表设计": "/wms/Dashboard", # 待确认
"消息中心": "/wms/Dashboard",
"常见案例": "/wms/Dashboard",
"在线开发": "/wms/Dashboard",
"系统监控": "/wms/Dashboard",
"统计报表": "/wms/Dashboard",
}
path = path_map.get(sub, "")
if path:
all_pages.append((f"{menu_name} > {sub}", path))
# 去重
seen = set()
unique_pages = []
for name, path in all_pages:
if path not in seen:
seen.add(path)
unique_pages.append((name, path))
pages_data = {}
success_count = 0
for idx, (name, path) in enumerate(unique_pages, 1):
try:
url = BASE + path
print(f"[{idx}/{len(unique_pages)}] {name}", end=" ... ")
await page.goto(url, timeout=15000)
await page.wait_for_timeout(2500)
if "login" in page.url.lower():
print("会话过期!")
break
# 抓表格列头
headers = []
th_selectors = [".ant-table-thead th", "thead th", "table th"]
for ths in th_selectors:
els = page.locator(ths)
for i in range(min(await els.count(), 30)):
try:
h = (await els.nth(i).inner_text()).strip()
if h and h not in headers:
headers.append(h)
except:
pass
# 搜索字段
fields = []
labels = page.locator("label, .ant-form-item-label")
for i in range(min(await labels.count(), 25)):
try:
t = (await labels.nth(i).inner_text()).strip()
if t and t not in fields and len(t) < 30:
fields.append(t)
except:
pass
# 操作按钮
buttons = []
btns = page.locator("button:not([class*='pagination']):not([class*='quick-jumper'])")
for i in range(min(await btns.count(), 20)):
try:
b = (await btns.nth(i).inner_text()).strip()
if b and b not in buttons and len(b) < 25:
buttons.append(b)
except:
pass
pages_data[name] = {
"url": url,
"title": await page.title(),
"table_headers": headers,
"search_fields": fields,
"buttons": buttons,
}
print(f"✅ headers:{len(headers)} fields:{len(fields)} btns:{len(buttons)}")
success_count += 1
except Exception as e:
print(f"❌ {str(e)[:80]}")
pages_data[name] = {"error": str(e)[:200]}
# 保存数据
output = {
"system_name": "柏悦物流管理系统 (Baiyue WMS)",
"scrape_time": time.strftime("%Y-%m-%d %H:%M:%S"),
"menu_structure": menu_structure,
"pages": pages_data,
"pages_count": success_count,
}
out_path = os.path.join(OUTPUT_DIR, "wms_full_data.json")
with open(out_path, "w", encoding="utf-8") as f:
json.dump(output, f, indent=2, ensure_ascii=False)
print(f"\n✅ 完成! {success_count}/{len(unique_pages)} 页面成功抓取")
print(f"输出: {out_path}")
print("\n浏览器10秒后关闭...")
await asyncio.sleep(10)
await browser.close()
asyncio.run(main())
@@ -0,0 +1,322 @@
{
"system_name": "柏悦物流管理系统 (Baiyue WMS)",
"scrape_time": "2026-06-01 12:06:00",
"menu_structure": {
"客户管理": [
"客户公司管理",
"用户资料",
"海外仓阶梯费用",
"客户账户流水"
],
"入库": [
"入库单列表",
"入库单测量",
"测量数据审核",
"接收审核"
],
"出库": [
"派送单列表",
"BOL",
"自提单"
],
"货柜管理": [
"货柜列表",
"装柜清单"
],
"库存": [
"海外仓库存查询",
"理货管理",
"报损单"
],
"财务结算": [
"客户应收应付",
"客户对账单",
"结算单位应付",
"结算单位对账单",
"洛杉矶仓储成本"
],
"托盘管理": [
"托盘列表",
"生成托盘"
],
"通用": [
"产品信息",
"费用项目",
"FBA编码地址",
"托盘管理",
"库位/货架管理",
"订单流转路径",
"快递公司",
"往来单位"
],
"常见案例": [
"自定义组件",
"对象存储"
],
"在线开发": [
"Online表单开发"
],
"报表设计": [
"积木报表设计",
"生产销售监控",
"智慧物流监控"
],
"消息中心": [
"模板管理",
"消息管理",
"我的消息"
],
"系统管理": [
"用户管理",
"角色管理",
"菜单管理",
"租户管理",
"部门管理",
"我的部门",
"职务管理",
"通讯录",
"数据字典",
"分类字典",
"系统通告",
"菜单管理2"
],
"系统监控": [
"在线用户",
"路由网关",
"定时任务",
"日志管理",
"数据日志",
"SQL监控",
"在线文档"
]
},
"pages": {
"首页": {
"url": "http://39.101.149.173/wms/Dashboard",
"title": "柏悦物流管理系统",
"table_headers": [],
"search_fields": [],
"buttons": [
"返回首页"
]
},
"入库 > 入库单列表": {
"url": "http://39.101.149.173/wms/ByInstoreList",
"title": "入库单列表 · 柏悦物流管理系统",
"table_headers": [
"仓库",
"交货仓",
"配载状态",
"是否扣货",
"扣货理由",
"集装箱号",
"柜号",
"公司",
"业务类型",
"提货方式",
"提货时间",
"提货联系人电话",
"提货地址",
"提货邮编",
"提货联系人邮箱",
"渠道",
"买单/退税",
"是否整柜",
"商检验",
"开船时间",
"到港时间",
"总箱数",
"总毛重(T.K.GW)"
],
"search_fields": [
"定位仓库",
"交货仓",
"入仓码",
"订单状态",
"费用审核状态"
],
"buttons": [
"查询",
"重置",
"国内仓下单",
"海外分仓下单",
"整托下单",
"高级查询",
"导出报表",
"导出入库清单总表",
"导出唛头",
"导出FBA流转单ZIP",
"下载"
]
},
"入库 > 入库单测量": {
"url": "http://39.101.149.173/wms/ByInstoreList4Measured",
"title": "入库单测量 · 柏悦物流管理系统",
"table_headers": [
"入仓码",
"仓库",
"订单状态",
"配载状态",
"测量数据审核状态",
"唛头文件",
"是否扣货",
"扣货理由",
"公司",
"业务类型",
"渠道",
"买单/退税",
"是否整柜",
"商检验",
"总箱数",
"总毛重(T.K.GW)",
"总体积(m³)",
"总材积重",
"总净重(T.K.NW)",
"目的仓类型",
"目的仓",
"货件编号",
"测量完成时间",
"操作"
],
"search_fields": [
"仓库",
"入仓码",
"公司",
"FBA货件编号"
],
"buttons": [
"查询",
"重置",
"下载"
]
},
"入库 > 测量数据审核": {
"url": "http://39.101.149.173/wms/ByInstoreList4MeasuredAudit",
"title": "测量数据审核 · 柏悦物流管理系统",
"table_headers": [
"入仓码",
"仓库",
"订单状态",
"配载状态",
"测量数据审核状态",
"唛头文件",
"是否扣货",
"扣货理由",
"公司",
"业务类型",
"渠道",
"买单/退税",
"是否整柜",
"商检验",
"总箱数",
"总毛重(T.K.GW)",
"总体积(m³)",
"总材积重",
"总净重(T.K.NW)",
"目的仓类型",
"目的仓",
"货件编号",
"测量完成时间",
"操作"
],
"search_fields": [
"仓库",
"入仓码",
"公司",
"FBA货件编号"
],
"buttons": [
"查询",
"重置",
"下载"
]
},
"入库 > 接收审核": {
"url": "http://39.101.149.173/wms/ByInstoreList4ReceiveAudit",
"title": "接收审核 · 柏悦物流管理系统",
"table_headers": [
"入仓码",
"仓库",
"订单状态",
"配载状态",
"测量数据审核状态",
"唛头文件",
"是否扣货",
"扣货理由",
"公司",
"业务类型",
"渠道",
"买单/退税",
"是否整柜",
"商检验",
"总箱数",
"总毛重(T.K.GW)",
"总体积(m³)",
"总材积重",
"总净重(T.K.NW)",
"目的仓类型",
"目的仓",
"货件编号",
"测量完成时间",
"操作"
],
"search_fields": [
"仓库",
"入仓码",
"公司",
"FBA货件编号"
],
"buttons": [
"查询",
"重置",
"下载"
]
},
"出库 > 派送单列表": {
"url": "http://39.101.149.173/wms/ByDispatchList",
"title": "柏悦物流管理系统",
"table_headers": [],
"search_fields": [],
"buttons": [
"返回首页"
]
},
"通用 > 托盘管理": {
"url": "http://39.101.149.173/wms/ByTrayList",
"title": "柏悦物流管理系统",
"table_headers": [],
"search_fields": [],
"buttons": [
"返回首页"
]
},
"系统管理 > 用户管理": {
"url": "http://39.101.149.173/system/UserList",
"title": "柏悦物流管理系统",
"table_headers": [],
"search_fields": [],
"buttons": [
"返回首页"
]
},
"系统管理 > 角色管理": {
"url": "http://39.101.149.173/system/RoleList",
"title": "柏悦物流管理系统",
"table_headers": [],
"search_fields": [],
"buttons": [
"返回首页"
]
},
"系统管理 > 菜单管理": {
"url": "http://39.101.149.173/system/MenuList",
"title": "柏悦物流管理系统",
"table_headers": [],
"search_fields": [],
"buttons": [
"返回首页"
]
}
},
"pages_count": 10
}
@@ -0,0 +1,393 @@
{
"menu_items": [
{
"text": "首页",
"selector": ".ant-menu-item",
"href": ""
},
{
"text": "入库单列表",
"selector": ".ant-menu-item",
"href": ""
},
{
"text": "入库单测量",
"selector": ".ant-menu-item",
"href": ""
},
{
"text": "测量数据审核",
"selector": ".ant-menu-item",
"href": ""
},
{
"text": "接收审核",
"selector": ".ant-menu-item",
"href": ""
},
{
"text": "刷 新",
"selector": ".ant-menu-item",
"href": ""
},
{
"text": "关闭左侧",
"selector": ".ant-menu-item",
"href": ""
},
{
"text": "关闭右侧",
"selector": ".ant-menu-item",
"href": ""
},
{
"text": "关闭其它",
"selector": ".ant-menu-item",
"href": ""
},
{
"text": "客户管理",
"selector": ".ant-menu-submenu-title",
"href": ""
},
{
"text": "入库",
"selector": ".ant-menu-submenu-title",
"href": ""
},
{
"text": "出库",
"selector": ".ant-menu-submenu-title",
"href": ""
},
{
"text": "货柜管理",
"selector": ".ant-menu-submenu-title",
"href": ""
},
{
"text": "库存",
"selector": ".ant-menu-submenu-title",
"href": ""
},
{
"text": "财务结算",
"selector": ".ant-menu-submenu-title",
"href": ""
},
{
"text": "托盘管理",
"selector": ".ant-menu-submenu-title",
"href": ""
},
{
"text": "通用",
"selector": ".ant-menu-submenu-title",
"href": ""
},
{
"text": "常见案例",
"selector": ".ant-menu-submenu-title",
"href": ""
},
{
"text": "在线开发",
"selector": ".ant-menu-submenu-title",
"href": ""
},
{
"text": "报表设计",
"selector": ".ant-menu-submenu-title",
"href": ""
},
{
"text": "消息中心",
"selector": ".ant-menu-submenu-title",
"href": ""
},
{
"text": "系统管理",
"selector": ".ant-menu-submenu-title",
"href": ""
},
{
"text": "系统监控",
"selector": ".ant-menu-submenu-title",
"href": ""
},
{
"text": "统计报表",
"selector": ".ant-menu-submenu-title",
"href": ""
},
{
"text": "入库 入库单列表 入库单测量 测量数据审核 接收审核",
"selector": "li[role='menuitem']",
"href": ""
},
{
"text": "定位仓库 请选择仓库",
"selector": "[class*='menu'] [class*='item']",
"href": ""
},
{
"text": "定位仓库",
"selector": "[class*='menu'] [class*='item']",
"href": ""
},
{
"text": "请选择仓库",
"selector": "[class*='menu'] [class*='item']",
"href": ""
},
{
"text": "交货仓 请选择交货仓",
"selector": "[class*='menu'] [class*='item']",
"href": ""
},
{
"text": "交货仓",
"selector": "[class*='menu'] [class*='item']",
"href": ""
},
{
"text": "请选择交货仓",
"selector": "[class*='menu'] [class*='item']",
"href": ""
},
{
"text": "入仓码",
"selector": "[class*='menu'] [class*='item']",
"href": ""
},
{
"text": "订单状态 请选择订单状态",
"selector": "[class*='menu'] [class*='item']",
"href": ""
},
{
"text": "订单状态",
"selector": "[class*='menu'] [class*='item']",
"href": ""
},
{
"text": "请选择订单状态",
"selector": "[class*='menu'] [class*='item']",
"href": ""
},
{
"text": "费用审核状态 请选择费用审核状态",
"selector": "[class*='menu'] [class*='item']",
"href": ""
},
{
"text": "费用审核状态",
"selector": "[class*='menu'] [class*='item']",
"href": ""
},
{
"text": "请选择费用审核状态",
"selector": "[class*='menu'] [class*='item']",
"href": ""
},
{
"text": "1",
"selector": "[class*='menu'] [class*='item']",
"href": ""
},
{
"text": "2",
"selector": "[class*='menu'] [class*='item']",
"href": ""
},
{
"text": "3",
"selector": "[class*='menu'] [class*='item']",
"href": ""
},
{
"text": "4",
"selector": "[class*='menu'] [class*='item']",
"href": ""
},
{
"text": "5",
"selector": "[class*='menu'] [class*='item']",
"href": ""
},
{
"text": "•••",
"selector": "[class*='menu'] [class*='item']",
"href": ""
},
{
"text": "36",
"selector": "[class*='menu'] [class*='item']",
"href": ""
}
],
"all_links": [
"/system/ConfigList",
"/system/DictList",
"/system/MenuList",
"/system/RoleList",
"/system/UserList",
"/wms/ByBolList",
"/wms/ByContainerList",
"/wms/ByCustomerList",
"/wms/ByDispatchList",
"/wms/ByFinanceList",
"/wms/ByInstoreList",
"/wms/ByInstoreList4Measured",
"/wms/ByInstoreList4MeasuredAudit",
"/wms/ByInstoreList4ReceiveAudit",
"/wms/ByInventoryList",
"/wms/ByOutstoreList",
"/wms/ByTrayList",
"/wms/Dashboard",
"/wms/home",
"/wms/index"
],
"api_calls": [
{
"url": "http://39.101.149.173/user/login?redirect=%2Fwms%2FByInstoreList",
"status": 200,
"method": "GET"
},
{
"url": "https://adtest.bylogistic.cn/sys/randomImage/1780285671792?_t=1780285671",
"status": 200,
"method": "GET"
},
{
"url": "https://adtest.bylogistic.cn/sys/randomImage/1780285691766?_t=1780285691",
"status": 200,
"method": "GET"
},
{
"url": "https://adtest.bylogistic.cn/sys/randomImage/1780285694400?_t=1780285694",
"status": 200,
"method": "GET"
},
{
"url": "https://adtest.bylogistic.cn/sys/login",
"status": 200,
"method": "POST"
},
{
"url": "https://adtest.bylogistic.cn/sys/randomImage/1780285703728?_t=1780285703",
"status": 200,
"method": "GET"
},
{
"url": "https://adtest.bylogistic.cn/sys/login",
"status": 200,
"method": "POST"
},
{
"url": "https://adtest.bylogistic.cn/sys/randomImage/1780285711155?_t=1780285711",
"status": 200,
"method": "GET"
},
{
"url": "https://adtest.bylogistic.cn/sys/login",
"status": 200,
"method": "POST"
},
{
"url": "http://39.101.149.173/user/login?redirect=%2Fwms%2FByInstoreList",
"status": 200,
"method": "GET"
},
{
"url": "https://adtest.bylogistic.cn/sys/randomImage/1780285725968?_t=1780285725",
"status": 200,
"method": "GET"
},
{
"url": "https://adtest.bylogistic.cn/sys/randomImage/1780285750913?_t=1780285750",
"status": 200,
"method": "GET"
},
{
"url": "https://adtest.bylogistic.cn/sys/login",
"status": 200,
"method": "POST"
},
{
"url": "https://adtest.bylogistic.cn/sys/permission/getUserPermissionByToken?_t=1780285758",
"status": 200,
"method": "GET"
},
{
"url": "https://adtest.bylogistic.cn/sys/dict/getDictItems/by_company,company_name,id",
"status": 200,
"method": "GET"
},
{
"url": "https://adtest.bylogistic.cn/sys/dict/getDictItems/by_order_status,name,value,value%20not%20in%20(1001%5E%5E1002)",
"status": 200,
"method": "GET"
},
{
"url": "https://adtest.bylogistic.cn/sys/dict/getDictItems/by_customer,realname,id",
"status": 200,
"method": "GET"
},
{
"url": "https://adtest.bylogistic.cn/sys/dict/getDictItems/by_store,store_name,store_no",
"status": 200,
"method": "GET"
},
{
"url": "https://adtest.bylogistic.cn/sys/dict/getDictItems/by_order_status,name,value",
"status": 200,
"method": "GET"
},
{
"url": "https://adtest.bylogistic.cn/sys/dict/getDictItems/by_store,store_name,store_no,parent_store_id!=''",
"status": 200,
"method": "GET"
},
{
"url": "https://adtest.bylogistic.cn/sys/dict/getDictItems/by_order_path,path_name,id",
"status": 200,
"method": "GET"
},
{
"url": "https://adtest.bylogistic.cn/sys/dict/getDictItems/by_fba,fba_no,fba_no",
"status": 200,
"method": "GET"
},
{
"url": "https://adtest.bylogistic.cn/sys/annountCement/listByUser?_t=1780285758",
"status": 200,
"method": "GET"
},
{
"url": "https://adtest.bylogistic.cn/sys/permission/getUserPermissionByToken?_t=1780285762",
"status": 200,
"method": "GET"
},
{
"url": "https://adtest.bylogistic.cn/sys/loginfo?_t=1780285763",
"status": 200,
"method": "GET"
},
{
"url": "https://adtest.bylogistic.cn/sys/visitInfo?_t=1780285763",
"status": 200,
"method": "GET"
},
{
"url": "https://adtest.bylogistic.cn/sys/annountCement/listByUser?_t=1780285763",
"status": 200,
"method": "GET"
},
{
"url": "https://adtest.bylogistic.cn/sys/permission/getUserPermissionByToken?_t=1780285764",
"status": 200,
"method": "GET"
}
]
}
@@ -0,0 +1,67 @@
{
"/system/ConfigList": {
"title": "柏悦物流管理系统",
"url": "http://39.101.149.173/dashboard/analysis",
"headers": [],
"buttons": [],
"form_fields": [],
"content_preview": "欢迎进入 柏悦物流管理系统\n欢迎您,管理员 退出登录\n首页\nCopyright 2022 BaiYue Logisitcs"
},
"/system/DictList": {
"error": "Page.wait_for_timeout: Target page, context or browser has been closed"
},
"/system/MenuList": {
"error": "Page.goto: Target page, context or browser has been closed"
},
"/system/RoleList": {
"error": "Page.goto: Target page, context or browser has been closed"
},
"/system/UserList": {
"error": "Page.goto: Target page, context or browser has been closed"
},
"/wms/ByBolList": {
"error": "Page.goto: Target page, context or browser has been closed"
},
"/wms/ByContainerList": {
"error": "Page.goto: Target page, context or browser has been closed"
},
"/wms/ByCustomerList": {
"error": "Page.goto: Target page, context or browser has been closed"
},
"/wms/ByDispatchList": {
"error": "Page.goto: Target page, context or browser has been closed"
},
"/wms/ByFinanceList": {
"error": "Page.goto: Target page, context or browser has been closed"
},
"/wms/ByInstoreList": {
"error": "Page.goto: Target page, context or browser has been closed"
},
"/wms/ByInstoreList4Measured": {
"error": "Page.goto: Target page, context or browser has been closed"
},
"/wms/ByInstoreList4MeasuredAudit": {
"error": "Page.goto: Target page, context or browser has been closed"
},
"/wms/ByInstoreList4ReceiveAudit": {
"error": "Page.goto: Target page, context or browser has been closed"
},
"/wms/ByInventoryList": {
"error": "Page.goto: Target page, context or browser has been closed"
},
"/wms/ByOutstoreList": {
"error": "Page.goto: Target page, context or browser has been closed"
},
"/wms/ByTrayList": {
"error": "Page.goto: Target page, context or browser has been closed"
},
"/wms/Dashboard": {
"error": "Page.goto: Target page, context or browser has been closed"
},
"/wms/home": {
"error": "Page.goto: Target page, context or browser has been closed"
},
"/wms/index": {
"error": "Page.goto: Target page, context or browser has been closed"
}
}
@@ -0,0 +1,510 @@
# 柏悦物流管理系统(Baiyue WMS)系统文档
> **版本**: 基于测试系统 2026-06-01 抓取分析
> **访问地址**: http://39.101.149.173
> **后端API**: https://adtest.bylogistic.cn
> **技术栈**: Ant Design Vue + Spring Boot + MySQL + Redis
> **测试账号**: admin / ndui#Emdfj23123
---
## 目录
1. [系统概述](#1-系统概述)
2. [技术架构](#2-技术架构)
3. [首页仪表盘](#3-首页仪表盘)
4. [客户管理模块](#4-客户管理模块)
5. [入库管理模块](#5-入库管理模块)
6. [出库管理模块](#6-出库管理模块)
7. [货柜管理模块](#7-货柜管理模块)
8. [库存管理模块](#8-库存管理模块)
9. [财务结算模块](#9-财务结算模块)
10. [托盘管理模块](#10-托盘管理模块)
11. [通用配置模块](#11-通用配置模块)
12. [常见案例模块](#12-常见案例模块)
13. [在线开发模块](#13-在线开发模块)
14. [报表设计模块](#14-报表设计模块)
15. [消息中心模块](#15-消息中心模块)
16. [系统管理模块](#16-系统管理模块)
17. [系统监控模块](#17-系统监控模块)
18. [API接口清单](#18-api接口清单)
---
## 1. 系统概述
柏悦物流管理系统(Baiyue WMS)是一套面向跨境物流仓储行业的综合管理系统,覆盖从**客户管理 → 入库 → 出库 → 货柜管理 → 库存 → 托盘 → 财务结算**的全链路业务流程。
### 1.1 核心业务流程
```
客户下单 → 入库单 → 入库测量 → 测量审核 → 接收审核 → 库存管理
↓
出库单 → BOL/派送 → 托盘管理 → 货柜管理 → 财务结算
```
### 1.2 系统模块总览
| 序号 | 模块 | 子页面数 | 说明 |
|------|------|----------|------|
| 1 | 首页 | 1 | 数据看板/仪表盘 |
| 2 | 客户管理 | 4 | 客户公司、用户、阶梯费用、流水 |
| 3 | 入库 | 4 | 入库单、测量、审核、接收 |
| 4 | 出库 | 3 | 派送单、BOL、自提单 |
| 5 | 货柜管理 | 2 | 货柜列表、装柜清单 |
| 6 | 库存 | 3 | 海外仓库存查询、理货、报损 |
| 7 | 财务结算 | 5 | 应收应付、对账单、仓储成本 |
| 8 | 托盘管理 | 2 | 托盘列表、生成托盘 |
| 9 | 通用 | 8 | 产品、费用、FBA地址、库位等 |
| 10 | 常见案例 | 2 | 自定义组件、对象存储 |
| 11 | 在线开发 | 1 | Online表单开发 |
| 12 | 报表设计 | 3 | 积木报表、生产监控、物流监控 |
| 13 | 消息中心 | 3 | 模板、消息管理、我的消息 |
| 14 | 系统管理 | 12 | 用户、角色、菜单、租户等 |
| 15 | 系统监控 | 7 | 在线用户、日志、定时任务等 |
| 16 | 统计报表 | - | 报表设计入口 |
---
## 2. 技术架构
### 2.1 前端技术
- **框架**: Vue.js(SPA 单页应用)
- **UI 组件库**: Ant Design Vue
- **路由**: Vue Router(Hash 模式)
- **状态管理**: Vuex
- **图表**: 集成数据可视化组件
### 2.2 后端技术
- **框架**: Spring Boot(推测,基于 Spring 生态)
- **数据库**: MySQL
- **缓存**: Redis
- **认证**: Token-based(JWT)
- **API 风格**: RESTful
### 2.3 认证流程
```
用户登录 → POST /sys/login → 返回 Token
后续请求 → Header: Authorization: Bearer {token}
权限验证 → GET /sys/permission/getUserPermissionByToken
```
---
## 3. 首页仪表盘
- **URL**: `/wms/Dashboard`
- **页面标题**: 柏悦物流管理系统
首页提供系统总览数据看板,展示关键业务指标(订单数、库存状态、财务概况等)。
---
## 4. 客户管理模块
### 4.1 客户公司管理
管理客户公司的基本信息,包括公司名称、联系方式、合作状态等。
### 4.2 用户资料
管理客户下的用户账号资料,包括登录账号、角色权限等。
### 4.3 海外仓阶梯费用
配置客户在海外仓的阶梯式费用规则,按不同量级设置不同单价。
### 4.4 客户账户流水
查看客户账户的资金流水明细,包括充值、消费、退款等记录。
---
## 5. 入库管理模块
入库是系统的核心业务流程模块,包含四个阶段:入库单创建 → 测量 → 测量审核 → 接收审核。
### 5.1 入库单列表
- **URL**: `/wms/ByInstoreList`
- **页面标题**: 入库单列表 · 柏悦物流管理系统
#### 搜索条件
| 字段 | 说明 |
|------|------|
| 定位仓库 | 筛选指定仓库的入库单 |
| 交货仓 | 筛选指定交货仓的入库单 |
| 入仓码 | 按入仓码精确查询 |
| 订单状态 | 按订单状态筛选(如:待处理、处理中、已完成) |
| 费用审核状态 | 按费用审核状态筛选 |
#### 表格列头(23列)
| 列名 | 说明 |
|------|------|
| 仓库 | 入库目标仓库 |
| 交货仓 | 货物来源交货仓 |
| 配载状态 | 货物配载状态 |
| 是否扣货 | 是否被扣货标记 |
| 扣货理由 | 扣货原因说明 |
| 集装箱号 | 集装箱编号 |
| 柜号 | 货柜编号 |
| 公司 | 客户公司名称 |
| 业务类型 | 业务分类 |
| 提货方式 | 提货方式(自提/送货等) |
| 提货时间 | 预约提货时间 |
| 提货联系人电话 | 提货联系人电话 |
| 提货地址 | 提货地址详情 |
| 提货邮编 | 提货地邮编 |
| 提货联系人邮箱 | 提货联系人邮箱 |
| 渠道 | 业务渠道来源 |
| 买单/退税 | 贸易方式(买单/退税) |
| 是否整柜 | 整柜/拼柜标记 |
| 商检验 | 商检检验标记 |
| 开船时间 | 预计开船时间 |
| 到港时间 | 预计到港时间 |
| 总箱数 | 货物总箱数 |
| 总毛重(T.K.GW) | 总毛重(吨/公斤) |
#### 操作按钮
| 按钮 | 功能 |
|------|------|
| 查询 | 根据搜索条件查询数据 |
| 重置 | 清空搜索条件 |
| 国内仓下单 | 创建国内仓入库单 |
| 海外分仓下单 | 创建海外分仓入库单 |
| 整托下单 | 以整托方式创建入库单 |
| 高级查询 | 打开高级筛选面板 |
| 导出报表 | 导出当前查询结果为Excel |
| 导出入库清单总表 | 导出完整的入库清单 |
| 导出唛头 | 导出唛头标签文件 |
| 导出FBA流转单ZIP | 导出FBA流转单压缩包 |
| 下载 | 下载选中记录的相关文件 |
### 5.2 入库单测量
- **URL**: `/wms/ByInstoreList4Measured`
- **页面标题**: 入库单测量 · 柏悦物流管理系统
对入库货物进行实际测量,记录测量数据。
#### 搜索条件
| 字段 | 说明 |
|------|------|
| 仓库 | 筛选仓库 |
| 入仓码 | 按入仓码查询 |
| 公司 | 按客户公司筛选 |
| FBA货件编号 | 按FBA货件编号查询 |
#### 表格列头(24列)
| 列名 | 说明 |
|------|------|
| 入仓码 | 入仓编码 |
| 仓库 | 目标仓库 |
| 订单状态 | 订单处理状态 |
| 配载状态 | 配载情况 |
| 测量数据审核状态 | 测量审核进度 |
| 唛头文件 | 唛头标签文件 |
| 是否扣货 | 扣货标记 |
| 扣货理由 | 扣货原因 |
| 公司 | 客户公司 |
| 业务类型 | 业务分类 |
| 渠道 | 业务渠道 |
| 买单/退税 | 贸易方式 |
| 是否整柜 | 整柜/拼柜 |
| 商检验 | 商检状态 |
| 总箱数 | 货物总箱数 |
| 总毛重(T.K.GW) | 总毛重 |
| 总体积(m³) | 货物总体积 |
| 总材积重 | 材积重量 |
| 总净重(T.K.NW) | 总净重 |
| 目的仓类型 | 目的仓库类型 |
| 目的仓 | 目的仓库 |
| 货件编号 | 货件编号 |
| 测量完成时间 | 测量完成时间 |
| 操作 | 操作按钮列 |
#### 操作按钮
| 按钮 | 功能 |
|------|------|
| 查询 | 根据条件查询 |
| 重置 | 清空条件 |
| 下载 | 下载数据 |
### 5.3 测量数据审核
- **URL**: `/wms/ByInstoreList4MeasuredAudit`
- **页面标题**: 测量数据审核 · 柏悦物流管理系统
对测量数据进行审核确认。表头和搜索条件与入库单测量一致。
### 5.4 接收审核
- **URL**: `/wms/ByInstoreList4ReceiveAudit`
- **页面标题**: 接收审核 · 柏悦物流管理系统
最终接收审核环节,确认货物入库完成。表头和搜索条件与入库单测量一致。
---
## 6. 出库管理模块
### 6.1 派送单列表
- **URL**: `/wms/ByDispatchList`
管理出库派送订单,包括派送地址、物流方式、派送状态等。
### 6.2 BOL(提单管理)
- **URL**: `/wms/ByBolList`
管理 Bill of Lading(提单),是国际物流中的重要单证。包括提单号、船名航次、发货人、收货人等。
### 6.3 自提单
管理客户自提订单,记录自提预约、提货人信息等。
---
## 7. 货柜管理模块
### 7.1 货柜列表
- **URL**: `/wms/ByContainerList`
管理所有货柜信息,包括柜号、类型、尺寸、状态、当前位置等。
### 7.2 装柜清单
记录每个货柜的装柜明细,展示柜内货物列表和装载方案。
---
## 8. 库存管理模块
### 8.1 海外仓库存查询
- **URL**: `/wms/ByInventoryList`
查询海外仓当前库存情况,支持按仓库、产品、SKU等维度查询。
### 8.2 理货管理
对库存货物进行盘点、整理、移位等操作。
### 8.3 报损单
管理货物损耗/损坏的报损申请和审批流程。
---
## 9. 财务结算模块
### 9.1 客户应收应付
- **URL**: `/wms/ByFinanceList`
管理每个客户的应收账款和应付账款明细。
### 9.2 客户对账单
生成和查看客户对账单,用于与客户进行账务核对。
### 9.3 结算单位应付
按结算单位(如承运商、仓库方)管理应付账款。
### 9.4 结算单位对账单
生成结算单位的对账单。
### 9.5 洛杉矶仓储成本
专门针对洛杉矶仓库的仓储成本核算和分析。
---
## 10. 托盘管理模块
### 10.1 托盘列表
- **URL**: `/wms/ByTrayList`
管理所有托盘信息,包括托盘编号、规格、状态、关联货物等。
### 10.2 生成托盘
根据入库货物自动/手动生成托盘,分配货物到托盘。
---
## 11. 通用配置模块
系统基础数据和配置管理。
| 子模块 | 说明 |
|--------|------|
| 产品信息 | 管理产品/货品基础信息(名称、规格、HS编码等) |
| 费用项目 | 定义各类费用项目(仓储费、操作费、运输费等) |
| FBA编码地址 | 管理Amazon FBA仓库编码和地址信息 |
| 托盘管理 | 托盘基础配置 |
| 库位/货架管理 | 仓库库位和货架的编号和配置 |
| 订单流转路径 | 定义订单在不同节点之间的流转规则 |
| 快递公司 | 合作的快递/物流公司信息 |
| 往来单位 | 管理供应商、承运商等往来单位信息 |
---
## 12. 常见案例模块
### 12.1 自定义组件
展示系统自定义组件的使用示例和开发指南。
### 12.2 对象存储
文件/对象存储功能的配置和使用说明。
---
## 13. 在线开发模块
### 13.1 Online表单开发
低代码表单开发工具,允许管理员在线设计和配置业务表单,无需编写代码。
---
## 14. 报表设计模块
### 14.1 积木报表设计
可视化报表设计器,支持拖拽式设计各类业务报表。
### 14.2 生产销售监控
展示生产和销售的监控数据看板。
### 14.3 智慧物流监控
物流全链路监控大屏,展示实时物流数据。
---
## 15. 消息中心模块
### 15.1 模板管理
管理系统通知的消息模板,支持短信、邮件、站内信等类型。
### 15.2 消息管理
查看和管理所有已发送的系统消息。
### 15.3 我的消息
当前登录用户收到的个人消息列表。
---
## 16. 系统管理模块
系统后台管理功能。
| 子模块 | 说明 |
|--------|------|
| 用户管理 | 管理系统用户账号(创建、编辑、禁用、重置密码) |
| 角色管理 | 定义角色并分配权限 |
| 菜单管理 | 配置系统导航菜单结构 |
| 租户管理 | 多租户隔离管理(适用于SaaS模式) |
| 部门管理 | 组织架构部门管理 |
| 我的部门 | 当前用户所属部门信息 |
| 职务管理 | 定义系统内职务 |
| 通讯录 | 系统用户通讯录 |
| 数据字典 | 系统枚举值和字典数据维护 |
| 分类字典 | 分类树形字典维护 |
| 系统通告 | 发布系统级公告通知 |
| 菜单管理2 | 菜单管理的增强版/备用入口 |
---
## 17. 系统监控模块
系统运行状态监控和运维管理。
| 子模块 | 说明 |
|--------|------|
| 在线用户 | 查看当前在线用户列表,支持强制下线 |
| 路由网关 | 查看系统路由配置和网关状态 |
| 定时任务 | 管理系统的定时任务(Cron Job) |
| 日志管理 | 查看系统操作日志 |
| 数据日志 | 数据变更日志记录 |
| SQL监控 | SQL执行监控(基于Druid连接池) |
| 在线文档 | 系统API文档(Swagger/Knife4j) |
---
## 18. API接口清单
系统后端API部署在 `https://adtest.bylogistic.cn`,以下为已发现的API端点:
### 认证相关
| 接口 | 方法 | 说明 |
|------|------|------|
| `/sys/login` | POST | 用户登录 |
| `/sys/randomImage/{timestamp}` | GET | 获取验证码图片 |
| `/sys/permission/getUserPermissionByToken` | GET | 获取用户权限 |
### 数据字典
| 接口 | 方法 | 说明 |
|------|------|------|
| `/sys/dict/getDictItems/by_company` | GET | 获取公司字典 |
| `/sys/dict/getDictItems/by_order_status` | GET | 获取订单状态字典 |
| `/sys/dict/getDictItems/by_customer` | GET | 获取客户字典 |
| `/sys/dict/getDictItems/by_store` | GET | 获取仓库字典 |
| `/sys/dict/getDictItems/by_order_path` | GET | 获取订单路径字典 |
| `/sys/dict/getDictItems/by_fba` | GET | 获取FBA编码字典 |
### 系统功能
| 接口 | 方法 | 说明 |
|------|------|------|
| `/sys/annountCement/listByUser` | GET | 获取用户公告列表 |
| `/sys/loginfo` | GET | 系统日志信息 |
| `/sys/visitInfo` | GET | 访问统计信息 |
---
## 附录:SKU明细区域列头
在入库单详情页面,SKU明细表格包含以下列:
| 列名 | 说明 |
|------|------|
| 操作 | 编辑/删除操作 |
| # | 序号 |
| 报关HS_CODE | 报关用HS海关编码 |
| 货品图片 | 货品图片预览 |
| 仓/储/微(快递)码/纱 | 仓储编码/微信码/快递码 |
| 中品名 | 中文品名 |
| 英品名 | 英文品名 |
| 清关HS_CODE | 清关用HS海关编码 |
| 箱规长(cm) | 外箱长度 |
| 箱规宽(cm) | 外箱宽度 |
---
> **文档生成时间**: 2026-06-01
> **数据来源**: 测试系统 http://39.101.149.173 实际登录抓取
> **说明**: 部分页面因SPA路由限制未能获取完整表头信息,已根据菜单结构推断标注。如需补充具体页面详情,请登录系统确认后更新。
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff