diff --git a/.axhub/make/client.json b/.axhub/make/client.json index ecaa91a..464df47 100644 --- a/.axhub/make/client.json +++ b/.axhub/make/client.json @@ -4,7 +4,6 @@ "repository": "https://github.com/lintendo/Axhub-Make/tree/main/client", "templateUrl": "https://github.com/lintendo/Axhub-Make/releases/download/make-client-template-v0.1.4/axhub-make-client-template.zip", "templateVersion": "0.1.4", - "makeVersion": "0.6.4", "project": { "id": "make-project-20260629", "name": "小羚羚「小程序」" diff --git a/.axhub/make/sidebar-tree.json b/.axhub/make/sidebar-tree.json index a8bba3c..86cfb69 100644 --- a/.axhub/make/sidebar-tree.json +++ b/.axhub/make/sidebar-tree.json @@ -3,10 +3,17 @@ "updatedAt": "2026-06-30T02:16:40.884Z", "prototypes": [ { - "id": "item:prototypes:xll-miniapp", - "kind": "item", - "title": "小程序", - "itemKey": "prototypes/xll-miniapp" + "id": "folder-prototypes-xll-miniapp", + "kind": "folder", + "title": "小羚羚小程序", + "children": [ + { + "id": "item:prototypes:xll-miniapp", + "kind": "item", + "title": "小程序", + "itemKey": "prototypes/xll-miniapp" + } + ] } ], "docs": [], diff --git a/src/prototypes/xll-miniapp/.spec/authorized-person.md b/src/prototypes/xll-miniapp/.spec/authorized-person.md new file mode 100644 index 0000000..c61e998 --- /dev/null +++ b/src/prototypes/xll-miniapp/.spec/authorized-person.md @@ -0,0 +1,56 @@ +# 交车 · 被授权人空态 + +**模块**:小羚羚小程序 / 交车(`#page=delivery`) +**代码路径**:`src/prototypes/xll-miniapp/index.tsx`(`dvGetAuthorizedPersons` / `dvFormatEmptyAuthorizedHint` / `renderAuthorizedPersonBlock` / `validateDeliveryBeforeSign`) +**数据说明**:原型本地种子;正式环境被授权人列表与业管姓名来自关联租赁合同,未接真实 API。 + +--- + +## 1. 判定优先级 + +| 顺序 | 条件 | 用户可见结果 | 是否可发送签章 | +| --- | --- | --- | --- | +| 1 | 关联合同被授权人列表长度为 0 | 选择区展示:`无授权人,请联系{业管姓名}补充授权人信息`;点「发送签章文件 / 重新发起签章」时 Toast 同文案 | 否 | +| 2 | 列表非空,但未选中任何人 | 展示候选人卡片;Toast「请选择被授权人」 | 否 | +| 3 | 列表非空且已选中 | 选中卡片高亮,可继续签章流程 | 是 | + +--- + +## 2. 前置条件 + +- 仅在交车办理页「拍摄照片」步骤的「选择被授权人」区块生效。 +- 只读查看态(待客户签章 / 客户已签章)若已落库被授权人姓名,仍展示只读卡片;若无姓名且列表为空,同样展示空态提示。 +- 待重新签章:列表非空时可重选;列表为空时展示空态并阻断重新发起。 + +--- + +## 3. 数据源 + +| 字段 | 来源(原型) | 说明 | +| --- | --- | --- | +| 被授权人列表 | 交车订单 `authorizedPersons` | 按订单挂载;`[]` 表示合同无被授权人 | +| 业管姓名 | 交车订单 `businessOwner` | 合同业务负责人;为空时文案回退为「业管」 | +| 已选被授权人 | 单车草稿 `authorizedPersonId/Name/Phone` | 发送签章前必填(列表非空时) | + +演示种子: + +- `o1`(桐乡韵达):`authorizedPersons: []`,业管「刘念念」——用于验收空态。 +- `o4` / `o5`:有候选人列表——用于验收正常选择。 + +--- + +## 4. 文案规则 + +```text +无授权人,请联系{businessOwner}补充授权人信息 +``` + +示例:`无授权人,请联系刘念念补充授权人信息` + +--- + +## 5. 验收要点 + +- [ ] 打开桐乡韵达「未开始」交车单,到「选择被授权人」可见橙色空态提示,姓名为「刘念念」 +- [ ] 点「发送签章文件」弹出同文案 Toast,不进入 E签宝 +- [ ] 聚德订单(有授权人)仍可点选卡片并发送签章 diff --git a/src/prototypes/xll-miniapp/.spec/prototype-comments.json b/src/prototypes/xll-miniapp/.spec/prototype-comments.json new file mode 100644 index 0000000..c63bacb --- /dev/null +++ b/src/prototypes/xll-miniapp/.spec/prototype-comments.json @@ -0,0 +1,12 @@ +{ + "schemaVersion": 1, + "kind": "prototype-edit-comments", + "resource": { + "id": "xll-miniapp", + "targetPath": "prototypes/xll-miniapp", + "filePath": "src/prototypes/xll-miniapp/.spec/prototype-comments.json" + }, + "comments": [], + "tasks": {}, + "images": [] +} diff --git a/src/prototypes/xll-miniapp/annotation-source.json b/src/prototypes/xll-miniapp/annotation-source.json index 21f3145..44deb0c 100644 --- a/src/prototypes/xll-miniapp/annotation-source.json +++ b/src/prototypes/xll-miniapp/annotation-source.json @@ -211,6 +211,24 @@ "createdAt": 1779667200000, "updatedAt": 1779667200000 }, + { + "id": "page-vehicle-detail-location", + "index": 9.5, + "title": "车辆位置", + "pageId": "vehicle", + "locator": { + "selectors": [ + "[data-annotation-id=\"page-vehicle-detail-location\"]" + ] + }, + "aiPrompt": "车辆详情 Tab 内 GPS 地图位置展示。", + "annotationText": "", + "hasMarkdown": true, + "color": "#2563EB", + "images": [], + "createdAt": 1779667200000, + "updatedAt": 1779667200000 + }, { "id": "page-third-return", "index": 10, @@ -257,7 +275,7 @@ "[data-annotation-id=\"page-map\"]" ] }, - "aiPrompt": "说明氢能车与加氢站地图切换。", + "aiPrompt": "说明三类资源(氢能车/加氢站/维修站)、角色权限、地图/列表/统计三种模式及详情穿透。", "annotationText": "", "hasMarkdown": true, "color": "#0EA5E9", @@ -286,22 +304,23 @@ ] }, "markdownMap": { - "page-todo": "# 工作台 · 待办\n\n聚合交车、还车、年审、调拨、异动等待处理任务。\n\n- 卡片按任务类型色条区分\n- 「去处理」根据类型跳转对应业务页或本页任务办理\n- 原型入口默认打开本页,无需登录", - "main-tabbar": "# 底部主导航\n\n| Tab | 说明 |\n| --- | --- |\n| 工作台 | 待办任务列表 |\n| 业务 | 运维与审批入口 |\n| 地图 | 车辆 / 加氢站 |\n| 我的 | 账号信息 |", + "page-todo": "# 工作台 · 待办\n\n从「流程待办」升级为「行动待办」:每条任务自带行动类型、风险等级与责任链,帮助一线人员 3 秒内判断要不要处理。\n\n## 分区规则\n\n| 分区 | 含义 |\n| --- | --- |\n| 今日必办 | 高风险或 SLA 临近,建议优先处理 |\n| 待我处理 | 需审批 / 需填报 / 需发起 / 需执行 |\n| 待我跟进 | 流程未到我节点,但我有业务责任 |\n| 仅知会 | 抄送类任务,不计入角标 |\n\n## 卡片字段\n\n- **行动类型**:需审批、需填报、需发起、需执行、需跟进、仅知会\n- **风险等级**:高 / 中 / 低 / 无 + 风险摘要\n- **责任链**:展示通过后下一责任人与动作\n- **SLA**:剩余时间或超期提示\n\n## 跳转\n\n- 审批类 → 审批中心\n- 交车 / 年审 → 对应业务模块\n- 其余 → 本页任务办理子页\n\n角标仅统计「需处理」任务,不含仅知会。", + "main-tabbar": "# 底部主导航\n\n| Tab | 说明 |\n| --- | --- |\n| 工作台 | 待办任务列表 |\n| 业务 | 运维与审批入口 |\n| 地图 | 氢能车 / 加氢站 / 维修站 |\n| 我的 | 账号信息 |", "page-business": "# 业务页\n\n分三大区:运维管理、审批管理、数据可视化。\n\n点击模块图标跳转到对应独立原型页(hash 路由),各页内列表与表单可直接跳转。", "page-audit": "# 审批中心\n\n四类列表:我发起的、我的待办、我的已办、抄送我的。\n\n### 筛选与搜索\n\n- **流程类型 Chip**:全部、合同审批、提车应收款、租赁账单、还车应结款(含四部门子类型)、车辆调拨;「更多类型」抽屉展示全部流程类型。\n- **更多筛选**(搜索框右侧筛选按钮):发起人、发起时间(开始至结束)。\n- **搜索**:按卡片加粗标题(工作流标题)模糊匹配,如提车应收款为**客户全称**、还车应结款为**车牌号**。\n\n### 更多筛选交互(小程序式)\n\n| 能力 | 说明 |\n| --- | --- |\n| 底部抽屉 | 「更多筛选」限定在**手机预览框内**弹出,不铺满浏览器(`.xll-mod-sheet-*`) |\n| 发起人 | 点击「请选择 ›」→ 底部滚轮;首项「不限」 |\n| 发起时间 | 「开始日期 至 结束日期」→ 年/月/日三列滚轮,取消/确定 |\n| 嵌套选择器 | 滚轮叠在筛选抽屉之上,高亮条在文字下层 |\n\n### 列表卡片(以提车应收款为例)\n\n| 区域 | 说明 |\n| --- | --- |\n| 左上角标签 | 流程类型,如「提车应收款」 |\n| 右上角状态 | 审批中 + 当前节点审批人(最多 2 人,超出显示 +N) |\n| 加粗标题 | 客户名称全称 |\n| 副标题 | 合同项目名称 |\n| 元信息 | 发起人、实收金额(选中车辆租金+服务费+保证金合计) |\n| 底部 | 发起时间精确至分钟;待办卡片可点「去审批」或整卡进入办理页 |\n\n**还车应结款**:主流程以车牌为主标题;子流程主标题为「还车应结款-部门名」。四部门子流程全部通过后,须由**业务管理组主动发起**主流程审批,任务才会进入审批列表。\n\n### 需求文档入口\n\n导航栏**不设**「需求说明」按钮;PRD 见标注目录「PRD 01|审批中心改版说明」。\n\n### 审批操作弹窗\n\n待办详情底部「通过」「驳回」「终止」「评论」均从手机屏幕底部滑出半屏表单(限定在手机预览框内,非全浏览器宽);通过/驳回/终止除标题外字段一致(通知方式、附件上传、抄送人、审批意见)。「通过」标题为「审批通过」。", "page-audit-return": "# 还车应结款\n\n**主工作流**「还车应结款」汇总四部门费用与应退/应补总额。\n\n**四个子流程**(各部门独立提交):还车应结款-业务管理组、还车应结款-能源服务组、还车应结款-运维组、还车应结款-安全部。\n\n**主流程发起规则**:四部门子流程全部审批通过后,**不会自动生成**主流程任务;须由**业务管理组主动发起**主流程审批,发起后任务才会出现在审批列表。\n\n子流程 Hero 统一为「还车应结款-部门名」+ 车牌重点样式,仅展示本部门费用。\n\n### 原型 mock 场景\n\n| 场景 | 车牌 | 说明 |\n| --- | --- | --- |\n| A | 粤B58888F | 四部门已通过 → 业务管理组已发起 → 主流程在待办 |\n| B | 浙F06901F | 四部门子流程均在待办 |\n| C | 苏E12345F | 四部门已通过 → 业务管理组尚未发起 → 主流程不在列表 |\n\n### 部门结算卡片状态标签\n\n各分组费用卡片右上角展示本部门子流程提交状态:\n\n| 状态 | 展示文案 | 触发条件 |\n| --- | --- | --- |\n| 待提交 | 待提交 | 表单未提交、子流程未发起 |\n| 审批中 | 审批中 | 用户已提交子流程审批 |\n| 已提交 | 已提交 | 该部门子流程全部审批完成 |\n| 已驳回 | 已驳回 | 子流程审批被驳回 |\n\n**主流程详情**:四张分组卡片右上角均展示「已提交」(四部门已全部完成子流程审批,且业务管理组已发起主流程后才可进入)。\n\n| 部门 | 费用要点 |\n| --- | --- |\n| 业务管理组 | ETC、停车费等 |\n| 能源服务组 | 氢量差补缴、预付款退费等 |\n| 运维组 | 含「无忧包减免」列,应结算 = 金额 − 减免 |\n| 安全部 | 违章违约金、保险上浮、其他违规费用 |\n\n详见「PRD 01|审批中心改版说明」。", "page-delivery": "# 交车\n\n列表与多步骤交车办理在同一原型页内切换。\n\n完整产品需求见标注目录 **PRD 02|交车说明**(与 `page-delivery` 标注节点对应)。", "page-inspection": "# 年审\n\n待办列表与检测表单在同一原型页内切换。", - "page-vehicle": "# 车辆管理\n\n车辆列表与详情在同一原型页内切换。", + "page-vehicle": "# 车辆管理\n\n车辆列表与详情在同一原型页内切换。\n\n## 列表\n- 顶部 KPI:全部 / GPS 在线 / GPS 离线,可点击筛选\n- 卡片展示车牌、运营状态、GPS 状态、当前位置摘要\n- 支持搜索、运营状态与车辆状态筛选\n\n## 详情 · 车辆详情 Tab\n- **车辆位置**:参照交车表单,腾讯地图风格底图 + 定位标记 + 地址与坐标 + GPS 上传时间\n- 运营信息、车辆档案、权属与停放、交还车记录分卡片展示", + "page-vehicle-detail-location": "# 车辆位置\n\n展示方式与交车表单「交车位置」一致:\n\n- 腾讯地图风格底图\n- 在线车辆显示定位标记与经纬度\n- 底部展示详细地址\n- 离线车辆地图置灰,无标记\n- 下方展示 GPS 上传时间", "page-third-return": "# 三方退车\n\n退车任务列表与办理表单在同一原型页内切换。", "page-replace": "# 替换车\n\n申请列表与详情/办理在同一原型页内切换。", - "page-map": "# 地图\n\n氢能车 Tab 展示车辆实时位置;加氢站 Tab 展示站点 POI。\n\n支持车牌搜索与全图视野(原型提示)。", + "page-map": "# 地图模块\n\n## 核心资源\n氢能车 · 加氢站 · 维修站\n\n## 角色权限\n内部(管理/运维/授权)可见全量;外部(客户/司机/车队)按授权过滤。\n\n## 浏览模式\n1. **地图**:聚合点 → 展开标记 → 详情(多层信息 Tab)\n2. **列表**:搜索筛选 + 卡片穿透详情\n3. **统计**:KPI 卡片下钻列表明细", "page-mine": "# 我的\n\n展示当前用户头像、姓名、岗位与联系方式。", "prd-overview": "# 小羚羚小程序 · 总览\n\n氢能车辆运营移动端原型,面向一线运维与管理人员。**原型无需登录**,默认进入工作台。\n\n## 核心路径\n\n1. 工作台处理待办\n2. 业务 → 进入各运维 / 审批模块(独立原型页)\n3. 审批中心 → 本页内跳转**本期已接入**的审批工作流(提车应收款、还车应结款及子类型、租赁账单、车辆调拨、替换车申请等)\n\n## 页面导航\n\n左侧预览列表可切换 10 个功能页;各业务模块内列表与表单在同一页内跳转。\n\n## 最新改版\n\n审批中心与还车应结款多部门子流程见 **PRD 01|审批中心改版说明**(V1.4)。\n\n### 审批中心要点(与标注 `page-audit` 一致)\n\n- 搜索:仅按卡片加粗标题(工作流标题)模糊匹配\n- 更多筛选:发起人(滚轮选择)、发起时间(年/月/日滚轮,起止日期)\n- 底部抽屉与选择器:限定在手机预览框内,模拟小程序交互\n- 还车应结款:主流程以车牌为主标题;四部门子流程以「还车应结款-部门名」为主标题;四部门全过后须业务管理组发起主流程\n\n## 需求文档与标注\n\n- **标注目录**:PRD 00 / PRD 01 / PRD 02 及页面说明(`page-audit`、`page-delivery` 等)\n- **PRD 02|交车说明**:交车列表、分步办理与签章流程\n- **导航栏**:不设「需求说明」入口;以标注工具与目录为主\n- **业务评审**:`.spec/prototype-review.md`\n", "prd-approval-v11": "# 小羚羚小程序 · 审批中心改版说明\n\n**文档版本**:V1.4 \n**更新日期**:2026-06-29 \n**适用范围**:审批中心、还车应结款、提车应收款及相关导航结构\n\n---\n\n## 一、改版背景\n\n本次迭代聚焦审批体验与还车应结款多部门子流程,并调整原型信息架构:去除登录门槛、按功能拆分为独立预览页,便于评审与交付。\n\n---\n\n## 二、信息架构与导航\n\n### 2.1 去除登录\n\n- 原型默认进入「工作台 · 待办」,无需登录页。\n- 左侧预览列表可切换 **10 个功能页**(hash 路由)。\n- 源码中仍保留登录页组件与样式,**不在默认导航与预览目录中暴露**;评审以「免登录直达工作台」为准。\n\n### 2.2 页面清单\n\n| 路由 | 页面 | 说明 |\n| --- | --- | --- |\n| `todo` | 工作台 | 待办聚合,默认入口 |\n| `business` | 业务 | 运维 / 审批 / 数据可视化入口 |\n| `map` | 地图 | 氢能车与加氢站 |\n| `mine` | 我的 | 个人信息 |\n| `audit` | 审批中心 | 列表 + 本页内跳转各审批详情 |\n| `audit-return` | 还车应结款(说明) | 还车应结款专项说明页 |\n| `delivery` | 交车 | 列表与办理表单同页切换 |\n| `inspection` | 年审 | 列表与办理表单同页切换 |\n| `vehicle` | 车辆管理 | 列表与详情同页切换 |\n| `third-return` | 三方退车 | 列表与办理同页切换 |\n| `replace` | 替换车 | 列表与详情同页切换 |\n\n### 2.3 核心路径\n\n1. 工作台处理待办 → 「去处理」跳转对应业务页 \n2. 业务 → 进入各运维 / 审批模块(独立原型页) \n3. 审批中心 → 本页内打开**本期已接入**的审批工作流详情(见 §3.5)\n\n### 2.4 需求文档入口\n\n- 导航栏**不设**「需求说明」按钮(按批注已移除该入口)。\n- 需求与 PRD 通过**左侧标注目录**(「PRD 00|小程序总览」「PRD 01|审批中心改版说明」)及页面标注节点(如 `page-audit`)查看。\n\n---\n\n## 三、审批中心列表\n\n### 3.1 Tab\n\n四类:**我发起的**、**我的待办**、**我的已办**、**抄送我的**。\n\n### 3.2 筛选与搜索\n\n- **流程类型 Chip**:全部、合同审批、提车应收款、租赁账单、**还车应结款**(含四部门子类型)、车辆调拨等;「更多类型」抽屉展示全部流程类型。\n- 「还车应结款」Chip 同时匹配主流程与四个部门子流程。\n- **更多筛选**(搜索框右侧筛选按钮):**发起人**、**发起时间**(开始至结束,按日期区间过滤)。\n- **搜索**:按**卡片加粗标题(工作流标题)**模糊匹配;不按单据号、摘要等多字段联合检索。示例:提车应收款搜**客户全称**;还车应结款主流程搜**车牌号**;还车四部门子流程搜**「还车应结款-部门名」**。\n\n> **决策说明**(与 `page-audit` 标注一致):发起人、发起时间从搜索中拆出,归入「更多筛选」,搜索仅面向列表主识别字段。\n\n### 3.3 列表卡片字段\n\n以提车应收款为例:\n\n| 区域 | 说明 |\n| --- | --- |\n| 左上角标签 | 流程类型,如「提车应收款」 |\n| 右上角状态 | 审批中 + 当前节点审批人(最多 2 人,超出显示 +N) |\n| 加粗标题 | 工作流标题(见 §3.4 标题规则) |\n| 副标题 | 合同项目名称等 |\n| 元信息 | 发起人、实收金额(提车:选中车辆租金 + 服务费 + 保证金合计,**不含氢费预付款**) |\n| 底部 | 发起时间精确至分钟;待办卡片可点「去审批」或整卡进入办理页 |\n\n**还车应结款**:主流程以车牌为主标题并展示应退/应补;四部门子流程主标题为「还车应结款-部门名」,不展示汇总金额。四部门子流程全部通过后,须由**业务管理组主动发起**主流程审批,任务才会进入审批列表。\n\n### 3.4 列表卡片标题规则\n\n| 类型 | 主标题(加粗) | 辅信息 | 金额 |\n| --- | --- | --- | --- |\n| 主流程「还车应结款」 | **车牌号** | 客户、项目等 | 展示应退 / 应补 |\n| 四部门子流程 | **还车应结款-部门名** | 车牌、客户等 | 不展示汇总金额 |\n| 提车应收款 | **客户全称** | 合同项目 | 实收金额(见 §3.3) |\n| 租赁账单 / 替换车申请 | **客户全称** | — | 按业务 |\n| 车辆调拨 | **调拨路线摘要** | — | 按业务 |\n| 其他流程 | 摘要或单据号 | — | 按业务 |\n\n部门子流程包括:**还车应结款-业务管理组**、**还车应结款-能源服务组**、**还车应结款-运维组**、**还车应结款-安全部**。\n\n### 3.5 跳转与本期原型范围\n\n**已接入详情页(页内跳转)**:提车应收款、还车应结款(含四部门子类型)、租赁账单、车辆调拨、替换车申请。待办 Tab 展示底部操作栏;其余 Tab 为只读查看态。\n\n**列表占位(点击 Toast 提示,暂无办理页)**:合同审批、氢费对账单(对站/对客)、车辆异动等。演示时可在列表中看到待办,但无法进入完整办理流程。\n\n**审批操作弹窗**:待办详情底部「通过」「驳回」「终止」「评论」均从手机屏幕底部滑出半屏表单(限定在手机预览框内);通过/驳回/终止除标题外字段一致(通知方式、附件上传、抄送人、审批意见)。「通过」标题为「审批通过」。\n\n### 3.6 更多筛选交互(小程序式)\n\n| 能力 | 规则 |\n| --- | --- |\n| 底部抽屉 | 「更多筛选」及同类 `.xll-mod-sheet-*` 抽屉从**手机预览框底部**滑出,遮罩与面板相对当前页面模块定位,**不铺满浏览器全屏** |\n| 发起人 | 表单行展示「请选择 ›」;点击后弹出底部**滚轮选择器**,选项来自 mock 发起人列表,首项「不限」 |\n| 发起时间 | 「开始日期 至 结束日期」两行可点;分别弹出**年 / 月 / 日**三列滚轮,工具栏「取消 / 确定」 |\n| 嵌套层级 | 滚轮选择器叠在筛选抽屉之上(`z-index` 更高),同样限定在手机预览框内;选中高亮条在文字下层,不遮挡选项文字 |\n\n---\n\n## 四、还车应结款审批\n\n### 4.1 流程结构\n\n**主工作流**:`还车应结款` — 汇总四部门费用,判定保证金抵扣后的应退/应补。\n\n**四个子流程**(各部门独立提交审批):\n\n| 子流程 | 提交部门 |\n| --- | --- |\n| 还车应结款-业务管理组 | 业务管理组 |\n| 还车应结款-能源服务组 | 能源服务组 |\n| 还车应结款-运维组 | 运维组 |\n| 还车应结款-安全部 | 安全部 |\n\n**提交规则**:\n\n1. 四个子流程分别由对应部门填报费用并**独立提交审批**;\n2. 四个子流程**全部审批通过**后,**不会自动生成**主流程「还车应结款」审批任务;\n3. 须由**业务管理组**在还车结算侧**主动发起**主流程审批,发起后任务进入审批列表;\n4. 主流程进入列表后,由财务等环节整体审批;详情页展示四部门子流程通过状态;\n5. 四部门未全部通过前,主流程不可发起;若已进入主流程详情,「通过」操作在四部门未全过时仍被拦截。\n\n#### 原型 mock 场景(开发 / 测试对照)\n\n| 场景 | 车牌 | 单据号 | 说明 |\n| --- | --- | --- | --- |\n| A | 粤B58888F | HC-2026-0418 | 四部门已通过 → 业务管理组已发起 → 主流程在「我的待办」(财务审核) |\n| B | 浙F06901F | HC-2026-0625 | 四部门子流程均在待办(审批中);主流程未发起 |\n| C | 苏E12345F | HC-2026-0610 | 四部门已通过 → 业务管理组**尚未发起** → 主流程不出现在列表 |\n\n> 主流程发起人 mock 为业务管理组人员(如陈高伟);发起时间晚于四部门子流程全部通过时间。\n\n### 4.2 Hero 区规则\n\n| 类型 | Hero 主信息 | 是否展示应退/应补 |\n| --- | --- | --- |\n| 主流程「还车应结款」 | 应退还总额 / 应补缴总额;保证金、待结算总额、车辆实际租金;「结算明细」抽屉;部门子流程进度 | **是** |\n| 业务管理组子流程 | 「还车应结款-业务管理组」+ **车牌重点样式** | 否 |\n| 能源服务组子流程 | 「还车应结款-能源服务组」+ 车牌重点样式 | 否 |\n| 运维组子流程 | 「还车应结款-运维组」+ 车牌重点样式 | 否 |\n| 安全部子流程 | 「还车应结款-安全部」+ 车牌重点样式 | 否 |\n\n**车牌重点样式**:Hero 内展示「车牌号」标签 + 加粗胶囊样式车牌,便于子流程快速识别车辆。\n\n子流程 Hero **不展示**保证金、待结算总额、应退/应补等汇总类金额。\n\n### 4.3 分组费用明细\n\n#### 业务管理组\n\n- 固定费项表格(如 ETC 费用、停车费等)。\n- **不含**违章处理违约金、保险上浮(归属安全部)。\n- 底部展示本部门应结算总额。\n\n#### 能源服务组\n\n- 氢量差补缴、交/还车氢量、退还单价、预付款退费等。\n\n#### 运维组\n\n- 费项:清洗、保养、维修、车损、工具/证件/广告丢失、送接车服务、轮胎磨损等。\n- 表格列:**序号 / 费用项 / 金额 / 无忧包减免**(横向滚动,减免为输入框样式只读展示)。\n- **应结算 = 金额 − 无忧包减免**(按行合计后汇总)。\n\n#### 安全部\n\n- **固定费项三行**(由安全部填报):\n 1. 违章处理违约金 \n 2. 保险上浮 \n 3. 其他违规费用 \n- 保留违章统计区:违章次数、已缴/未缴金额等。\n- 应结算总额为上述三行费用合计。\n\n#### 部门结算卡片状态标签\n\n各分组费用卡片右上角展示本部门子流程提交状态:\n\n| 状态 | 展示文案 | 触发条件 |\n| --- | --- | --- |\n| 待提交 | 待提交 | 表单未提交、子流程未发起 |\n| 审批中 | 审批中 | 用户已提交子流程审批 |\n| 已提交 | 已提交 | 该部门子流程全部审批完成 |\n| 已驳回 | 已驳回 | 子流程审批被驳回 |\n\n**主流程详情**:四张分组卡片右上角均展示「已提交」(四部门已全部完成子流程审批,且业务管理组已发起主流程后才可进入主流程详情)。\n\n### 4.4 审批操作\n\n- **审批情况**:纵向时间轴,节点展示审批人头像、状态 Tag、时间与评论气泡。\n- **待办态底部**:评论、终止、驳回、通过(规则同提车应收款)。\n- **查看态**:隐藏操作栏,仅浏览。\n\n---\n\n## 五、提车应收款审批\n\n### 5.1 页面目标\n\n审批人核对实收金额、车辆明细、氢费预付款(如有)及开票信息。\n\n### 5.2 布局细节\n\n- **车辆明细**:区块标题行(`.tc-section-head`)与首张车辆卡片之间保留 **12px** 上间距,避免标题分隔线与首卡紧贴。\n- **审批情况**:时间轴支持评论展示;待办态底部操作同还车应结款。\n\n### 5.3 实收金额口径\n\n| 位置 | 规则 |\n| --- | --- |\n| 列表卡片 | 选中车辆 **租金 + 服务费 + 保证金 − 减免** 合计(**不含氢费预付款**),由车辆明细动态汇总 |\n| 详情 Hero | 与列表卡片**同一口径**,展示实收款总额 |\n\n> **原型缺口**:当前列表已按 §3.3 动态计算,部分 mock 详情 Hero 仍使用静态 `actualAmount` 字段,评审时需对齐(见 `.spec/prototype-review.md`)。\n\n---\n\n## 六、验收要点\n\n请在预览中逐项确认(可提供截图或页面路径反馈):\n\n- [ ] 打开原型无需登录,默认进入工作台 \n- [ ] 左侧可切换 10 个功能页 \n- [ ] 导航栏无「需求说明」按钮;PRD 可从标注目录打开 \n- [ ] 审批中心「还车应结款」Chip 可筛出主流程 + 四部门子流程 \n- [ ] **更多筛选**抽屉在手机预览框内弹出(非全浏览器宽) \n- [ ] **更多筛选**:发起人、发起时间为滚轮选择器(非原生输入框) \n- [ ] **搜索**仅匹配卡片加粗标题(工作流标题) \n- [ ] 子流程列表卡片主标题为「还车应结款-部门名」;主流程卡片以车牌为主标题并展示应退/应补 \n- [ ] 四部门子流程详情 Hero 为部门标题 + 车牌重点样式,无汇总金额 \n- [ ] 主流程还车应结款 Hero 展示应退/应补、结算明细与四部门子流程进度 \n- [ ] 四部门子流程未全部通过时,主流程不可发起;四部门全过但业务管理组未发起时,主流程不出现在列表 \n- [ ] 业务管理组无违章违约金、保险上浮;安全部固定三行费项 \n- [ ] 运维组费用表含「无忧包减免」列,应结算 = 金额 − 减免 \n- [ ] 提车应收款车辆明细首卡与标题间距 12px \n- [ ] 提车列表实收金额与详情 Hero 口径一致(不含氢费预付款) \n- [ ] 审批时间轴展示头像、状态与评论;待办态可操作通过/驳回/终止/评论 \n\n**预览地址示例**:`http://localhost:51721/prototypes/xll-miniapp#page=audit`\n\n---\n\n## 七、关联文档\n\n| 文档 | 位置 |\n| --- | --- |\n| 小程序总览 | 标注目录「PRD 00|小程序总览」 |\n| 原型评审结论 | `src/prototypes/xll-miniapp/.spec/prototype-review.md` |\n| 页面标注 | `page-audit`、`page-audit-return`、`page-audit-pickup` 等 |\n", "page-audit-pickup": "# 提车应收款\n\nHero 展示实收款总额与客户/合同/项目信息。\n\n**车辆明细**:区块标题与首张车辆卡片之间保留 12px 上间距。\n\n审批时间轴支持评论;待办态底部评论/终止/驳回/通过。", - "prd-delivery": "# 小羚羚小程序 · 交车说明\n\n**文档版本**:V1.2 \n**更新日期**:2026-06-29 \n**适用范围**:运维管理 / 交车模块(`#page=delivery`) \n**参照**:web 交车管理 + Axhub「交车(完成)」原型 · 默认区域权限:浙江省-嘉兴市\n\n---\n\n## 一、列表页\n\n- **KPI**:全部 / 进行中 / 已完成,点击切换筛选。\n- **搜索**:车牌、项目、客户名称。\n- **卡片字段**:车牌(未选车显示「车辆待选」)、交车状态 Tag(未开始 / 已保存 / 待客户签章 / 待重新签章 / 客户已签章)、项目、客户、交车区域。\n- **进行中 Tab**:提供状态筛选 Chip(全部状态、未开始、已保存、待客户签章、待重新签章)。\n- **操作**:进行中任务展示「去办理 / 继续办理」按钮。\n\n---\n\n## 二、分步表单(顶部步骤条)\n\n顶部步骤条:**车辆情况** → **交车检查项** → **拍摄照片**,可点击切换。\n\n### 2.1 车辆情况\n\nHero 展示客户名称、项目名称与交车区域;含选择车辆、车辆配置、驾驶培训、交车数据、交车位置(独立卡片)。\n\n#### 识别车牌号\n\n拍照 OCR 识别车牌后,根据识别结果匹配车辆并校验交车资格,须同时满足:\n\n- **车辆状态 = 已备车**\n- **保险状态 = 正常**\n- **证照状态 = 正常**\n\n任一条件不满足则报错阻断,不可选车。报错提示文案与「选择车辆」页车辆卡片底部 readiness 提示条一致,例如:\n\n- 「交强险已到期无法交车」\n- 「商业险已到期无法交车」\n- 「交强险、商业险已到期无法交车」\n- 「行驶证已到期无法交车」\n- 未备车时「该车辆未备车,请先进行备车」\n\n支持**拍照识别**或**相册选取**车牌照片。\n\n#### 相册上传\n\n交车模块内**所有拍照环节**均支持从手机相册本地上传图片,包括:识别车牌号、车辆配置(车身广告/放大字)、驾驶培训手动记录证件照、交车连拍/补拍、瑕疵与其他照片添加、备胎拍摄等;相机页底部提供「拍照」「相册」操作。\n\n#### 车辆配置\n\n- 车身广告及放大字、尾板(必填,有则须上传照片)\n- 备胎(选填,仅选择有/无)\n\n#### 驾驶培训(必填)\n\n支持**电子培训** / **手动记录**两种模式。\n\n**电子培训**:展示培训二维码(微信扫码观看视频),完成后识别提车码。\n\n**手动记录(运维端)**:\n\n1. 录入司机手机号、姓名、身份证号\n2. 上传身份证正反面、驾驶证正反面、司机正面照片、从业资格证(重卡必填)\n3. 点击底部「生成培训码」\n4. 培训码与录入信息绑定,展示二维码供司机微信扫码\n5. 生成后交车页标记「待司机确认」,可「查看培训码」或「刷新培训状态」\n6. 司机签字完成并同步后标记「已完成驾驶培训」\n\n**手动记录 · 司机微信端**(仅需求说明,本期原型不实现):\n\n司机微信扫描二维码后,首先阅读并同意安全培训文件(倒计时 10 秒后可点确定);随后在司机微信浏览器展示运维登记的司机「姓名」「手机号」「身份证号」及身份证正反面、驾驶证正反面、司机正面照片、从业资格证照片;下方点击「确认签字」唤起签名板,完成签字后提示培训成功,相关信息同步至安全培训记录。\n\n#### 交车数据(必填)\n\n- 里程、氢量、电量均支持 2 位小数输入(无数字步进器)\n- 氢量单位按「型号参数」仪表盘单位随车型自动匹配(%/MPa,不可手动切换)\n- 送车服务费为选填,精确至 2 位小数(元)\n- 氢量下方备注为选填\n- **不含**交车时间字段\n\n#### 交车位置\n\n- 车辆 GPS 在线:交车位置取车辆 GPS 当前定位\n- 车辆 GPS 离线:交车位置取手机当前定位\n- 独立卡片展示地图、蓝点标记坐标;GPS 离线时支持手动「获取当前定位」\n\n### 2.2 交车检查项\n\n证件信息 → 工具信息 → 外观检查 → 轮胎检查。\n\n- 前 3 类:开关表示检查情况并可填备注\n- 轮胎检查项:填写胎纹深度 (mm)\n\n**实际交车时间**:运维提交 E签宝签章时系统自动记录;仅在交车列表「待客户签章」卡片展示。\n\n### 2.3 拍摄照片\n\n- 分车身 / 底盘 / 轮胎 / 瑕疵 / 其他五类\n- 连拍严格按顺序拍摄且不可跳过;中途退出后再次进入自动从下一项续拍\n- 模拟相机页支持**拍照或相册上传**,轮胎项下方录入胎纹;完成后可补拍瑕疵/其他\n- **所有拍照场景均支持调焦**:点击取景区域设定对焦点,右侧 +/- 调整 1.0×~3.0× 数码变焦(连拍、备胎、车身广告/放大字、手动记录证件照)\n\n**备胎照片**:从车辆情况步骤移至拍摄照片「轮胎情况」连拍末项(有备胎时必拍);车辆情况仅保留是否有备胎。\n\n**分步操作条**:车辆情况、交车检查项点击「下一步」时自动保存;拍摄照片完成后为「保存」「发送签章文件」。\n\n**照片上传**:连拍/单拍每次点击「完成」继续时,上一张自动上传并在服务端打水印(时间、地点)。\n\n**胎纹 OCR**:胎纹未反写成功时禁止继续拍摄下一张。\n\n### 2.4 签章流程\n\n**选择被授权人**:多卡片展示姓名与手机号,选中高亮;发送签章文件后跳转 E签宝 完成运维签字。\n\n| 状态 | 规则 |\n| --- | --- |\n| **待客户签章** | 只读查看各步骤,底部「返回」「清除签章」;确认后记录变为「待重新签章」 |\n| **待重新签章** | 交车信息全部锁定,仅可重选被授权人;底部「返回」「重新发起签章」,完成 E签宝 运维签字后恢复「待客户签章」 |\n| **客户已签章** | 被授权人短信完成签章后进入「已完成」列表 |\n\n---\n\n## 三、查看态\n\n- **客户已签章 / 待客户签章**:只读浏览各步骤,不可编辑。\n- **待重新签章**:各步骤只读,仅「选择被授权人」可改。\n- **已完成**:可下载 E签宝签章 PDF;展示是否归还。\n\n---\n\n## 四、导航\n\n- 表单内左上返回先退出办理页;再次返回退出交车模块。\n- 步骤条可点击跳转。\n\n---\n\n## 五、验收要点\n\n请在预览中逐项确认(可提供截图或页面路径反馈):\n\n- [ ] 业务页进入交车模块,列表 KPI / 搜索 / 状态筛选可用\n- [ ] 进行中任务可「去办理 / 继续办理」,列表与表单同页切换\n- [ ] OCR / 相册识别车牌,未备车或保险/证照异常时阻断选车\n- [ ] 车辆配置、驾驶培训、交车数据、交车位置各卡片可填写\n- [ ] 交车检查项四类可填,轮胎胎纹支持 mm 录入\n- [ ] 拍摄照片五类连拍、相册上传、调焦与备胎末项规则\n- [ ] 发送签章 → 待客户签章 → 清除签章 / 重新签章 / 客户已签章状态流转\n- [ ] 标注目录可打开本 PRD;页面标注 `page-delivery` 可定位交车模块\n\n**预览地址示例**:`http://localhost:51722/prototypes/xll-miniapp#page=delivery`\n\n---\n\n## 六、关联文档\n\n| 文档 | 位置 |\n| --- | --- |\n| 小程序总览 | 标注目录「PRD 00|小程序总览」 |\n| 审批中心改版 | 标注目录「PRD 01|审批中心改版说明」 |\n| 页面标注 | `page-delivery` |\n| 原型评审 | `src/prototypes/xll-miniapp/.spec/prototype-review.md` |\n" + "prd-delivery": "# 小羚羚小程序 · 交车说明\n\n**文档版本**:V1.2 \n**更新日期**:2026-06-29 \n**适用范围**:运维管理 / 交车模块(`#page=delivery`) \n**参照**:web 交车管理 + Axhub「交车(完成)」原型 · 默认区域权限:浙江省-嘉兴市\n\n---\n\n## 一、列表页\n\n- **KPI**:全部 / 进行中 / 已完成,点击切换筛选。\n- **搜索**:车牌、项目、客户名称。\n- **卡片字段**:车牌(未选车显示「车辆待选」)、交车状态 Tag(未开始 / 已保存 / 待客户签章 / 待重新签章 / 客户已签章)、项目、客户、交车区域。\n- **进行中 Tab**:提供状态筛选 Chip(全部状态、未开始、已保存、待客户签章、待重新签章)。\n- **操作**:进行中任务展示「去办理 / 继续办理」按钮。\n\n---\n\n## 二、分步表单(顶部步骤条)\n\n顶部步骤条:**车辆情况** → **交车检查项** → **拍摄照片**,可点击切换。\n\n### 2.1 车辆情况\n\nHero 展示客户名称、项目名称与交车区域;含选择车辆、车辆配置、驾驶培训、交车数据、交车位置(独立卡片)。\n\n#### 识别车牌号\n\n拍照 OCR 识别车牌后,根据识别结果匹配车辆并校验交车资格,须同时满足:\n\n- **车辆状态 = 已备车**\n- **保险状态 = 正常**\n- **证照状态 = 正常**\n\n任一条件不满足则报错阻断,不可选车。报错提示文案与「选择车辆」页车辆卡片底部 readiness 提示条一致,例如:\n\n- 「交强险已到期无法交车」\n- 「商业险已到期无法交车」\n- 「交强险、商业险已到期无法交车」\n- 「行驶证已到期无法交车」\n- 未备车时「该车辆未备车,请先进行备车」\n\n支持**拍照识别**或**相册选取**车牌照片。\n\n#### 相册上传\n\n交车模块内**所有拍照环节**均支持从手机相册本地上传图片,包括:识别车牌号、车辆配置(车身广告/放大字)、驾驶培训手动记录证件照、交车连拍/补拍、瑕疵与其他照片添加、备胎拍摄等;相机页底部提供「拍照」「相册」操作。\n\n#### 车辆配置\n\n- 车身广告及放大字、尾板(必填,有则须上传照片)\n- 备胎(选填,仅选择有/无)\n\n#### 驾驶培训(必填)\n\n支持**电子培训** / **手动记录**两种模式。\n\n**电子培训**:展示培训二维码(微信扫码观看视频),完成后识别提车码。\n\n**手动记录(运维端)**:\n\n1. 录入司机手机号、姓名、身份证号\n2. 上传身份证正反面、驾驶证正反面、司机正面照片、从业资格证(重卡必填)\n3. 点击底部「生成培训码」\n4. 培训码与录入信息绑定,展示二维码供司机微信扫码\n5. 生成后交车页标记「待司机确认」,可「查看培训码」或「刷新培训状态」\n6. 司机签字完成并同步后标记「已完成驾驶培训」\n\n**手动记录 · 司机微信端**(仅需求说明,本期原型不实现):\n\n司机微信扫描二维码后,首先阅读并同意安全培训文件(倒计时 10 秒后可点确定);随后在司机微信浏览器展示运维登记的司机「姓名」「手机号」「身份证号」及身份证正反面、驾驶证正反面、司机正面照片、从业资格证照片;下方点击「确认签字」唤起签名板,完成签字后提示培训成功,相关信息同步至安全培训记录。\n\n#### 交车数据(必填)\n\n- 里程、氢量、电量均支持 2 位小数输入(无数字步进器)\n- 氢量单位按「型号参数」仪表盘单位随车型自动匹配(%/MPa,不可手动切换)\n- 送车服务费为选填,精确至 2 位小数(元)\n- 氢量下方备注为选填\n- **不含**交车时间字段\n\n#### 交车位置\n\n- 车辆 GPS 在线:交车位置取车辆 GPS 当前定位\n- 车辆 GPS 离线:交车位置取手机当前定位\n- 独立卡片展示地图、蓝点标记坐标;GPS 离线时支持手动「获取当前定位」\n\n### 2.2 交车检查项\n\n证件信息 → 工具信息 → 外观检查 → 轮胎检查。\n\n- 前 3 类:开关表示检查情况并可填备注\n- 轮胎检查项:填写胎纹深度 (mm)\n\n**实际交车时间**:运维提交 E签宝签章时系统自动记录;仅在交车列表「待客户签章」卡片展示。\n\n### 2.3 拍摄照片\n\n- 分车身 / 底盘 / 轮胎 / 瑕疵 / 其他五类\n- 连拍严格按顺序拍摄且不可跳过;中途退出后再次进入自动从下一项续拍\n- 模拟相机页支持**拍照或相册上传**,轮胎项下方录入胎纹;完成后可补拍瑕疵/其他\n- **所有拍照场景均支持调焦**:点击取景区域设定对焦点,右侧 +/- 调整 1.0×~3.0× 数码变焦(连拍、备胎、车身广告/放大字、手动记录证件照)\n\n**备胎照片**:从车辆情况步骤移至拍摄照片「轮胎情况」连拍末项(有备胎时必拍);车辆情况仅保留是否有备胎。\n\n**分步操作条**:车辆情况、交车检查项点击「下一步」时自动保存;拍摄照片完成后为「保存」「发送签章文件」。\n\n**照片上传**:连拍/单拍每次点击「完成」继续时,上一张自动上传并在服务端打水印(时间、地点)。\n\n**胎纹 OCR**:胎纹未反写成功时禁止继续拍摄下一张。\n\n### 2.4 签章流程\n\n**选择被授权人**:多卡片展示姓名与手机号,选中高亮;发送签章文件后跳转 E签宝 完成运维签字。若关联合同无被授权人,页面展示「无授权人,请联系{业管姓名}补充授权人信息」(业管自动取合同业务负责人 `businessOwner`),并阻断发送签章。规则全文见 `.spec/authorized-person.md`。\n\n| 状态 | 规则 |\n| --- | --- |\n| **待客户签章** | 只读查看各步骤,底部「返回」「清除签章」;确认后记录变为「待重新签章」 |\n| **待重新签章** | 交车信息全部锁定,仅可重选被授权人;底部「返回」「重新发起签章」,完成 E签宝 运维签字后恢复「待客户签章」 |\n| **客户已签章** | 被授权人短信完成签章后进入「已完成」列表 |\n\n---\n\n## 三、查看态\n\n- **客户已签章 / 待客户签章**:只读浏览各步骤,不可编辑。\n- **待重新签章**:各步骤只读,仅「选择被授权人」可改。\n- **已完成**:可下载 E签宝签章 PDF;展示是否归还。\n\n---\n\n## 四、导航\n\n- 表单内左上返回先退出办理页;再次返回退出交车模块。\n- 步骤条可点击跳转。\n\n---\n\n## 五、验收要点\n\n请在预览中逐项确认(可提供截图或页面路径反馈):\n\n- [ ] 业务页进入交车模块,列表 KPI / 搜索 / 状态筛选可用\n- [ ] 进行中任务可「去办理 / 继续办理」,列表与表单同页切换\n- [ ] OCR / 相册识别车牌,未备车或保险/证照异常时阻断选车\n- [ ] 合同无被授权人时展示「无授权人,请联系{业管}补充授权人信息」并阻断发送签章\n- [ ] 车辆配置、驾驶培训、交车数据、交车位置各卡片可填写\n- [ ] 交车检查项四类可填,轮胎胎纹支持 mm 录入\n- [ ] 拍摄照片五类连拍、相册上传、调焦与备胎末项规则\n- [ ] 发送签章 → 待客户签章 → 清除签章 / 重新签章 / 客户已签章状态流转\n- [ ] 标注目录可打开本 PRD;页面标注 `page-delivery` 可定位交车模块\n\n**预览地址示例**:`http://localhost:51722/prototypes/xll-miniapp#page=delivery`\n\n---\n\n## 六、关联文档\n\n| 文档 | 位置 |\n| --- | --- |\n| 小程序总览 | 标注目录「PRD 00|小程序总览」 |\n| 审批中心改版 | 标注目录「PRD 01|审批中心改版说明」 |\n| 页面标注 | `page-delivery` |\n| 原型评审 | `src/prototypes/xll-miniapp/.spec/prototype-review.md` |\n" }, "assetMap": {}, "directory": { @@ -413,7 +432,7 @@ "id": "prd-delivery", "title": "PRD 02|交车说明", "markdownPath": "src/resources/xll-miniapp/PRD-02-交车说明.md", - "markdown": "# 小羚羚小程序 · 交车说明\n\n**文档版本**:V1.2 \n**更新日期**:2026-06-29 \n**适用范围**:运维管理 / 交车模块(`#page=delivery`) \n**参照**:web 交车管理 + Axhub「交车(完成)」原型 · 默认区域权限:浙江省-嘉兴市\n\n---\n\n## 一、列表页\n\n- **KPI**:全部 / 进行中 / 已完成,点击切换筛选。\n- **搜索**:车牌、项目、客户名称。\n- **卡片字段**:车牌(未选车显示「车辆待选」)、交车状态 Tag(未开始 / 已保存 / 待客户签章 / 待重新签章 / 客户已签章)、项目、客户、交车区域。\n- **进行中 Tab**:提供状态筛选 Chip(全部状态、未开始、已保存、待客户签章、待重新签章)。\n- **操作**:进行中任务展示「去办理 / 继续办理」按钮。\n\n---\n\n## 二、分步表单(顶部步骤条)\n\n顶部步骤条:**车辆情况** → **交车检查项** → **拍摄照片**,可点击切换。\n\n### 2.1 车辆情况\n\nHero 展示客户名称、项目名称与交车区域;含选择车辆、车辆配置、驾驶培训、交车数据、交车位置(独立卡片)。\n\n#### 识别车牌号\n\n拍照 OCR 识别车牌后,根据识别结果匹配车辆并校验交车资格,须同时满足:\n\n- **车辆状态 = 已备车**\n- **保险状态 = 正常**\n- **证照状态 = 正常**\n\n任一条件不满足则报错阻断,不可选车。报错提示文案与「选择车辆」页车辆卡片底部 readiness 提示条一致,例如:\n\n- 「交强险已到期无法交车」\n- 「商业险已到期无法交车」\n- 「交强险、商业险已到期无法交车」\n- 「行驶证已到期无法交车」\n- 未备车时「该车辆未备车,请先进行备车」\n\n支持**拍照识别**或**相册选取**车牌照片。\n\n#### 相册上传\n\n交车模块内**所有拍照环节**均支持从手机相册本地上传图片,包括:识别车牌号、车辆配置(车身广告/放大字)、驾驶培训手动记录证件照、交车连拍/补拍、瑕疵与其他照片添加、备胎拍摄等;相机页底部提供「拍照」「相册」操作。\n\n#### 车辆配置\n\n- 车身广告及放大字、尾板(必填,有则须上传照片)\n- 备胎(选填,仅选择有/无)\n\n#### 驾驶培训(必填)\n\n支持**电子培训** / **手动记录**两种模式。\n\n**电子培训**:展示培训二维码(微信扫码观看视频),完成后识别提车码。\n\n**手动记录(运维端)**:\n\n1. 录入司机手机号、姓名、身份证号\n2. 上传身份证正反面、驾驶证正反面、司机正面照片、从业资格证(重卡必填)\n3. 点击底部「生成培训码」\n4. 培训码与录入信息绑定,展示二维码供司机微信扫码\n5. 生成后交车页标记「待司机确认」,可「查看培训码」或「刷新培训状态」\n6. 司机签字完成并同步后标记「已完成驾驶培训」\n\n**手动记录 · 司机微信端**(仅需求说明,本期原型不实现):\n\n司机微信扫描二维码后,首先阅读并同意安全培训文件(倒计时 10 秒后可点确定);随后在司机微信浏览器展示运维登记的司机「姓名」「手机号」「身份证号」及身份证正反面、驾驶证正反面、司机正面照片、从业资格证照片;下方点击「确认签字」唤起签名板,完成签字后提示培训成功,相关信息同步至安全培训记录。\n\n#### 交车数据(必填)\n\n- 里程、氢量、电量均支持 2 位小数输入(无数字步进器)\n- 氢量单位按「型号参数」仪表盘单位随车型自动匹配(%/MPa,不可手动切换)\n- 送车服务费为选填,精确至 2 位小数(元)\n- 氢量下方备注为选填\n- **不含**交车时间字段\n\n#### 交车位置\n\n- 车辆 GPS 在线:交车位置取车辆 GPS 当前定位\n- 车辆 GPS 离线:交车位置取手机当前定位\n- 独立卡片展示地图、蓝点标记坐标;GPS 离线时支持手动「获取当前定位」\n\n### 2.2 交车检查项\n\n证件信息 → 工具信息 → 外观检查 → 轮胎检查。\n\n- 前 3 类:开关表示检查情况并可填备注\n- 轮胎检查项:填写胎纹深度 (mm)\n\n**实际交车时间**:运维提交 E签宝签章时系统自动记录;仅在交车列表「待客户签章」卡片展示。\n\n### 2.3 拍摄照片\n\n- 分车身 / 底盘 / 轮胎 / 瑕疵 / 其他五类\n- 连拍严格按顺序拍摄且不可跳过;中途退出后再次进入自动从下一项续拍\n- 模拟相机页支持**拍照或相册上传**,轮胎项下方录入胎纹;完成后可补拍瑕疵/其他\n- **所有拍照场景均支持调焦**:点击取景区域设定对焦点,右侧 +/- 调整 1.0×~3.0× 数码变焦(连拍、备胎、车身广告/放大字、手动记录证件照)\n\n**备胎照片**:从车辆情况步骤移至拍摄照片「轮胎情况」连拍末项(有备胎时必拍);车辆情况仅保留是否有备胎。\n\n**分步操作条**:车辆情况、交车检查项点击「下一步」时自动保存;拍摄照片完成后为「保存」「发送签章文件」。\n\n**照片上传**:连拍/单拍每次点击「完成」继续时,上一张自动上传并在服务端打水印(时间、地点)。\n\n**胎纹 OCR**:胎纹未反写成功时禁止继续拍摄下一张。\n\n### 2.4 签章流程\n\n**选择被授权人**:多卡片展示姓名与手机号,选中高亮;发送签章文件后跳转 E签宝 完成运维签字。\n\n| 状态 | 规则 |\n| --- | --- |\n| **待客户签章** | 只读查看各步骤,底部「返回」「清除签章」;确认后记录变为「待重新签章」 |\n| **待重新签章** | 交车信息全部锁定,仅可重选被授权人;底部「返回」「重新发起签章」,完成 E签宝 运维签字后恢复「待客户签章」 |\n| **客户已签章** | 被授权人短信完成签章后进入「已完成」列表 |\n\n---\n\n## 三、查看态\n\n- **客户已签章 / 待客户签章**:只读浏览各步骤,不可编辑。\n- **待重新签章**:各步骤只读,仅「选择被授权人」可改。\n- **已完成**:可下载 E签宝签章 PDF;展示是否归还。\n\n---\n\n## 四、导航\n\n- 表单内左上返回先退出办理页;再次返回退出交车模块。\n- 步骤条可点击跳转。\n\n---\n\n## 五、验收要点\n\n请在预览中逐项确认(可提供截图或页面路径反馈):\n\n- [ ] 业务页进入交车模块,列表 KPI / 搜索 / 状态筛选可用\n- [ ] 进行中任务可「去办理 / 继续办理」,列表与表单同页切换\n- [ ] OCR / 相册识别车牌,未备车或保险/证照异常时阻断选车\n- [ ] 车辆配置、驾驶培训、交车数据、交车位置各卡片可填写\n- [ ] 交车检查项四类可填,轮胎胎纹支持 mm 录入\n- [ ] 拍摄照片五类连拍、相册上传、调焦与备胎末项规则\n- [ ] 发送签章 → 待客户签章 → 清除签章 / 重新签章 / 客户已签章状态流转\n- [ ] 标注目录可打开本 PRD;页面标注 `page-delivery` 可定位交车模块\n\n**预览地址示例**:`http://localhost:51722/prototypes/xll-miniapp#page=delivery`\n\n---\n\n## 六、关联文档\n\n| 文档 | 位置 |\n| --- | --- |\n| 小程序总览 | 标注目录「PRD 00|小程序总览」 |\n| 审批中心改版 | 标注目录「PRD 01|审批中心改版说明」 |\n| 页面标注 | `page-delivery` |\n| 原型评审 | `src/prototypes/xll-miniapp/.spec/prototype-review.md` |\n" + "markdown": "# 小羚羚小程序 · 交车说明\n\n**文档版本**:V1.2 \n**更新日期**:2026-06-29 \n**适用范围**:运维管理 / 交车模块(`#page=delivery`) \n**参照**:web 交车管理 + Axhub「交车(完成)」原型 · 默认区域权限:浙江省-嘉兴市\n\n---\n\n## 一、列表页\n\n- **KPI**:全部 / 进行中 / 已完成,点击切换筛选。\n- **搜索**:车牌、项目、客户名称。\n- **卡片字段**:车牌(未选车显示「车辆待选」)、交车状态 Tag(未开始 / 已保存 / 待客户签章 / 待重新签章 / 客户已签章)、项目、客户、交车区域。\n- **进行中 Tab**:提供状态筛选 Chip(全部状态、未开始、已保存、待客户签章、待重新签章)。\n- **操作**:进行中任务展示「去办理 / 继续办理」按钮。\n\n---\n\n## 二、分步表单(顶部步骤条)\n\n顶部步骤条:**车辆情况** → **交车检查项** → **拍摄照片**,可点击切换。\n\n### 2.1 车辆情况\n\nHero 展示客户名称、项目名称与交车区域;含选择车辆、车辆配置、驾驶培训、交车数据、交车位置(独立卡片)。\n\n#### 识别车牌号\n\n拍照 OCR 识别车牌后,根据识别结果匹配车辆并校验交车资格,须同时满足:\n\n- **车辆状态 = 已备车**\n- **保险状态 = 正常**\n- **证照状态 = 正常**\n\n任一条件不满足则报错阻断,不可选车。报错提示文案与「选择车辆」页车辆卡片底部 readiness 提示条一致,例如:\n\n- 「交强险已到期无法交车」\n- 「商业险已到期无法交车」\n- 「交强险、商业险已到期无法交车」\n- 「行驶证已到期无法交车」\n- 未备车时「该车辆未备车,请先进行备车」\n\n支持**拍照识别**或**相册选取**车牌照片。\n\n#### 相册上传\n\n交车模块内**所有拍照环节**均支持从手机相册本地上传图片,包括:识别车牌号、车辆配置(车身广告/放大字)、驾驶培训手动记录证件照、交车连拍/补拍、瑕疵与其他照片添加、备胎拍摄等;相机页底部提供「拍照」「相册」操作。\n\n#### 车辆配置\n\n- 车身广告及放大字、尾板(必填,有则须上传照片)\n- 备胎(选填,仅选择有/无)\n\n#### 驾驶培训(必填)\n\n支持**电子培训** / **手动记录**两种模式。\n\n**电子培训**:展示培训二维码(微信扫码观看视频),完成后识别提车码。\n\n**手动记录(运维端)**:\n\n1. 录入司机手机号、姓名、身份证号\n2. 上传身份证正反面、驾驶证正反面、司机正面照片、从业资格证(重卡必填)\n3. 点击底部「生成培训码」\n4. 培训码与录入信息绑定,展示二维码供司机微信扫码\n5. 生成后交车页标记「待司机确认」,可「查看培训码」或「刷新培训状态」\n6. 司机签字完成并同步后标记「已完成驾驶培训」\n\n**手动记录 · 司机微信端**(仅需求说明,本期原型不实现):\n\n司机微信扫描二维码后,首先阅读并同意安全培训文件(倒计时 10 秒后可点确定);随后在司机微信浏览器展示运维登记的司机「姓名」「手机号」「身份证号」及身份证正反面、驾驶证正反面、司机正面照片、从业资格证照片;下方点击「确认签字」唤起签名板,完成签字后提示培训成功,相关信息同步至安全培训记录。\n\n#### 交车数据(必填)\n\n- 里程、氢量、电量均支持 2 位小数输入(无数字步进器)\n- 氢量单位按「型号参数」仪表盘单位随车型自动匹配(%/MPa,不可手动切换)\n- 送车服务费为选填,精确至 2 位小数(元)\n- 氢量下方备注为选填\n- **不含**交车时间字段\n\n#### 交车位置\n\n- 车辆 GPS 在线:交车位置取车辆 GPS 当前定位\n- 车辆 GPS 离线:交车位置取手机当前定位\n- 独立卡片展示地图、蓝点标记坐标;GPS 离线时支持手动「获取当前定位」\n\n### 2.2 交车检查项\n\n证件信息 → 工具信息 → 外观检查 → 轮胎检查。\n\n- 前 3 类:开关表示检查情况并可填备注\n- 轮胎检查项:填写胎纹深度 (mm)\n\n**实际交车时间**:运维提交 E签宝签章时系统自动记录;仅在交车列表「待客户签章」卡片展示。\n\n### 2.3 拍摄照片\n\n- 分车身 / 底盘 / 轮胎 / 瑕疵 / 其他五类\n- 连拍严格按顺序拍摄且不可跳过;中途退出后再次进入自动从下一项续拍\n- 模拟相机页支持**拍照或相册上传**,轮胎项下方录入胎纹;完成后可补拍瑕疵/其他\n- **所有拍照场景均支持调焦**:点击取景区域设定对焦点,右侧 +/- 调整 1.0×~3.0× 数码变焦(连拍、备胎、车身广告/放大字、手动记录证件照)\n\n**备胎照片**:从车辆情况步骤移至拍摄照片「轮胎情况」连拍末项(有备胎时必拍);车辆情况仅保留是否有备胎。\n\n**分步操作条**:车辆情况、交车检查项点击「下一步」时自动保存;拍摄照片完成后为「保存」「发送签章文件」。\n\n**照片上传**:连拍/单拍每次点击「完成」继续时,上一张自动上传并在服务端打水印(时间、地点)。\n\n**胎纹 OCR**:胎纹未反写成功时禁止继续拍摄下一张。\n\n### 2.4 签章流程\n\n**选择被授权人**:多卡片展示姓名与手机号,选中高亮;发送签章文件后跳转 E签宝 完成运维签字。若关联合同无被授权人,页面展示「无授权人,请联系{业管姓名}补充授权人信息」(业管自动取合同业务负责人 `businessOwner`),并阻断发送签章。规则全文见 `.spec/authorized-person.md`。\n\n| 状态 | 规则 |\n| --- | --- |\n| **待客户签章** | 只读查看各步骤,底部「返回」「清除签章」;确认后记录变为「待重新签章」 |\n| **待重新签章** | 交车信息全部锁定,仅可重选被授权人;底部「返回」「重新发起签章」,完成 E签宝 运维签字后恢复「待客户签章」 |\n| **客户已签章** | 被授权人短信完成签章后进入「已完成」列表 |\n\n---\n\n## 三、查看态\n\n- **客户已签章 / 待客户签章**:只读浏览各步骤,不可编辑。\n- **待重新签章**:各步骤只读,仅「选择被授权人」可改。\n- **已完成**:可下载 E签宝签章 PDF;展示是否归还。\n\n---\n\n## 四、导航\n\n- 表单内左上返回先退出办理页;再次返回退出交车模块。\n- 步骤条可点击跳转。\n\n---\n\n## 五、验收要点\n\n请在预览中逐项确认(可提供截图或页面路径反馈):\n\n- [ ] 业务页进入交车模块,列表 KPI / 搜索 / 状态筛选可用\n- [ ] 进行中任务可「去办理 / 继续办理」,列表与表单同页切换\n- [ ] OCR / 相册识别车牌,未备车或保险/证照异常时阻断选车\n- [ ] 合同无被授权人时展示「无授权人,请联系{业管}补充授权人信息」并阻断发送签章\n- [ ] 车辆配置、驾驶培训、交车数据、交车位置各卡片可填写\n- [ ] 交车检查项四类可填,轮胎胎纹支持 mm 录入\n- [ ] 拍摄照片五类连拍、相册上传、调焦与备胎末项规则\n- [ ] 发送签章 → 待客户签章 → 清除签章 / 重新签章 / 客户已签章状态流转\n- [ ] 标注目录可打开本 PRD;页面标注 `page-delivery` 可定位交车模块\n\n**预览地址示例**:`http://localhost:51722/prototypes/xll-miniapp#page=delivery`\n\n---\n\n## 六、关联文档\n\n| 文档 | 位置 |\n| --- | --- |\n| 小程序总览 | 标注目录「PRD 00|小程序总览」 |\n| 审批中心改版 | 标注目录「PRD 01|审批中心改版说明」 |\n| 页面标注 | `page-delivery` |\n| 原型评审 | `src/prototypes/xll-miniapp/.spec/prototype-review.md` |\n" } ] } diff --git a/src/prototypes/xll-miniapp/index.tsx b/src/prototypes/xll-miniapp/index.tsx index 0b5d464..256795d 100644 --- a/src/prototypes/xll-miniapp/index.tsx +++ b/src/prototypes/xll-miniapp/index.tsx @@ -131,12 +131,56 @@ const TASK_THEME = { inspection: { accent: COLOR_WARN, soft: 'rgba(255, 125, 0, 0.12)', label: '年审' }, transfer: { accent: '#8B5CF6', soft: 'rgba(139, 92, 246, 0.12)', label: '调拨' }, move: { accent: '#14B8A6', soft: 'rgba(20, 184, 166, 0.12)', label: '异动' }, + audit: { accent: '#9333EA', soft: 'rgba(147, 51, 234, 0.12)', label: '审批' }, }; +const TODO_ACTION_META = { + approve: { label: '需审批', tone: 'purple' }, + fill: { label: '需填报', tone: 'blue' }, + initiate: { label: '需发起', tone: 'orange' }, + execute: { label: '需执行', tone: 'green' }, + follow: { label: '需跟进', tone: 'slate' }, + notify: { label: '仅知会', tone: 'gray' }, +}; + +const TODO_RISK_META = { + high: { label: '高', color: '#F53F3F', bg: 'rgba(245, 63, 63, 0.1)' }, + medium: { label: '中', color: '#FF7D00', bg: 'rgba(255, 125, 0, 0.12)' }, + low: { label: '低', color: '#00B42A', bg: 'rgba(0, 180, 42, 0.1)' }, + none: { label: '无', color: '#86909C', bg: 'rgba(134, 144, 156, 0.12)' }, +}; + +const TODO_ZONES = [ + { key: 'today', label: '今日必办' }, + { key: 'handle', label: '待我处理' }, + { key: 'follow', label: '待我跟进' }, + { key: 'notify', label: '仅知会' }, +]; + const TODO_TASKS = [ { - id: 't1', type: 'delivery', badge: null, + id: 't6', type: 'audit', zone: 'today', actionType: 'approve', riskLevel: 'high', + title: '还车应结款 · 财务终审', + riskHint: '高金额应退 ¥4,072.50', + nextOwner: '赵敏', + nextAction: '执行退款(2 工作日内)', + slaRemain: '剩余 6h', + cta: '去审批', + targetPage: 'audit', + fields: [ + { label: '车牌号', value: '粤B58888F' }, + { label: '应退金额', value: '¥4,072.50' }, + { label: '发起人', value: '陈高伟' }, + ], + }, + { + id: 't1', type: 'delivery', zone: 'today', actionType: 'execute', riskLevel: 'medium', title: '交车任务(3辆)', + riskHint: '交车窗口 6h 内', + nextOwner: '客户', + nextAction: '签署交车单', + slaRemain: '剩余 6h', + cta: '去办理', fields: [ { label: '项目名称', value: '嘉兴氢能示范项目' }, { label: '客户名称', value: '嘉兴某某物流有限公司' }, @@ -145,25 +189,26 @@ const TODO_TASKS = [ ], }, { - id: 't2', type: 'return', badge: null, - title: '还车任务(粤AGP5368)', - fields: [ - { label: '项目名称', value: '嘉兴腾4.5T租赁' }, - { label: '客户名称', value: '嘉兴某某物流有限公司' }, - { label: '还车时间', value: '2026-06-03 16:20' }, - ], - }, - { - id: 't3', type: 'inspection', badge: 4, + id: 't3', type: 'inspection', badge: 4, zone: 'handle', actionType: 'execute', riskLevel: 'high', title: '年审任务', + riskHint: '证照已到期,阻塞运营', + nextOwner: '张明辉', + nextAction: '完成年审办理', + slaRemain: '已超期 10 天', + cta: '去办理', fields: [ { label: '车牌号', value: '粤B58888F' }, { label: '年审/等评时间', value: '2026-05-28(已到期)', warn: true }, ], }, { - id: 't4', type: 'transfer', badge: 2, + id: 't4', type: 'transfer', badge: 2, zone: 'handle', actionType: 'approve', riskLevel: 'medium', title: '张三发起的调拨申请', + riskHint: '40 台车跨区调拨', + nextOwner: '运维中心', + nextAction: '安排运输与接车', + slaRemain: '剩余 1 天', + cta: '去审批', fields: [ { label: '调拨日期', value: '2026-06-01' }, { label: '出发区域', value: '广东省深圳市' }, @@ -172,16 +217,68 @@ const TODO_TASKS = [ ], }, { - id: 't5', type: 'move', badge: 3, + id: 't5', type: 'move', badge: 3, zone: 'handle', actionType: 'approve', riskLevel: 'low', title: '异动申请(粤A08875F)', + riskHint: '保养异动,无合规异常', + nextOwner: '运维一部', + nextAction: '现场确认异动结束', + slaRemain: '剩余 2 天', + cta: '去审批', fields: [ { label: '异动类型', value: '保养' }, { label: '目的地', value: '嘉兴xx检测站' }, { label: '计划时间', value: '2026-06-02 08:00' }, ], }, + { + id: 't2', type: 'return', zone: 'handle', actionType: 'execute', riskLevel: 'low', + title: '还车任务(粤AGP5368)', + riskHint: '还车验车待完成', + nextOwner: '运维组', + nextAction: '完成验车与结算发起', + slaRemain: '剩余 3 天', + cta: '去办理', + fields: [ + { label: '项目名称', value: '嘉兴腾4.5T租赁' }, + { label: '客户名称', value: '嘉兴某某物流有限公司' }, + { label: '还车时间', value: '2026-06-03 16:20' }, + ], + }, + { + id: 't7', type: 'audit', zone: 'follow', actionType: 'initiate', riskLevel: 'medium', + title: '还车应结款 · 待发起主流程', + riskHint: '四部门已全部通过,待你发起', + nextOwner: '财务审核', + nextAction: '主流程审批', + slaRemain: '建议今日发起', + cta: '去发起', + targetPage: 'audit', + fields: [ + { label: '车牌号', value: '苏E12345F' }, + { label: '当前进度', value: '四部门子流程已通过' }, + { label: '阻塞说明', value: '主流程未发起,退款未启动' }, + ], + }, + { + id: 't8', type: 'audit', zone: 'notify', actionType: 'notify', riskLevel: 'none', + title: '租赁账单 · 抄送知会', + riskHint: '无需操作,知悉即可', + nextOwner: '—', + nextAction: '—', + slaRemain: '—', + cta: '查看', + targetPage: 'audit', + fields: [ + { label: '客户名称', value: '嘉兴某某物流有限公司' }, + { label: '账单周期', value: '2026年6月' }, + { label: '当前节点', value: '业管主管审批中(李晓彤)' }, + ], + }, ]; +const todoActionableCount = (tasks) => tasks.filter((t) => ['approve', 'fill', 'initiate', 'execute'].includes(t.actionType)).length; +const todoTasksByZone = (tasks, zoneKey) => tasks.filter((t) => t.zone === zoneKey); + const BUSINESS_SECTIONS = [ { title: '运维管理', @@ -272,7 +369,9 @@ const PAGE_STYLE = ` .xll-mod-card::before { content:''; position:absolute; left:12px; right:12px; top:0; height:2px; border-radius:2px 2px 0 0; background:var(--mod-accent, ${XLL_GREEN}); } .xll-mod-card:focus-visible { outline:2px solid ${XLL_GREEN}; outline-offset:2px; } .xll-mod-card-head { display:flex; justify-content:space-between; align-items:center; gap:10px; margin-bottom:8px; } +.xll-mod-card-head-tags { display:flex; align-items:center; gap:6px; flex-wrap:wrap; min-width:0; } .xll-mod-card-type { font-size:11px; font-weight:600; color:var(--mod-accent, ${XLL_GREEN}); background:var(--mod-soft, ${XLL_GREEN_SOFT}); padding:2px 8px; border-radius:999px; } +.xll-mod-card-cc-tag { font-size:11px; font-weight:600; color:#64748B; background:rgba(100,116,139,.12); padding:2px 8px; border-radius:999px; flex-shrink:0; } .xll-mod-card-status { display:inline-flex; align-items:center; justify-content:center; font-size:11px; font-weight:600; line-height:1; padding:4px 8px; border-radius:999px; box-sizing:border-box; flex-shrink:0; } .xll-mod-card-status.pending { color:${COLOR_WARN}; background:rgba(255,125,0,.1); } .xll-mod-card-status.with-approvers { max-width:58%; text-align:right; line-height:1.35; white-space:normal; } @@ -290,6 +389,7 @@ const PAGE_STYLE = ` .xll-mod-meta-val { color:${COLOR_TEXT_SEC}; font-weight:500; } .xll-mod-card-foot { display:flex; justify-content:space-between; align-items:center; margin-top:10px; padding-top:10px; border-top:1px solid ${COLOR_LINE}; } .xll-mod-card-btn { min-height:44px; padding:0 14px; border-radius:10px; border:1px solid rgba(122,185,41,.35); background:${XLL_GREEN_SOFT}; color:${XLL_GREEN}; font-size:13px; font-weight:600; cursor:pointer; touch-action:manipulation; } +.xll-mod-card-btn--view { border-color:rgba(100,116,139,.35); background:rgba(100,116,139,.1); color:#475569; } .xll-mod-card-btn:focus-visible { outline:2px solid ${XLL_GREEN}; outline-offset:2px; } .xll-mod-empty { text-align:center; padding:48px 24px; color:${COLOR_MUTED}; font-size:14px; line-height:1.6; } .xll-mod-scroll { flex:1; min-height:0; overflow-y:auto; -webkit-overflow-scrolling:touch; padding-bottom:88px; } @@ -534,6 +634,11 @@ const PAGE_STYLE = ` .xll-dv-photo-camera-actions { display:flex; gap:10px; padding:12px 16px calc(12px + env(safe-area-inset-bottom,0px)); background:${COLOR_BG}; border-top:1px solid ${COLOR_LINE}; } .xll-dv-photo-camera-actions--single .xll-dv-spare-capture-retake { flex:1; min-width:0; } .xll-dv-photo-camera-actions--dual .xll-dv-spare-capture-retake { flex:1; min-width:0; } +.xll-dv-photo-camera-actions--pair > .xll-dv-spare-capture-retake, +.xll-dv-photo-camera-actions--pair > .xll-dv-photo-album-btn { flex:1; min-width:0; } +.xll-dv-photo-camera-actions--triple > .xll-dv-spare-capture-retake, +.xll-dv-photo-camera-actions--triple > .xll-dv-photo-album-btn, +.xll-dv-photo-camera-actions--triple > .xll-dv-spare-capture-done { flex:1; min-width:0; } .xll-dv-photo-album-float { position:absolute; z-index:4; right:12px; bottom:14px; min-width:44px; min-height:44px; padding:0 14px; border:1px solid rgba(255,255,255,.35); border-radius:999px; background:rgba(0,0,0,.45); color:#fff; font-size:13px; font-weight:600; cursor:pointer; touch-action:manipulation; backdrop-filter:blur(6px); } .xll-dv-photo-album-float:active { background:rgba(0,0,0,.62); } .xll-dv-photo-album-btn { flex:0 0 auto; min-width:88px; min-height:44px; padding:0 12px; border:1px solid ${COLOR_LINE}; border-radius:10px; background:#fff; color:${COLOR_TEXT_SEC}; font-size:14px; font-weight:600; cursor:pointer; touch-action:manipulation; } @@ -758,6 +863,8 @@ const PAGE_STYLE = ` .xll-dv-authorized-panel { padding:12px 14px 14px; } .xll-dv-authorized-section .xll-dv-summary-card { margin:0 0 12px; } .xll-dv-authorized-hint { margin:0 0 10px; font-size:12px; color:${COLOR_TEXT_SEC}; line-height:1.55; } +.xll-dv-authorized-empty { margin:0; padding:12px 14px; border-radius:10px; background:rgba(255,125,0,.1); border:1px solid rgba(255,125,0,.28); color:#C2410C; font-size:13px; line-height:1.55; } +.xll-dv-authorized-empty strong { color:${COLOR_TEXT}; font-weight:700; } .xll-dv-authorized-subtitle { margin:0 0 8px; font-size:12px; font-weight:600; color:${COLOR_MUTED}; } .xll-dv-authorized-grid { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:10px; } .xll-dv-authorized-card { position:relative; display:flex; flex-direction:column; align-items:center; justify-content:center; min-height:96px; padding:14px 10px 12px; border-radius:12px; border:1.5px solid ${COLOR_LINE}; background:${COLOR_BG}; text-align:center; cursor:pointer; touch-action:manipulation; transition:border-color .15s ease, background .15s ease, box-shadow .15s ease; -webkit-appearance:none; appearance:none; font:inherit; color:inherit; } @@ -864,6 +971,28 @@ const PAGE_STYLE = ` .xll-vm-customer-bar:not(.is-long) .xll-vm-customer-text { -webkit-line-clamp:1; } .xll-vm-customer-hint { display:block; margin-top:4px; font-size:11px; color:${XLL_GREEN}; font-weight:500; } .xll-vm-name-modal-text { font-size:15px; color:${COLOR_TEXT}; line-height:1.6; word-break:break-word; overflow-wrap:anywhere; padding:4px 0; } +.xll-vm-kpi-row { display:flex; gap:8px; padding:10px 14px 2px; overflow-x:auto; scrollbar-width:none; -webkit-overflow-scrolling:touch; } +.xll-vm-kpi-row::-webkit-scrollbar { display:none; } +.xll-vm-kpi-chip { flex-shrink:0; min-width:76px; padding:10px 12px; border-radius:12px; background:${COLOR_BG}; border:1px solid ${COLOR_LINE}; cursor:pointer; touch-action:manipulation; text-align:left; transition:border-color .15s ease,background-color .15s ease,box-shadow .15s ease; } +.xll-vm-kpi-chip:active { transform:scale(0.98); } +.xll-vm-kpi-chip.active { border-color:${XLL_GREEN}; background:${XLL_GREEN_SOFT}; box-shadow:0 2px 8px rgba(122,185,41,.12); } +.xll-vm-kpi-chip-val { display:block; font-size:18px; font-weight:800; color:${COLOR_TEXT}; font-variant-numeric:tabular-nums; line-height:1.2; } +.xll-vm-kpi-chip-label { display:block; margin-top:2px; font-size:11px; color:${COLOR_MUTED}; font-weight:500; } +.xll-vm-section-card { margin:12px 14px 0; padding:14px; background:${COLOR_BG}; border-radius:14px; border:1px solid rgba(0,0,0,.04); box-shadow:0 2px 8px rgba(15,23,42,.04); } +.xll-vm-section-card .xll-mod-section-title { margin-bottom:12px; } +.xll-vm-location-head { display:flex; align-items:center; justify-content:space-between; gap:10px; margin-bottom:10px; } +.xll-vm-location-meta { margin-top:8px; font-size:12px; color:${COLOR_MUTED}; line-height:1.45; } +.xll-vm-location-map--compact { height:120px; border-radius:10px; } +.xll-vm-card-location { display:flex; align-items:flex-start; gap:8px; margin:0 0 10px; padding:9px 10px; background:${COLOR_PAGE}; border-radius:10px; border:1px solid rgba(0,0,0,.04); min-width:0; } +.xll-vm-location-pin { flex-shrink:0; width:18px; height:18px; margin-top:1px; border-radius:50%; background:${XLL_GREEN_SOFT}; position:relative; } +.xll-vm-location-pin::after { content:''; position:absolute; left:50%; top:50%; width:8px; height:8px; margin:-4px 0 0 -4px; border-radius:50%; background:${XLL_GREEN}; box-shadow:0 0 0 2px #fff; } +.xll-vm-card-location-body { flex:1; min-width:0; } +.xll-vm-card-location-text { display:block; font-size:12px; color:${COLOR_TEXT}; font-weight:600; line-height:1.45; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; } +.xll-vm-card-location-time { display:block; margin-top:2px; font-size:11px; color:${COLOR_MUTED}; font-variant-numeric:tabular-nums; } +.xll-vm-list-card { position:relative; overflow:hidden; } +.xll-vm-list-card::before { content:''; position:absolute; left:12px; right:12px; top:0; height:2px; border-radius:2px 2px 0 0; background:var(--mod-accent, ${XLL_GREEN}); opacity:.85; } +.xll-vm-list-card .xll-mod-card-head { padding-top:4px; } +.xll-vm-subtab:focus-visible { outline:2px solid ${XLL_GREEN}; outline-offset:-2px; } .xll-list-head { display:flex; align-items:center; justify-content:space-between; padding:12px 16px 4px; font-size:12px; color:${COLOR_MUTED}; } .xll-list-count { font-variant-numeric:tabular-nums; font-weight:600; } .xll-task-card { position:relative; margin:0 14px 12px; background:${COLOR_BG}; border-radius:14px; overflow:hidden; box-shadow:0 2px 8px rgba(15,23,42,.05); border:1px solid rgba(0,0,0,.04); animation:xll-card-in 0.35s ease both; } @@ -886,6 +1015,36 @@ const PAGE_STYLE = ` .xll-kv-value.warn { color:${COLOR_DANGER}; font-weight:600; } .xll-kv-value.warn::after { content:' '; } .xll-warn-tag { display:inline-flex; align-items:center; gap:4px; font-size:11px; font-weight:600; color:${COLOR_DANGER}; background:rgba(245,63,63,.1); padding:1px 6px; border-radius:4px; margin-left:4px; vertical-align:middle; } +.xll-todo-summary { display:flex; flex-wrap:wrap; gap:8px; padding:0 16px 10px; } +.xll-todo-summary-chip { display:inline-flex; align-items:center; gap:6px; min-height:32px; padding:0 10px; border-radius:999px; border:1px solid ${COLOR_LINE}; background:${COLOR_BG}; font-size:12px; color:${COLOR_TEXT_SEC}; font-variant-numeric:tabular-nums; } +.xll-todo-summary-chip b { color:${COLOR_TEXT}; font-weight:700; } +.xll-todo-summary-chip--accent { border-color:rgba(122,185,41,.35); background:${XLL_GREEN_SOFT}; color:${XLL_GREEN_DEEP}; } +.xll-todo-zone { margin-bottom:4px; } +.xll-todo-zone-head { display:flex; align-items:center; justify-content:space-between; padding:8px 16px 6px; font-size:12px; font-weight:600; color:${COLOR_MUTED}; letter-spacing:0.02em; } +.xll-todo-zone-count { font-variant-numeric:tabular-nums; font-weight:700; color:${COLOR_TEXT_SEC}; } +.xll-task-meta-row { display:flex; flex-wrap:wrap; gap:6px; margin-bottom:8px; } +.xll-task-pill { display:inline-flex; align-items:center; min-height:22px; padding:0 8px; border-radius:999px; font-size:11px; font-weight:600; line-height:1.2; } +.xll-task-pill--purple { color:#7C3AED; background:rgba(124,58,237,.12); } +.xll-task-pill--blue { color:#2563EB; background:rgba(37,99,235,.12); } +.xll-task-pill--orange { color:#EA580C; background:rgba(234,88,12,.12); } +.xll-task-pill--green { color:${XLL_GREEN_DEEP}; background:${XLL_GREEN_SOFT}; } +.xll-task-pill--slate { color:#475569; background:rgba(71,85,105,.1); } +.xll-task-pill--gray { color:#64748B; background:rgba(100,116,139,.12); } +.xll-task-risk { font-variant-numeric:tabular-nums; } +.xll-task-risk--high { color:#F53F3F; background:rgba(245,63,63,.1); } +.xll-task-risk--medium { color:#FF7D00; background:rgba(255,125,0,.12); } +.xll-task-risk--low { color:#00B42A; background:rgba(0,180,42,.1); } +.xll-task-risk--none { color:#86909C; background:rgba(134,144,156,.12); } +.xll-task-risk-hint { display:flex; align-items:flex-start; gap:6px; margin:0 16px 10px; padding:8px 10px; border-radius:10px; background:${COLOR_PAGE}; border:1px solid rgba(0,0,0,.04); font-size:12px; line-height:1.45; color:${COLOR_TEXT_SEC}; } +.xll-task-risk-hint svg { flex-shrink:0; margin-top:1px; color:${COLOR_WARN}; } +.xll-task-chain { margin:0 16px 12px; padding:10px 12px; border-radius:10px; background:linear-gradient(135deg, rgba(122,185,41,.06) 0%, rgba(122,185,41,.02) 100%); border:1px solid rgba(122,185,41,.18); font-size:12px; line-height:1.5; color:${COLOR_TEXT_SEC}; } +.xll-task-chain strong { color:${COLOR_TEXT}; font-weight:600; } +.xll-task-chain-line { display:block; margin-top:4px; } +.xll-task-foot { display:flex; align-items:center; justify-content:space-between; gap:8px; padding:0 16px 14px; font-size:11px; color:${COLOR_MUTED}; font-variant-numeric:tabular-nums; } +.xll-task-sla { font-weight:600; } +.xll-task-sla.is-overdue { color:${COLOR_DANGER}; } +.xll-task-card--notify { opacity:.92; } +.xll-task-card--notify .xll-task-action { color:${COLOR_MUTED}; border-color:${COLOR_LINE}; background:${COLOR_PAGE}; } .xll-biz-section { margin:0 14px 14px; background:${COLOR_BG}; border-radius:14px; padding:14px 12px 6px; box-shadow:0 2px 8px rgba(15,23,42,.04); border:1px solid rgba(0,0,0,.03); } .xll-biz-section-title { font-size:13px; font-weight:600; color:${COLOR_MUTED}; margin-bottom:12px; padding-left:4px; letter-spacing:0.02em; } .xll-biz-grid { display:grid; grid-template-columns:repeat(3,1fr); gap:12px 8px; } @@ -919,6 +1078,53 @@ const PAGE_STYLE = ` .xll-map-full:focus-visible { outline:2px solid ${XLL_GREEN}; outline-offset:2px; } .xll-map-marker { position:absolute; width:36px; height:36px; border-radius:50%; background:${COLOR_BG}; border:2px solid ${XLL_GREEN}; box-shadow:0 2px 8px rgba(0,0,0,.15); display:flex; align-items:center; justify-content:center; color:${XLL_GREEN}; z-index:1; } .xll-map-marker--station { border-color:#2563EB; color:#2563EB; } +.xll-map-marker--repair { border-color:#FF7D00; color:#FF7D00; } +.xll-map-marker--cluster { width:44px; height:44px; font-size:13px; font-weight:700; border-width:2px; cursor:pointer; z-index:3; } +.xll-map-marker-btn { border:none; padding:0; cursor:pointer; touch-action:manipulation; transition:transform 0.15s ease; } +.xll-map-marker-btn:active { transform:scale(0.94); } +.xll-map-marker-btn:focus-visible { outline:2px solid ${XLL_GREEN}; outline-offset:2px; } +.xll-map-mode-bar { display:flex; gap:8px; padding:10px 14px; background:${COLOR_BG}; border-bottom:1px solid ${COLOR_LINE}; overflow-x:auto; -webkit-overflow-scrolling:touch; } +.xll-map-mode-bar::-webkit-scrollbar { display:none; } +.xll-map-mode-btn { flex-shrink:0; min-height:36px; padding:0 14px; border-radius:999px; border:1px solid ${COLOR_LINE}; background:${COLOR_PAGE}; color:${COLOR_TEXT_SEC}; font-size:13px; font-weight:600; cursor:pointer; touch-action:manipulation; } +.xll-map-mode-btn.active { background:${XLL_GREEN_SOFT}; border-color:rgba(122,185,41,.45); color:${XLL_GREEN}; } +.xll-map-role-bar { display:flex; align-items:center; gap:8px; padding:8px 14px 10px; background:${COLOR_BG}; border-bottom:1px solid ${COLOR_LINE}; overflow-x:auto; -webkit-overflow-scrolling:touch; } +.xll-map-role-bar::-webkit-scrollbar { display:none; } +.xll-map-role-label { flex-shrink:0; font-size:12px; color:${COLOR_MUTED}; font-weight:600; } +.xll-map-role-chip { flex-shrink:0; min-height:32px; padding:0 12px; border-radius:999px; border:1px solid ${COLOR_LINE}; background:${COLOR_BG}; color:${COLOR_TEXT_SEC}; font-size:12px; font-weight:600; cursor:pointer; touch-action:manipulation; } +.xll-map-role-chip.active { background:${COLOR_TEXT}; border-color:${COLOR_TEXT}; color:#fff; } +.xll-map-role-chip--external.active { background:#2563EB; border-color:#2563EB; } +.xll-map-list { padding:0 14px 14px; display:flex; flex-direction:column; gap:10px; } +.xll-map-list-head { display:flex; align-items:center; justify-content:space-between; padding:12px 0 4px; font-size:13px; color:${COLOR_MUTED}; } +.xll-map-list-card { width:100%; text-align:left; border:1px solid ${COLOR_LINE}; border-radius:12px; background:${COLOR_BG}; padding:14px; cursor:pointer; touch-action:manipulation; box-shadow:0 1px 4px rgba(15,23,42,.04); } +.xll-map-list-card:active { background:${COLOR_PAGE}; } +.xll-map-list-card-title { font-size:16px; font-weight:700; color:${COLOR_TEXT}; margin-bottom:4px; } +.xll-map-list-card-sub { font-size:13px; color:${COLOR_TEXT_SEC}; margin-bottom:8px; } +.xll-map-list-card-meta { display:flex; align-items:center; justify-content:space-between; font-size:12px; color:${COLOR_MUTED}; } +.xll-map-stat-grid { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:10px; padding:12px 14px 14px; } +.xll-map-stat-card { border:1px solid ${COLOR_LINE}; border-radius:12px; background:${COLOR_BG}; padding:14px; text-align:left; cursor:pointer; touch-action:manipulation; box-shadow:0 1px 4px rgba(15,23,42,.04); } +.xll-map-stat-card:active { background:${COLOR_PAGE}; } +.xll-map-stat-val { font-size:24px; font-weight:800; color:${COLOR_TEXT}; font-variant-numeric:tabular-nums; letter-spacing:-0.02em; margin-bottom:4px; } +.xll-map-stat-label { font-size:12px; color:${COLOR_MUTED}; font-weight:600; } +.xll-map-stat-hint { margin-top:6px; font-size:11px; color:${XLL_GREEN}; font-weight:600; } +.xll-map-detail { display:flex; flex-direction:column; min-height:min-content; } +.xll-map-detail-head { display:flex; align-items:center; gap:8px; padding:8px 14px; border-bottom:1px solid ${COLOR_LINE}; background:${COLOR_BG}; } +.xll-map-detail-back { min-width:44px; min-height:44px; border:none; background:transparent; color:${COLOR_TEXT}; cursor:pointer; display:flex; align-items:center; justify-content:center; touch-action:manipulation; } +.xll-map-detail-title { flex:1; min-width:0; font-size:16px; font-weight:700; color:${COLOR_TEXT}; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; } +.xll-map-detail-hero { margin:12px 14px 0; padding:16px; border-radius:12px; background:linear-gradient(135deg,${XLL_GREEN_SOFT} 0%,${COLOR_BG} 100%); border:1px solid rgba(122,185,41,.2); } +.xll-map-detail-hero-name { font-size:18px; font-weight:800; color:${COLOR_TEXT}; margin-bottom:4px; } +.xll-map-detail-hero-sub { font-size:13px; color:${COLOR_TEXT_SEC}; margin-bottom:8px; } +.xll-map-detail-tag { display:inline-flex; align-items:center; min-height:24px; padding:0 10px; border-radius:999px; background:${XLL_GREEN_SOFT}; color:${XLL_GREEN}; font-size:12px; font-weight:700; } +.xll-map-detail-tabs { display:flex; gap:0; margin:12px 14px 0; border-bottom:1px solid ${COLOR_LINE}; overflow-x:auto; -webkit-overflow-scrolling:touch; } +.xll-map-detail-tab { flex-shrink:0; min-height:44px; padding:0 14px; border:none; background:transparent; font-size:14px; font-weight:600; color:${COLOR_MUTED}; cursor:pointer; position:relative; touch-action:manipulation; } +.xll-map-detail-tab.active { color:${XLL_GREEN}; } +.xll-map-detail-tab.active::after { content:''; position:absolute; left:14px; right:14px; bottom:0; height:3px; border-radius:3px 3px 0 0; background:${XLL_GREEN}; } +.xll-map-detail-body { padding:12px 14px 16px; } +.xll-map-detail-kv { display:flex; justify-content:space-between; gap:12px; padding:12px 0; border-bottom:1px solid ${COLOR_LINE}; font-size:14px; } +.xll-map-detail-kv:last-child { border-bottom:none; } +.xll-map-detail-kv-label { color:${COLOR_MUTED}; flex-shrink:0; } +.xll-map-detail-kv-value { color:${COLOR_TEXT}; font-weight:500; text-align:right; word-break:break-all; } +.xll-map-empty { padding:32px 24px; text-align:center; color:${COLOR_MUTED}; font-size:14px; line-height:1.6; } +.xll-map-filter-tag { display:inline-flex; align-items:center; gap:6px; margin:0 14px 8px; padding:6px 10px; border-radius:999px; background:${XLL_GREEN_SOFT}; color:${XLL_GREEN}; font-size:12px; font-weight:600; } .xll-map-brand { position:absolute; left:8px; bottom:8px; font-size:10px; color:${COLOR_MUTED}; background:rgba(255,255,255,.85); padding:3px 8px; border-radius:6px; border:1px solid ${COLOR_LINE}; } .xll-mine-hero { margin:12px 14px 0; padding:20px 16px; background:linear-gradient(135deg, ${XLL_GREEN} 0%, ${XLL_GREEN_DEEP} 100%); border-radius:14px; display:flex; align-items:center; gap:14px; box-shadow:0 4px 16px rgba(122,185,41,.3); } .xll-mine-avatar { width:56px; height:56px; border-radius:50%; background:rgba(255,255,255,.25); border:2px solid rgba(255,255,255,.6); display:flex; align-items:center; justify-content:center; color:#fff; font-size:22px; font-weight:800; flex-shrink:0; } @@ -2050,6 +2256,19 @@ const dvIsPhotoItemRequired = (item) => DV_PHOTO_REQUIRED_CATEGORIES.has(item.ca /** 原型:交车照片演示图(按项固定 seed,避免加载失败) */ const dvGetPhotoDemoUrl = (photoKey) => `https://picsum.photos/seed/oneos-dv-${encodeURIComponent(photoKey)}/480/480`; +const DV_PHOTO_UPLOAD_ACCEPT = 'image/jpeg,image/jpg,image/png,image/gif,image/webp'; + +const dvReadImageFile = (file) => new Promise((resolve, reject) => { + if (!file || !String(file.type || '').startsWith('image/')) { + reject(new Error('not image')); + return; + } + const reader = new FileReader(); + reader.onload = () => resolve(String(reader.result || '')); + reader.onerror = () => reject(new Error('read failed')); + reader.readAsDataURL(file); +}); + const dvSimulatePhotoUpload = (photoKey, cameraDraft, formDraft, row) => { const loc = dvResolveDeliveryLocation(formDraft?.plateNo, row, formDraft?.deliveryLocation); const watermarkTime = dvFormatOpsSignTime(); @@ -2281,9 +2500,19 @@ const dvMetricInputChange = (raw) => { return null; }; +/** 原型:合同被授权人候选(客户方短信签章);按订单挂载,允许为空 */ +const DV_AUTHORIZED_PERSONS_SAMPLE = [ + { id: 'ap1', name: '李晓明', phone: '13800138001' }, + { id: 'ap2', name: '王芳', phone: '13900139002' }, + { id: 'ap3', name: '赵强', phone: '13700137003' }, + { id: 'ap4', name: '陈静', phone: '13600136004' }, +]; + const DV_MOCK_ORDERS = [ { id: 'o1', expectedDate: '2025-02-28 至 2025-03-05', contractCode: 'LNZLHT 20260104001', projectName: '桐乡韵达租赁4.5T*10', customerName: '桐乡市丰韵快递有限责任公司', businessDept: '业务二部', businessOwner: '刘念念', taskSource: '替换车', bizType: '租赁', deliveryRegion: '浙江省-嘉兴市', deliveryAddress: '平湖指定停车场', createTime: '2026-06-04 11:28', createBy: '赵小峰', + /** 演示:关联合同无被授权人 */ + authorizedPersons: [], vehicleList: [ { vehicleKey: 1, seq: 1, vehicleType: '4.5吨冷链车', brand: '现代', model: '帕力安牌4.5吨冷链车', vin: 'LNBSCPKB8RR123401', replaceOldPlate: '浙A88601F', plateNo: '', deliveryTime: '', deliveryPerson: '', deliveryStatus: '未开始', deliveryMileage: null, deliveryH2: null, deliveryH2Unit: '%', deliveryElec: null, hasAd: '', hasTailgate: '', spareTire: '', driverTraining: '' }, { vehicleKey: 2, seq: 2, vehicleType: '4.5吨冷链车', brand: '现代', model: '帕力安牌4.5吨冷链车', vin: 'LNBSCPKB8RR123402', replaceOldPlate: '浙A88602F', plateNo: '浙F88601', deliveryTime: '2026-06-03 14:20', deliveryPerson: '张明辉', deliveryStatus: '待客户签章', deliveryMileage: 12500, deliveryH2: 18, deliveryH2Unit: '%', deliveryElec: 76, serviceFee: 200, hasAd: '无', hasTailgate: '有', spareTire: '有', spareTirePhotoUploaded: true, spareTirePhotoUrl: DV_SPARE_TIRE_DEMO_PHOTO, spareTireTreadDepth: '5.2', driverTraining: '已完成', authorizedPersonId: 'ap1', authorizedPersonName: '李晓明', authorizedPersonPhone: '13800138001', signSent: true }, @@ -2291,6 +2520,7 @@ const DV_MOCK_ORDERS = [ }, { id: 'o4', expectedDate: '2024-11-15', contractCode: 'LNZLHT2024111401', projectName: '聚德11月新增苏龙18T*2', customerName: '沈阳聚德物流有限公司', businessDept: '业务三部', businessOwner: '金可鹏', taskSource: '交车任务', bizType: '租赁', deliveryRegion: '浙江省-嘉兴市', deliveryAddress: '嘉兴港区氢能停车场', createTime: '2024-11-15 15:05', createBy: '何苗苗', + authorizedPersons: DV_AUTHORIZED_PERSONS_SAMPLE, vehicleList: [ { vehicleKey: 1, seq: 1, vehicleType: '18吨双飞翼货车', brand: '苏龙', model: '海格牌18吨双飞翼货车', vin: 'LKLG7C4E4NA774701', plateNo: '浙F80088', deliveryTime: '2026-06-02 16:00', deliveryPerson: '魏山', deliveryStatus: '待客户签章', deliveryMileage: 46200, deliveryH2: 21, deliveryH2Unit: '%', deliveryElec: 80, hasAd: '无', hasTailgate: '有', spareTire: '有', spareTirePhotoUploaded: true, spareTirePhotoUrl: DV_SPARE_TIRE_DEMO_PHOTO, spareTireTreadDepth: '4.8', driverTraining: '已完成', authorizedPersonId: 'ap2', authorizedPersonName: '王芳', authorizedPersonPhone: '13900139002', signSent: true }, { vehicleKey: 2, seq: 2, vehicleType: '18吨双飞翼货车', brand: '苏龙', model: '海格牌18吨双飞翼货车', vin: 'LKLG7C4E4NA774702', plateNo: '沪A03802F', deliveryTime: '2026-06-01 10:15', deliveryPerson: '魏山', deliveryStatus: '待重新签章', deliveryMileage: 38800, deliveryH2: 24, deliveryH2Unit: '%', deliveryElec: 72, hasAd: '无', hasTailgate: '有', spareTire: '有', spareTirePhotoUploaded: true, spareTirePhotoUrl: DV_SPARE_TIRE_DEMO_PHOTO, spareTireTreadDepth: '5.0', driverTraining: '已完成', authorizedPersonId: '', authorizedPersonName: '', authorizedPersonPhone: '', signSent: false }, @@ -2298,6 +2528,7 @@ const DV_MOCK_ORDERS = [ }, { id: 'o5', expectedDate: '2025-02-15', contractCode: 'HT-ZL-2024-001', projectName: '嘉兴氢能示范项目', customerName: '嘉兴某某物流有限公司', businessDept: '业务一部', businessOwner: '张经理', taskSource: '交车任务', bizType: '租赁', deliveryRegion: '浙江省-嘉兴市', deliveryAddress: '南湖科技大道停车场', createTime: '2025-02-10 09:00', createBy: '系统', + authorizedPersons: DV_AUTHORIZED_PERSONS_SAMPLE.slice(0, 2), vehicleList: [ { vehicleKey: 1, seq: 1, vehicleType: '厢式货车', brand: '东风', model: 'DFH1180', vin: 'LKLG7C4E4NA774759', plateNo: '京A12345', deliveryTime: '2025-02-15 10:30', deliveryPerson: '张三', deliveryStatus: '客户已签章', deliveryMileage: 12580, deliveryH2: 35, deliveryH2Unit: 'MPa', deliveryElec: 45, serviceFee: 150, hasAd: '有', hasTailgate: '有', spareTire: '有', driverTraining: '已完成', customerSignTime: '2025-02-15 11:45', vehicleReturned: true, vehicleReturnTime: '2025-03-01 16:30' }, { vehicleKey: 2, seq: 2, vehicleType: '厢式货车', brand: '福田', model: 'BJ1180', vin: 'LKLG7C4E4NA774760', plateNo: '京C11111', deliveryTime: '2025-02-15 14:00', deliveryPerson: '李四', deliveryStatus: '客户已签章', deliveryMileage: 13200, deliveryH2: 68, deliveryH2Unit: '%', deliveryElec: 38, hasAd: '无', hasTailgate: '无', spareTire: '有', driverTraining: '已完成', customerSignTime: '2025-02-15 16:20', vehicleReturned: false }, @@ -2305,15 +2536,20 @@ const DV_MOCK_ORDERS = [ }, ]; -/** 原型:交车被授权人(客户方短信签章) */ -const DV_AUTHORIZED_PERSONS = [ - { id: 'ap1', name: '李晓明', phone: '13800138001' }, - { id: 'ap2', name: '王芳', phone: '13900139002' }, - { id: 'ap3', name: '赵强', phone: '13700137003' }, - { id: 'ap4', name: '陈静', phone: '13600136004' }, -]; - -const dvFindAuthorizedPerson = (id) => DV_AUTHORIZED_PERSONS.find((p) => p.id === id) || null; +/** 取交车任务关联合同的被授权人列表(原型本地种子;正式环境来自租赁合同) */ +const dvGetAuthorizedPersons = (row) => { + if (Array.isArray(row?.authorizedPersons)) return row.authorizedPersons; + return DV_AUTHORIZED_PERSONS_SAMPLE; +}; +const dvFindAuthorizedPerson = (id, persons) => { + const list = Array.isArray(persons) ? persons : DV_AUTHORIZED_PERSONS_SAMPLE; + return list.find((p) => p.id === id) || null; +}; +/** 合同无被授权人时的提示;业管姓名取订单 businessOwner(合同业务负责人) */ +const dvFormatEmptyAuthorizedHint = (businessOwner) => { + const name = String(businessOwner || '').trim() || '业管'; + return `无授权人,请联系${name}补充授权人信息`; +}; const dvPersonInitial = (name) => { const s = String(name || '').trim(); return s ? s.slice(-1) : '?'; @@ -2398,6 +2634,7 @@ const dvFlattenOrders = (orders) => { customerName: order.customerName, businessDept: order.businessDept, businessOwner: order.businessOwner, + authorizedPersons: Array.isArray(order.authorizedPersons) ? order.authorizedPersons : DV_AUTHORIZED_PERSONS_SAMPLE, taskSource: order.taskSource, bizType: order.bizType, deliveryRegion: order.deliveryRegion, @@ -4512,6 +4749,9 @@ const ApprovalCenterModule = ({ onRegisterBack, onPrdKeyChange }) => { const statusLabel = acTaskStatusLabel(task); const cardTitle = acCardBoldTitle(task); const pickupActualAmount = isPickup ? acPickupListActualAmount(task) : ''; + const isCcRecord = mainTab === 'cc'; + const showApproveBtn = mainTab === 'todo' && acIsPending(task.status); + const showViewBtn = isCcRecord; return (
+ 无授权人,请联系{ownerName}补充授权人信息 +
+ ); + } + return ( + <>+ 无授权人,请联系{ownerName}补充授权人信息 +
+ ); + } return ( <>.spec/authorized-person.md。地图聚合点、列表条目、统计卡片均可无断点下钻至单个资源详情页。