配货单(手机端)前端实现清单
写代码时的备忘。本地文档、不进 git。 配货单 = 销售单子类型(后端 sub_type=1104);前端体验与销售单一致,差异全由后端兜。 分支:在
feature/26.1.5上做(已含配货入库代码,和配货单一起上线、一个 MR)。 复用 vs 专用 决策依据:大且无分歧 → 复用(编辑器 4514 行、配货单行为相同);小且有分歧 → 写专用(详情 cell,配货单不需要活动逻辑)。
一、后端契约(已读代码确认,bizsspd feature/chain)
| 事项 | 结论 | 代码出处 |
|---|---|---|
| 单据类型判定 | 前端不传类型;后端按所选客户 relShopId 自动判定:relShopId>0(铺货客户)→ franShopId→subType=1104;否则普通单。覆盖前端传值 |
SalesBillCombService.initAgentBill / resolveFranShopIdByCust(D15) |
| 提交接口 | 配货单走销售单同一个 saveSaleBill(非订单分支),后端 save 时自动转 |
SaleEditorStore.save();后端 initAgentBill 在 save 流程内 |
| 列表过滤 | 传 subType=1104 只返/只汇总配货单;不传默认 1112 |
SspdSalesBillAction.findBillWithPayWay:280 putIfAbsent SALES_BILL_SSHOP |
| 铺货客户标记 | customer.typeId === 101(=FRAN_AGENT_TYPE_ID)。必修11 同步时按 rel_shop_id>0 CASE 改写下发,库里不动;需全量同步(清缓存)才有,上线会跑全量 |
必修11 / D28 commit 904f3754a7 |
| 券/活动/积分 | 后端隔离,前端零分支:积分 produceMoney=0、券/活动 agent 过滤 |
BaseSalesBillScoreAdapter:352-365;GrantCouponService / SalesBillApplyService agent 守卫 |
| 详情字段 | dresBranchSpu 无条件填 {special}(tenantSpu=null 才缺);actId 返回但空;discount 正常返回 |
getBillFull / detailFillDresBranchSpu |
二、看单(查看配货单)
| # | 文件 | 改动 | 性质 |
|---|---|---|---|
| 1 | src/model/bill/BillType.js(BillSubType ~73-116) |
加 AGENT:'agent';常量 SUB_TYPE_AGENT=1104、FRAN_AGENT_TYPE_ID=101 |
🟢新增 |
| 2 | src/module/bill/history/store/BillHistoryCoreStore.js(titleItems SALE 分支 ~180) |
push 配货单 tab,gate:购买笑铺后台 && PermissionSvc.hasPermission(Features.agentBill)(写法仿 SALE_ORDER/SALE_DETAILS) |
🟢新增 |
| 3 | src/module/bill/history/store/SaleBillHistoryStore.js(loadBill jsonParam :80-84) |
...(billSubType===AGENT ? { subType: SUB_TYPE_AGENT } : {}) |
🟢复用接口+条件 |
| 4 | src/module/bill/history/component/BillHistoryScreen.js(render ~650) |
加 key===AGENT 分支 → <BillHistoryFragment billSubType={AGENT} .../>(props 仿 PURCHASE) |
♻️复用 |
| 5 | 筛选面板 | 配货单 tab 隐藏"单据类型"筛选项(实现时定位筛选组件) | 🟢条件隐藏 |
| 6 | 详情外壳复用 SaleBillDetailsV2Screen;renderItem(:1506) 按 subType 分发 cell |
SaleBillDetailsV2Screen.tsx |
♻️复用+分支 |
| 6a | 新建配货单 cell DistributionSKUCell/SPUCell |
saleBillDetails/components/ 新增 |
🟢新增 |
| 6b | 销售单 cell dresBranchSpu?.special(潜在 NPE 修复,独立有效) |
SaleBillDetailsSKUCell.tsx:140 / SPUCell.tsx:138 |
✅已做 |
| — | 搜索框、查询条件、详情外壳 | 完全复用销售单,零改 | ♻️ |
详情 = cell 专用(不复用)+ 外壳复用(owner 指导 + 思考题13 原则:分支加在 renderItem 外壳层、cell 单一职责)。
renderItem分支:isAgent = store.billItem?.main?.subType === SUB_TYPE_AGENT;isAgent ? <Distribution{mode}Cell/> : <SaleBillDetails{mode}Cell/>(mode=SKU/SPU 维度不变,只在外层加 isAgent 分支)。- 配货单 cell = 干净精简新写,不是复制销售单 cell。 只渲染:商品图、款名、款号、颜色尺码、
数量×单价×折扣%=小计(+ 点击看详情弹窗)。显示折扣%(配货单有折扣)。不含活动角标(BadgeView)、不耦合 rootStore。预计 ~150 行/个。- 为什么专用而非复用/复制:销售单 cell 质量中下(352/510 行塞了 Modal+Badge+Store+styles、强耦合 rootStore、活动 8 分支 if-chain),配货单用不到那套 → 写干净专用的,比复用或复制都好。
?.(6b,已做)保留:纯修销售单自身潜在 NPE;配货单走专用 cell 与它无关,对销售单零影响。- 健壮性:专用 cell 物理上无活动角标,不赌后端 actId 返空;13 个详情调用方 subType≠1104 → 走老 cell,零影响。
三、录单(创建配货单)= 销售单编辑器 + 仅 2 处不同
| # | 文件 | 改动 | 性质 |
|---|---|---|---|
| 7 | BillHistoryScreen.js("+" :359-396) |
加 key===AGENT 分支 → navigate('SaleBillEditor', { billMode:'agent' })(权限同 gate) |
🟢新增分支 |
| 8 | saleBillV2/hooks/useSaleBillStarter.ts + SaleEditorStore |
读 route.params.billMode → store.billMode(新增字段,默认 'sale') |
🟢新增字段 |
| 9 | saleBillV2/views/CustHeader.tsx(:85 navigate CustomerSelection) |
加 custScene: store.billMode==='agent' ? 'agent' : 'normal' |
🟢加参数 |
| 10 | 选客户取数链:CustomerSelectionScreen.js:254 → CustomerSvc.fetchCustomerItems → CustomerDao.getCustomerItems |
透传 custScene,查询层 .andWhere:agent→typeId=101,normal→typeId!=101,未传→不过滤 |
🟢可选参数 |
编辑器其余(商品/数量/价格/收银/储值/提交)100% 复用销售单,零分支。提交走
saveSaleBill,后端自动转 1104。⚠️ 编辑器质量警告 + 复用纪律:
SaleEditorStore.js是 4514 行 god object(isOrder散落 23 处、rootStore耦合 100 处、39 @computed)。配货单行为和销售单完全一样(差异全后端兜)→ 重写不现实,必须复用。但触碰降到最小:
billMode字段只用在 CustHeader 一处(决定选客户过滤),绝不进那 23 个isOrder分支——别让它变成第二个 isOrder;- 对 god object 的改动只有:+1 个
billMode@observable(加法)、useSaleBillStarter 读一次、CustHeader 读一次。不碰任何现有业务逻辑。
四、双向选客户过滤(核心,详)
控制流:入口 billMode → CustHeader 传 custScene → 查询层按 typeId 过滤。
销售单(含新增/编辑)custScene='normal' → WHERE customer.typeId != 101 // 排除铺货,防误转配货单
配货单 custScene='agent' → WHERE customer.typeId == 101 // 只铺货客户
其它调用方(全局搜索/AutoComplete) 不传 → 不过滤,行为不变
- 为什么放查询层不放客户端:配货单只要铺货客户(少),客户端过滤分页会很稀疏/翻很多页;查询层
WHERE直接返铺货客户,分页正确高效。 - 为什么安全:可选参数、默认不过滤 → 老调用方(GlobalSearch / AutoComplete)不传就零影响。≠ 无条件改公共函数。
- 注意:销售单排除铺货客户是对现有行为的有意改动(必要,防 D15 静默转配货单)。
- 待确认:客户实体属性名是否
typeId(mapper 读item.typeId,应是它)。
五、显示条件 & 待后端/产品的 key(⏳ 阻塞 tab 显示,不阻塞开发)
| 项 | 状态 | 处理 |
|---|---|---|
| 配货单 tab = 购买笑铺后台 && 配货单菜单权限 | 已定 | — |
Features.agentBill 权限码 |
⏳ 后端还没加菜单权限,等它给码 | 先占位 + TODO;本地自测临时放开权限判断(否则 perm=false,tab 不显示、没法测) |
| "购买笑铺后台"确切判定 key | ⏳ 待确认(候选:commonStore.isSaas / 版本 / businessParam) |
参照 CommonStore.js:1483 productCode / isSaas |
六、开发顺序 & 影响范围
- 重要厘清(V1/V2 不是新旧之争,是竖屏/横屏):
- 竖屏 V1 =
editor/component/screen/SaleBillEditor.js(手机所有"+"开单入口 navigate('SaleBillEditor') 都进它,共 15 处)。 - 横屏 V2 =
pages/saleBillV2/(从landscapeRoute.tsx进、SideTab 侧栏,平板/横屏开单)。 - 配货单对标销售单:两屏都要支持。共享同一 store(billMode 字段两屏通用)+ 共享选客户查询层(CustomerDao)。
- 竖屏 V1 =
- 进度:
- 看单 1-6 + i18n + 6b
?.✅ 完成。 - 录单竖屏 V1 ✅ 完成:入口"+"(billMode='agent')、SaleBillEditor.js 设 billMode、CustomerDao/CustomerSvc/CustomerSelectionScreen 加 custScene 查询层过滤(双向)、SaleBillEditor 选客户传 custScene。typecheck 零报错。
- 录单横屏 V2 ⏳:useSaleBillStarter 设 billMode + CustHeader 传 custScene(易,镜像 V1);横屏的配货单入口待查(横屏是 SideTab 常驻,不走 navigate 传参)。
- 看单 1-6 + i18n + 6b
- 自测:临时放开权限 gate;配货单选客户前需在 app 内"系统设置→清缓存"让 typeId=101 同步下来。
- 影响范围:改的老代码全是加法/带条件(不进配货单 tab、不传 billMode/custScene 时老流程不变);唯一有意改动既有行为 = 销售单选客户排除铺货客户。
七、实测 QA 发现(竖屏 V1,2026-06-03)—— 复用 SALE 的收尾
核心已验证通过:配货单 Tab/列表/选客户过滤/建单/成功页 端到端跑通(#438/439/440 建出)。 以下是「复用销售单基础设施」带出的收尾,分两类。
A 类:写死 === BillSubType.SALE 把 AGENT 挡了(配货单要跟着 SALE 走的地方加 AGENT)
- ✅ 列表点详情
BillHistoryFragment.js:474(已加 AGENT) - ✅ 建单页 title
SaleBillEditor.js:1523(agent→配货单) - ⏳ #5 建单后列表不刷新(需下拉)—— 找建单成功→reload 路径的 SALE 门
- ⏳ 详情内操作(作废/退款/修改后刷新)
SaleBillDetailsV2Screen/Store里的 reloadBillWithBillSubType(SALE) - ⏳ 逐个过
BillHistoryStore.js:246/298/640/707等 ~25 处 SALE 判断(有些是查询过滤/业绩过滤,按需加 AGENT)
B 类:配货单该隐藏的零售 UI(编辑器 + 收款页,按 billMode==='agent' 隐藏)
隐藏:积分(客户积分显示 + 积分抵扣 + 产生积分)、优惠券/券(赠送优惠券 + 用券)、营销活动。 保留(铺货客户适用):余额(显示)、余额结算(用余额付款)、充值、现金/银行账户、抹零、其他费用、折扣%。
待确认/已确认
- ✅ 余额、余额结算、充值 → 保留(铺货客户有余额、可余额结算)
- ✅ 积分、优惠券、活动 → 隐藏