feat(miniapp): 完善小羚羚审批中心与各流程 PRD 说明

新增审批中心及提车/还车/租赁账单/调拨/替换车等流程需求文档,优化底部抽屉确认样式,并精简还车应结款审批页车辆租金展示。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
王冕
2026-06-10 14:05:43 +08:00
parent 34fd6fcdac
commit 6baff9ab7f

View File

@@ -634,6 +634,9 @@ const PAGE_STYLE = `
.tc-mini-sheet-title { font-size:16px; font-weight:700; color:${COLOR_TEXT}; }
.tc-mini-sheet-close { width:32px; height:32px; border:none; background:${COLOR_PAGE}; border-radius:999px; font-size:20px; line-height:1; color:${COLOR_MUTED}; cursor:pointer; flex-shrink:0; }
.tc-mini-sheet-body { flex:1; min-height:0; overflow-y:auto; -webkit-overflow-scrolling:touch; padding:4px 20px calc(16px + env(safe-area-inset-bottom,0px)); }
.tc-mini-sheet-foot { flex-shrink:0; padding:10px 16px calc(10px + env(safe-area-inset-bottom,0px)); border-top:1px solid ${COLOR_LINE}; }
.tc-mini-sheet-ok { width:100%; min-height:44px; border:none; border-radius:10px; font-size:15px; font-weight:600; cursor:pointer; background:${XLL_GREEN_DEEP}; color:#fff; touch-action:manipulation; }
.tc-mini-sheet-ok:active { opacity:.88; }
@keyframes tc-sheet-up { from { transform:translateY(100%); } to { transform:translateY(0); } }
@media (prefers-reduced-motion: reduce) {
.tc-mini-sheet-panel { animation:none; }
@@ -1785,14 +1788,6 @@ const ReturnSettlementApprovePage = ({ task, mode, onBack }) => {
<HcSettleGroupCard title="客户服务组" submitter="张三 · 已提交" settleTotal={customerServiceTotal}>
<HcFeeItemTable rows={businessServiceRows} />
<div className="hc-sub-block">
<div style={{ fontSize: 13, fontWeight: 600, marginBottom: 8, color: COLOR_TEXT }}>车辆租金</div>
<div className="tc-kv-grid" style={{ padding: 0 }}>
<TcInfoRow label="本期已收租金" value={formatYuan(billInfo.receivedRent)} />
<TcInfoRow label="车辆实际租金" value={formatYuan(billInfo.actualRent)} />
<TcInfoRow label="车辆应退租金" value={formatYuan(billInfo.shouldRefundRent)} />
</div>
</div>
</HcSettleGroupCard>
<HcSettleGroupCard title="能源采购组" submitter="李四 · 已提交" settleTotal={energySettleTotal}>
@@ -2454,6 +2449,16 @@ const VrReplaceVehicleCard = ({ pair, index }) => (
</div>
);
const getAuditPrdKey = (task) => {
if (!task) return 'audit';
if (task.flowType === '提车应收款') return 'audit-pickup';
if (task.flowType === '还车应结款') return 'audit-return';
if (task.flowType === '租赁账单') return 'audit-lease-bill';
if (task.flowType === '替换车申请') return 'audit-replace';
if (task.flowType === '车辆调拨') return task.transferStage === 'ops' ? 'audit-transfer-ops' : 'audit-transfer-create';
return 'audit';
};
const ReplaceVehicleApprovePage = ({ task, mode, onBack }) => {
const detail = useMemo(() => buildReplaceVehicleDetail(task), [task]);
const { pairs, projectInfo, approvalSteps } = detail;
@@ -2519,13 +2524,18 @@ const ReplaceVehicleApprovePage = ({ task, mode, onBack }) => {
);
};
const ApprovalCenterModule = ({ onRegisterBack }) => {
const ApprovalCenterModule = ({ onRegisterBack, onPrdKeyChange }) => {
const [mainTab, setMainTab] = useState('todo');
const [flowFilter, setFlowFilter] = useState('');
const [searchKey, setSearchKey] = useState('');
const [filterDrawerOpen, setFilterDrawerOpen] = useState(false);
const [detailTask, setDetailTask] = useState(null);
useEffect(() => {
onPrdKeyChange?.(getAuditPrdKey(detailTask));
return () => onPrdKeyChange?.(null);
}, [detailTask, onPrdKeyChange]);
const tabCounts = useMemo(() => {
const counts = { initiated: 0, todo: 0, done: 0, cc: 0 };
AC_MOCK_TASKS.forEach((t) => {
@@ -4927,6 +4937,199 @@ const PRD_DOCS = {
</div>
),
},
audit: {
title: '审批中心 · 产品需求说明',
body: (
<div className="xll-prd-doc">
<span className="xll-prd-tag">审批管理 / 审批中心</span>
<div className="xll-prd-meta">版本 V1.0 · 对接 ONE-OS 审批中心 · 适用有审批权限的业务/财务/管理人员</div>
<div className="xll-prd-h2">目标</div>
<p className="xll-prd-p">在小程序内聚合我发起的 / 我的待办 / 我的已办 / 抄送我的四类审批任务支持按流程类型筛选与搜索点击进入对应流程的办理或查看页</p>
<div className="xll-prd-h2">列表 Tab</div>
<ul className="xll-prd-ul">
<li className="xll-prd-li"><strong>我发起的</strong>当前用户作为发起人的流程</li>
<li className="xll-prd-li"><strong>我的待办</strong>待当前用户审批卡片展示当前节点与处理人</li>
<li className="xll-prd-li"><strong>我的已办</strong>当前用户已处理过的历史记录</li>
<li className="xll-prd-li"><strong>抄送我的</strong>仅知会无需操作的任务</li>
</ul>
<div className="xll-prd-h2">列表卡片字段</div>
<ul className="xll-prd-ul">
<li className="xll-prd-li">流程类型色条 + 类型名称合同审批提车应收款租赁账单还车应结款车辆调拨替换车申请等</li>
<li className="xll-prd-li">单据号摘要发起人到达/完成时间</li>
<li className="xll-prd-li">客户类流程额外展示客户名称项目名称还车展示应退/应补金额</li>
<li className="xll-prd-li">状态 Tag审批中 / 已通过 / 已驳回 / 已终止</li>
</ul>
<div className="xll-prd-h2">筛选与搜索</div>
<ul className="xll-prd-ul">
<li className="xll-prd-li">快捷 Chip全部合同审批提车应收款租赁账单车辆调拨更多类型抽屉展示全部流程类型</li>
<li className="xll-prd-li">搜索范围单据号摘要流程类型发起人客户名项目名车牌号</li>
</ul>
<div className="xll-prd-h2">跳转规则</div>
<ul className="xll-prd-ul">
<li className="xll-prd-li">已接入详情页提车应收款还车应结款租赁账单车辆调拨申请/运维替换车申请</li>
<li className="xll-prd-li">待办 Tab 进入办理态底部展示通过/驳回/终止/评论其余 Tab 为只读查看态</li>
<li className="xll-prd-li">左上返回详情页先退回列表再退回业务入口</li>
</ul>
</div>
),
},
'audit-pickup': {
title: '提车应收款审批 · 产品需求说明',
body: (
<div className="xll-prd-doc">
<span className="xll-prd-tag">审批中心 / 提车应收款</span>
<div className="xll-prd-meta">版本 V1.0 · 数据源自提车应收款业务单 · 审批节点业管 财务</div>
<div className="xll-prd-h2">页面目标</div>
<p className="xll-prd-p">审批人核对本次提车实收金额是否与合同约定一致确认车辆明细氢费预付款如有及开票信息后方可通过</p>
<div className="xll-prd-h2">Hero </div>
<ul className="xll-prd-ul">
<li className="xll-prd-li">主金额<strong>实收款总额</strong>大字号橙色主题</li>
<li className="xll-prd-li">副信息客户名称合同编码项目名称提车台数</li>
<li className="xll-prd-li">对比行应收款总额较应收减免差额</li>
<li className="xll-prd-li">实收明细底部抽屉分项展示月租金保证金服务费减免及氢费预收如有</li>
</ul>
<div className="xll-prd-h2">正文区块</div>
<ul className="xll-prd-ul">
<li className="xll-prd-li"><strong>车辆明细</strong>逐车展示品牌型号车牌应收/实收租金保证金服务费及减免凭证</li>
<li className="xll-prd-li"><strong>氢费预付款</strong>首期有应收实收减免金额与备注</li>
<li className="xll-prd-li"><strong>开票信息</strong>开票方式开票备注</li>
<li className="xll-prd-li"><strong>审批情况</strong>时间轴展示各部门审批状态审批人时间</li>
</ul>
<div className="xll-prd-h2">底部操作待办态</div>
<ul className="xll-prd-ul">
<li className="xll-prd-li">评论填写意见不改变流程状态</li>
<li className="xll-prd-li">终止 / 驳回需填写原因流程结束并通知发起人</li>
<li className="xll-prd-li">通过确认后流转下一节点末节点通过后单据完结</li>
</ul>
</div>
),
},
'audit-return': {
title: '还车应结款审批 · 产品需求说明',
body: (
<div className="xll-prd-doc">
<span className="xll-prd-tag">审批中心 / 还车应结款</span>
<div className="xll-prd-meta">版本 V1.0 · 数据源自还车结算单 · 多部门分组提交后汇总审批</div>
<div className="xll-prd-h2">页面目标</div>
<p className="xll-prd-p">还车完成后客户服务组能源采购组运维部安全部分别填报费用审批人核对分组明细与汇总金额判定保证金抵扣后应退或应补</p>
<div className="xll-prd-highlight">当保证金 待结算总额时Hero 展示应退还总额否则展示应补缴总额</div>
<div className="xll-prd-h2">Hero 紫色主题</div>
<ul className="xll-prd-ul">
<li className="xll-prd-li">主金额应退还总额 / 应补缴总额</li>
<li className="xll-prd-li">车辆信息车牌合同编码项目名称三项保险状态易损/轮胎/养护</li>
<li className="xll-prd-li">租期交车时间还车时间</li>
<li className="xll-prd-li">摘要保证金待结算总额车辆实际租金结算明细抽屉展示分项汇总</li>
</ul>
<div className="xll-prd-h2">分组结算卡片</div>
<ul className="xll-prd-ul">
<li className="xll-prd-li"><strong>客户服务组</strong>固定费项表格违章违约金保险上浮ETC 费用停车费等底部应结算总额</li>
<li className="xll-prd-li"><strong>能源采购组</strong>氢量差补缴/还车氢量退还单价预付款退费</li>
<li className="xll-prd-li"><strong>运维部</strong>清洗保养维修车损工具/证件/广告丢失送接车服务轮胎磨损等费项</li>
<li className="xll-prd-li"><strong>安全部</strong>违章次数已缴/未缴金额统计</li>
</ul>
<div className="xll-prd-h2">审批与操作</div>
<p className="xll-prd-p">审批时间轴 + 底部评论/终止/驳回/通过规则同提车应收款查看态隐藏操作栏仅浏览</p>
</div>
),
},
'audit-lease-bill': {
title: '租赁账单审批 · 产品需求说明',
body: (
<div className="xll-prd-doc">
<span className="xll-prd-tag">审批中心 / 租赁账单</span>
<div className="xll-prd-meta">版本 V1.0 · 数据源自月度租赁账单 · 审批节点业务服务组 业管主管 财务</div>
<div className="xll-prd-h2">页面目标</div>
<p className="xll-prd-p">审批人核对本期账单实收金额逐车租金与服务费减免是否合理首期账单需额外核对氢费预付款</p>
<div className="xll-prd-h2">Hero 蓝色主题</div>
<ul className="xll-prd-ul">
<li className="xll-prd-li">主金额实收款总额</li>
<li className="xll-prd-li">客户合同编码项目名称</li>
<li className="xll-prd-li">账单周期起止日期期数 Tag N </li>
<li className="xll-prd-li">应收款总额开票总额应收明细实收明细两个底部抽屉</li>
</ul>
<div className="xll-prd-h2">正文区块</div>
<ul className="xll-prd-ul">
<li className="xll-prd-li"><strong>车辆列表</strong>逐车展示应收/实收租金减免金额与凭证服务费明细</li>
<li className="xll-prd-li"><strong>氢费预付款</strong>仅第 1 期展示应收实收减免及备注</li>
<li className="xll-prd-li"><strong>审批情况</strong>各部门节点时间轴</li>
</ul>
<div className="xll-prd-h2">金额计算口径开发</div>
<ul className="xll-prd-ul">
<li className="xll-prd-li">应收总额 = 月租金 + 保证金首期+ 服务费 + 氢费预收首期</li>
<li className="xll-prd-li">实收总额 = 实收租金 + 保证金 + 实收服务费 租金减免 服务费减免 + 氢费实收 氢费减免</li>
<li className="xll-prd-li">开票总额不含保证金</li>
</ul>
</div>
),
},
'audit-transfer-create': {
title: '车辆调拨审批(申请方) · 产品需求说明',
body: (
<div className="xll-prd-doc">
<span className="xll-prd-tag">审批中心 / 车辆调拨 · 申请方</span>
<div className="xll-prd-meta">版本 V1.0 · transferStage=create · 区域间车辆调配申请</div>
<div className="xll-prd-h2">页面目标</div>
<p className="xll-prd-p">发起区域申请将车辆调拨至目标区域审批人核对路线运输方式车辆清单及出发时车况数据</p>
<div className="xll-prd-h2">Hero 绿色主题</div>
<ul className="xll-prd-ul">
<li className="xll-prd-li">调拨车辆台数出发区域 接收区域路线展示</li>
<li className="xll-prd-li">调拨日期Tag 标识调拨申请</li>
</ul>
<div className="xll-prd-h2">正文区块</div>
<ul className="xll-prd-ul">
<li className="xll-prd-li"><strong>调拨情况</strong>日期出发/接收区域调拨原因</li>
<li className="xll-prd-li"><strong>运输信息</strong>运输方式司机运输/板车运输等承运人预计到达</li>
<li className="xll-prd-li"><strong>车辆清单</strong>逐车车牌品牌型号出发里程/氢量/电量</li>
<li className="xll-prd-li"><strong>审批情况</strong>节点时间轴</li>
</ul>
<div className="xll-prd-h2">与运维方审批的差异</div>
<p className="xll-prd-p">申请方侧重为何调拨调哪些车运维方ops侧重接收区域验收与到达车况Hero Tag 运维调拨方</p>
</div>
),
},
'audit-transfer-ops': {
title: '车辆调拨审批(运维方) · 产品需求说明',
body: (
<div className="xll-prd-doc">
<span className="xll-prd-tag">审批中心 / 车辆调拨 · 运维方</span>
<div className="xll-prd-meta">版本 V1.0 · transferStage=ops · 接收区域运维确认调拨到达</div>
<div className="xll-prd-h2">页面目标</div>
<p className="xll-prd-p">接收区域运维负责人在车辆到达后审批确认核对运输过程信息与车辆到达时里程氢量电量是否与申请一致</p>
<div className="xll-prd-h2">页面结构</div>
<p className="xll-prd-p">布局与申请方审批页一致Hero Tag 改为运维调拨方车辆卡片除出发数据外需展示到达里程/氢量/电量如有</p>
<div className="xll-prd-h2">审批要点</div>
<ul className="xll-prd-ul">
<li className="xll-prd-li">确认车辆已全部到达接收区域</li>
<li className="xll-prd-li">核对车况数据异常是否已在备注中说明</li>
<li className="xll-prd-li">通过后更新车辆归属区域流程完结</li>
</ul>
</div>
),
},
'audit-replace': {
title: '替换车申请审批 · 产品需求说明',
body: (
<div className="xll-prd-doc">
<span className="xll-prd-tag">审批中心 / 替换车申请</span>
<div className="xll-prd-meta">版本 V1.0 · 数据源自替换车业务单 · 审批节点业务主管 事业部主管 运维主管</div>
<div className="xll-prd-h2">页面目标</div>
<p className="xll-prd-p">客户用车期间需更换车辆时审批人核对替换类型/替换车辆信息及替换原因客户原因替换需确认费用</p>
<div className="xll-prd-h2">Hero 玫红主题</div>
<ul className="xll-prd-ul">
<li className="xll-prd-li">替换车辆台数客户名称</li>
<li className="xll-prd-li">合同编码项目名称交车区域项目类型 Tag</li>
</ul>
<div className="xll-prd-h2">替换对卡片</div>
<ul className="xll-prd-ul">
<li className="xll-prd-li">左右对照被替换车原车牌/品牌型号 替换为新车牌/品牌型号</li>
<li className="xll-prd-li">替换类型 Tag永久替换 / 临时替换</li>
<li className="xll-prd-li">替换原因原因说明原因为客户原因时展示替换费用</li>
</ul>
<div className="xll-prd-h2">审批与操作</div>
<p className="xll-prd-p">支持多组替换对逐条展示底部操作栏规则与其他审批页一致通过后由运维安排交/还车衔接</p>
</div>
),
},
};
const IphoneStatusBar = () => (
@@ -4950,21 +5153,22 @@ const MpCapsule = () => (
const PrdModal = ({ prdKey, onClose }) => {
const doc = PRD_DOCS[prdKey];
if (!doc || !Modal) return null;
if (!doc || !prdKey) return null;
return (
<Modal
open={!!prdKey}
title={doc.title}
onCancel={onClose}
centered
width={420}
footer={Button ? [
<Button key="ok" type="primary" onClick={onClose} style={{ background: XLL_GREEN_DEEP, borderColor: XLL_GREEN_DEEP }}>知道了</Button>,
] : null}
styles={{ body: { maxHeight: '72vh', overflowY: 'auto', paddingTop: 8 } }}
>
{doc.body}
</Modal>
<div className="tc-mini-sheet" role="dialog" aria-modal="true" aria-label={doc.title}>
<button type="button" className="tc-mini-sheet-mask" onClick={onClose} aria-label="关闭" />
<div className="tc-mini-sheet-panel">
<div className="tc-mini-sheet-handle" aria-hidden="true" />
<div className="tc-mini-sheet-head">
<span className="tc-mini-sheet-title">{doc.title}</span>
<button type="button" className="tc-mini-sheet-close" onClick={onClose} aria-label="关闭">×</button>
</div>
<div className="tc-mini-sheet-body">{doc.body}</div>
<div className="tc-mini-sheet-foot">
<button type="button" className="tc-mini-sheet-ok" onClick={onClose}>知道了</button>
</div>
</div>
</div>
);
};
@@ -5157,6 +5361,11 @@ const Component = function XiaoLingLingMiniApp() {
const [todoDetail, setTodoDetail] = useState(null);
const [bizModule, setBizModule] = useState(null);
const [prdKey, setPrdKey] = useState(null);
const [auditPrdKey, setAuditPrdKey] = useState(null);
const handleAuditPrdKeyChange = useCallback((key) => {
setAuditPrdKey(key || null);
}, []);
const navTitle = useMemo(() => {
if (!loggedIn) return '登录';
@@ -5174,14 +5383,15 @@ const Component = function XiaoLingLingMiniApp() {
if (!loggedIn) return 'login';
if (bizModule === 'delivery') return 'delivery';
if (bizModule === 'replace') return 'replace';
if (bizModule === 'audit' || bizModule === 'inspection' || bizModule === 'vehicle') return 'business';
if (bizModule === 'audit') return auditPrdKey || 'audit';
if (bizModule === 'inspection' || bizModule === 'vehicle') return 'business';
if (todoDetail) return 'todo-detail';
if (mainTab === 'todo') return 'todo';
if (mainTab === 'business') return 'business';
if (mainTab === 'map') return 'map';
if (mainTab === 'mine') return 'mine';
return 'todo';
}, [loggedIn, mainTab, todoDetail, bizModule]);
}, [loggedIn, mainTab, todoDetail, bizModule, auditPrdKey]);
const handleOpenPrd = useCallback((key) => setPrdKey(key || currentPrdKey), [currentPrdKey]);
@@ -5296,7 +5506,7 @@ const Component = function XiaoLingLingMiniApp() {
<LoginPage onLogin={() => { setLoggedIn(true); message.success('登录成功'); }} onOpenPrd={handleOpenPrd} />
) : bizModule ? (
bizModule === 'audit' ? (
<ApprovalCenterModule onRegisterBack={registerModuleBack} />
<ApprovalCenterModule onRegisterBack={registerModuleBack} onPrdKeyChange={handleAuditPrdKeyChange} />
) : bizModule === 'inspection' ? (
<AnnualReviewModule onRegisterBack={registerModuleBack} />
) : bizModule === 'vehicle' ? (