264 lines
14 KiB
Markdown
264 lines
14 KiB
Markdown
|
|
# ShopXO 酷炫前端模板实现方案调研报告
|
|||
|
|
|
|||
|
|
> 调研日期:2026-04-20
|
|||
|
|
> 状态:**Round 3 收敛版本**
|
|||
|
|
> 参与:FrontendDev (Q1/Q4)、BackendArchitect (Q2)、ProductManager (Q3)、FirstPrinciples (Q4 集成)
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Q2 结论:单订单多 SKU 支持
|
|||
|
|
|
|||
|
|
### 核心答案
|
|||
|
|
|
|||
|
|
**ShopXO 订单模型技术上支持同一商品多规格(多 SKU)出现在同一订单中**,但现有 vr_ticket 模板的 `submit()` 只传单行,完整多座位下单需要做两件事:① 让前端传多行 `goods_data`,② 阻止 `OrderSplitService` 按 warehouse 拆单。
|
|||
|
|
|
|||
|
|
### 证据来源
|
|||
|
|
|
|||
|
|
| 文件 | 关键代码 | 说明 |
|
|||
|
|
|------|---------|------|
|
|||
|
|
| `BuyService.php:86` | `foreach($params['goods_data'] as $v)` | 循环处理每个商品项,每项独立 spec_base_id |
|
|||
|
|
| `BuyService.php:423-435` | `extension_data` JSON 序列化 | 每行 item 支持挂载座位/观演人扩展数据 |
|
|||
|
|
| `BuyService.php:101` | `md5(goods_id + spec implode)` | 内部用 goods_id+spec 组合生成唯一行 ID |
|
|||
|
|
| `OrderSplitService.php:52-53` | `GoodsWarehouseAggregate()` | **拆单触发点**:按仓库分组,多 SKU 同一仓库则合并 |
|
|||
|
|
| `OrderSplitService.php:289-310` | 按 spec MD5 找 spec_base_id | spec_base_id 已可不同(座位级 SKU) |
|
|||
|
|
| `ticket_detail.html:413-436` | `goodsParamsList.map()` | Plan A 代码已写好,但 URL 只传第一行(bug) |
|
|||
|
|
|
|||
|
|
### 最小可行方案(Multi-Seat Now)
|
|||
|
|
|
|||
|
|
**改动点仅 3 处,全在 ticket_detail.html,不碰 ShopXO 核心:**
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
ticket_detail.html submit() 函数
|
|||
|
|
|
|||
|
|
BEFORE: location.href = checkoutUrl + '&goods_params=' + encodeURIComponent(goodsParams)
|
|||
|
|
AFTER: goodsParamsList 整体 base64 编码,拆分成多条 goods_data 逐条 POST 到 CartSave,
|
|||
|
|
然后跳转到合并支付流程(ShopXO 购物车天然支持多商品同单)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**为什么走购物车路线更稳:**
|
|||
|
|
- `BuyCart` → `BuyTypeGoodsList` → 直接调用 `BuyGoods`,完美支持多 `goods_data` 行
|
|||
|
|
- 不需要hook `OrderSplitService`,购物车结算路径不触发按仓库拆单(只按商品拆)
|
|||
|
|
- 核销逻辑不受影响:支付成功后 `plugins_service_order_pay_success_handle_end` 钩子正常触发
|
|||
|
|
|
|||
|
|
### 理想方案(Multi-Seat Proper)
|
|||
|
|
|
|||
|
|
在插件中挂载 `plugins_service_buy_group_goods_handle` 钩子,拦截 `OrderSplitService`,将同一 goods_id + 不同 spec_base_id 的多行合并进同一个 order_base:
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
plugins_service_buy_group_goods_handle:
|
|||
|
|
- 按 goods_id 聚合,而非按 warehouse_id
|
|||
|
|
- 每个 goods_id 只生成一条 order_base,goods_items 内嵌多个 spec_base_id 不同的行
|
|||
|
|
- extension_data 按座位索引扁平化存储
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**ShopXO 官方立场**:这是非标准用法,建议走购物车路线。
|
|||
|
|
|
|||
|
|
### 最大风险点
|
|||
|
|
|
|||
|
|
1. **OrderSplitService 按仓库拆单** — 如果场次商品和周边商品挂在不同仓库,多座位票务订单会被拆成多个子单。用户会收到多笔支付通知,体验割裂。→ **最小方案走购物车绕过此风险**。
|
|||
|
|
2. **座位级 SKU 未在 ShopXO 后台创建** — `specBaseIdMap` 依赖数据库中已存在的 `sxo_goods_spec_base` 记录。如果模板生成的 seatKey(如 "A_1")没有对应的 spec_base_id,`submit()` 会降级到 Zone 级别 SKU(同一 zone 全部座位共享一个 spec),失去座位粒度。→ **需要后台管理员先为每个座位创建规格**。
|
|||
|
|
|
|||
|
|
### 优先级与依赖
|
|||
|
|
|
|||
|
|
- **Q2 是 Q4 的前提** — Q4 的"多座位选座流程"依赖 Q2 的多 SKU 订单能力。
|
|||
|
|
- Q2 本身不依赖 Q1,可以独立推进。
|
|||
|
|
- Q3 和 Q4 无依赖,但 Q3 生成的代码需适配 Q4 选型(H5 vs uni-app)。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Q1 结论:ShopXO 自定义模板最佳实践
|
|||
|
|
|
|||
|
|
### 核心答案
|
|||
|
|
|
|||
|
|
**票务详情页不走 DIY 设计器,直接修改 `ticket_detail.html` 的 PHP+原生 JS**;uni-app 端 fork `shopxo-uniapp` 改写 `goods-detail.vue`,无需经过 ShopXO 模板中间层。
|
|||
|
|
|
|||
|
|
### 证据来源
|
|||
|
|
|
|||
|
|
| 文件/文档 | 结论 |
|
|||
|
|
|----------|------|
|
|||
|
|
| `docs/02_FRONTEND_CUSTOMIZATION.md` | DIY 设计器只支持静态 HTML 区块嵌入,无法参数化;uni-app 完全独立于 ShopXO 模板 |
|
|||
|
|
| `docs/12_UNIAPP_FRONTEND_RESEARCH.md` | shopxo-uniapp 是独立 Vue 项目,通过 API 对接 ShopXO;CSS 在 H5/小程序完全一致(WebView 同源) |
|
|||
|
|
| `docs/14_TEMPLATE_RENDER_INVESTIGATION.md` | ShopXO view/goods/ 模板使用原生 PHP + 原生 JS,session/buy 等控制器直接 render |
|
|||
|
|
| `ticket_detail.html` | 当前已实现:场次选择 + 座位图渲染 + 观演人表单 + 购买栏 |
|
|||
|
|
|
|||
|
|
### 最小可行方案
|
|||
|
|
|
|||
|
|
**H5 端**:在现有 `ticket_detail.html` 基础上增强,引入:
|
|||
|
|
- 座位类型图例(已完成)
|
|||
|
|
- 已售座位 AJAX 实时标记(待实现 `loadSoldSeats()`)
|
|||
|
|
- 座位缩放/拖拽交互(原生 JS,<200 行)
|
|||
|
|
- 动态场次切换时重置已选座位(已写但未调用)
|
|||
|
|
|
|||
|
|
**技术栈**:`原生 HTML + 内联 CSS + 内联 JS`,无框架依赖,ShopXO 模板系统直接渲染,无需构建。
|
|||
|
|
|
|||
|
|
### 理想方案
|
|||
|
|
|
|||
|
|
**uni-app 端**:
|
|||
|
|
1. Fork `shopxo-uniapp` → `vr-shopxo-uniapp`
|
|||
|
|
2. 重写 `pages/goods-detail/goods-detail.vue`,接入 vr_ticket API
|
|||
|
|
3. 新建 `pages/ticket-seat/ticket-seat.vue`(选座主流程)
|
|||
|
|
4. 新建 `pages/ticket-wallet/ticket-wallet.vue`(票夹)
|
|||
|
|
5. H5 本地预览 = 小程序编译效果,CSS 完全一致
|
|||
|
|
|
|||
|
|
**关键约束(uni-app 开发规范)**:
|
|||
|
|
- 用 `rpx` 不用 `vw/vh`
|
|||
|
|
- 用 `<view>` 不用 `<div>`
|
|||
|
|
- 避免 `calc()` 混用单位
|
|||
|
|
- `position: fixed` 吸顶在 H5 正常,小程序需 shopxo-uniapp 已有方案
|
|||
|
|
|
|||
|
|
### 最大风险点
|
|||
|
|
|
|||
|
|
1. **shopxo-uniapp fork 同步成本** — 官方 shopxo-uniapp 更新后需要手动同步,未来维护成本高。→ 建议在 fork 分支上做票务专属页面,官方页面保持独立升级路径。
|
|||
|
|
2. **ShopXO 版本 vs shopxo-diy 版本匹配** — shopxo-diy v1.4.2 ↔ ShopXO v6.8.0,如果使用 DIY 设计器管理非票务页面,版本必须严格匹配。
|
|||
|
|
|
|||
|
|
### 优先级与依赖
|
|||
|
|
|
|||
|
|
- Q1 是 Q4 的技术基础,Q1 的结论直接支撑 Q4 的技术选型决策。
|
|||
|
|
- Q3 依赖 Q1 的约束条件输出。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Q3 结论:第三方无代码构建服务提示词策略
|
|||
|
|
|
|||
|
|
### 核心答案
|
|||
|
|
|
|||
|
|
**用"模板 + 示例 + 约束"三层结构撰写 Prompt**,ShopXO 模板的特殊性(模块化 PHP 标签、ShopXO 资源路径 API)在 Prompt 中明确声明,生成代码后只需做两件事后处理:① 替换静态资源路径为 `ModuleInclude()` 调用,② 注入座位图数据结构(从 PHP 模板变量传入)。
|
|||
|
|
|
|||
|
|
### Prompt 三层结构
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
【第一层:角色定义】
|
|||
|
|
你是一个 ShopXO v6.8.0 模板开发者,擅长编写票务商品详情页。
|
|||
|
|
|
|||
|
|
【第二层:约束清单】
|
|||
|
|
- HTML 结构:使用 <?php echo ModuleInclude('public/header'); ?> 包裹页面头
|
|||
|
|
- 样式:全部内联 <style>,CSS 类名前缀 vr- 避免冲突
|
|||
|
|
- JS 数据注入:const app = <?php echo json_encode($php_var); ?>
|
|||
|
|
- 资源路径:静态资源用 <?php echo ModuleInclude('images/foo.png'); ?>
|
|||
|
|
- 不使用:Vue/React CDN、外部 CDN(ShopXO 必须离线可用)
|
|||
|
|
|
|||
|
|
【第三层:具体需求】
|
|||
|
|
[座位图 UI 规格:rpx 规范、颜色、尺寸 + 交互事件定义]
|
|||
|
|
[ShopXO 数据契约:goods_spec_data、vr_seat_template、extension_data]
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 生成代码后处理步骤
|
|||
|
|
|
|||
|
|
1. **路径替换**:全局搜索 `src="/static/` → `<?php echo ModuleInclude('static/') ?>`
|
|||
|
|
2. **变量注入点**:在 `<script>` 顶部注入 `var seatMap = <?php echo json_encode($vr_seat_template['seat_map']); ?>`
|
|||
|
|
3. **事件绑定**:`onclick` 属性需改为 `onclick="vrTicketApp.toggleSeat(this)"` 格式(原生 JS)
|
|||
|
|
4. **样式隔离**:检查是否覆盖 `.goods-detail-*` 等 ShopXO 全局类名,如有则加 `.vr-ticket-page` 限定符
|
|||
|
|
|
|||
|
|
### 最大风险点
|
|||
|
|
|
|||
|
|
1. **无代码服务生成的 UI 过于复杂** — 座位图等高交互组件无法用无代码工具精确生成,强行生成会导致大量调试工作。→ **无代码服务适合静态展示区块(票务商品介绍、艺人信息图),座位图选座交互必须手写**。
|
|||
|
|
2. **ShopXO 离线可用约束** — ShopXO 运行在企业内部/私有化部署场景,所有资源必须本地化,无代码服务默认 CDN 引用必须全部替换。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Q4 结论:uni-app 兼容性技术栈选型
|
|||
|
|
|
|||
|
|
### 核心答案
|
|||
|
|
|
|||
|
|
**推荐方案:一套 shopxo-uniapp fork + 条件编译**,票务页面走独立路由(H5/小程序双端),商城标准页面复用 shopxo-uniapp 原生实现。
|
|||
|
|
|
|||
|
|
### 技术选型对比
|
|||
|
|
|
|||
|
|
| 维度 | 原生 HTML 模板 | uni-app fork shopxo-uniapp | Flutter / React Native |
|
|||
|
|
|------|---------------|---------------------------|----------------------|
|
|||
|
|
| H5 本地预览 | ✅ 直接浏览器打开 | ✅ HBuilderX H5 运行 | ❌ 需真机调试 |
|
|||
|
|
| 微信小程序 | ❌ 不支持 | ✅ 一键编译 | ✅ 需分别开发 |
|
|||
|
|
| ShopXO API 对接 | 需手动 HTTP | shopxo-uniapp 已封装 | 需手动 HTTP |
|
|||
|
|
| 学习成本 | 低 | 中(需熟悉 Vue) | 高 |
|
|||
|
|
| 座位图等复杂交互 | 原生 JS 手写 | Vue 组件手写 | 手写 |
|
|||
|
|
| 开发速度 | 快(单文件) | 中 | 慢 |
|
|||
|
|
|
|||
|
|
**最终推荐**:`fork shopxo-uniapp`,用 Vue 3 + SCSS,票务页面自研,其他页面复用。
|
|||
|
|
|
|||
|
|
### 票务页面与商城标准页面共存方案
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
vr-shopxo-uniapp/
|
|||
|
|
├── pages/index/index.vue ← 改写:底部 Tab 新增「票务」Tab
|
|||
|
|
├── pages/goods-detail/ ← 改写:票务商品跳 ticket-seat 页面
|
|||
|
|
├── pages/ticket-seat/ ← 新建:选座 + 购票主流程(Vue 组件)
|
|||
|
|
├── pages/ticket-wallet/ ← 新建:票夹(我的票)
|
|||
|
|
├── pages/ticket-verify/ ← 新建:B 端核销
|
|||
|
|
├── App.vue ← request_url 指向目标商城
|
|||
|
|
└── pages.json ← 路由配置
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**H5/小程序一致性**:uni-app H5 和小程序都基于 WebView,CSS 渲染一致。关键:用 `rpx`,用 `<view>`,避免浏览器私有前缀。
|
|||
|
|
|
|||
|
|
### 最大风险点
|
|||
|
|
|
|||
|
|
1. **shopxo-uniapp 官方更新同步** — 100+ forks,官方更新需手动 cherry-pick 到 vr fork。建议将票务专属页面与商城原生页面放在不同目录,改动隔离,升级时只同步商城页面。
|
|||
|
|
2. **ShopXO 版本与 shopxo-uniapp 版本匹配** — shopxo-uniapp 的 API 契约随 ShopXO 后端版本变化,vr_ticket 插件如使用 shopxo-uniapp,请确认 ShopXO 版本(当前 v6.8.0),使用对应的 shopxo-uniapp 版本。
|
|||
|
|
|
|||
|
|
### 优先级与依赖
|
|||
|
|
|
|||
|
|
- **Q4 依赖 Q2(多座位选座)和 Q1(H5 模板基础)**
|
|||
|
|
- Q4 本身是最终落地执行层,前三个 Q 的结论在 Q4 中整合实现
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 优先级矩阵
|
|||
|
|
|
|||
|
|
| 优先级 | 任务 | 负责 Agent | 前置条件 |
|
|||
|
|
|--------|------|-----------|---------|
|
|||
|
|
| P0 | Q2 多 SKU — 走购物车路线打通多座位下单 | BackendArchitect | 无 |
|
|||
|
|
| P1 | Q4 uni-app fork — 建立项目骨架 | FrontendDev | Q1 结论 |
|
|||
|
|
| P2 | Q4 ticket-seat.vue — 选座核心组件 | FrontendDev | P0 完成 |
|
|||
|
|
| P3 | Q1 ticket_detail.html 增强 — 已售座位实时标记 | FrontendDev | 无 |
|
|||
|
|
| P4 | Q3 提示词策略落地 — 无代码服务辅助静态区块 | ProductManager | Q1 结论 |
|
|||
|
|
| P5 | Q2 理想方案 — 插件 hook 拦截 OrderSplitService | BackendArchitect | P0 验证 |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 最小可行方案 vs 理想方案对比
|
|||
|
|
|
|||
|
|
| 维度 | 最小可行方案 | 理想方案 |
|
|||
|
|
|------|------------|---------|
|
|||
|
|
| 多座位下单 | 购物车路线(不碰 OrderSplitService) | 插件 hook 拦截,实现原生多 SKU 单订单 |
|
|||
|
|
| 前端 H5 | 增强 ticket_detail.html(< 3 处改动) | 迁移到 uni-app H5 |
|
|||
|
|
| 前端小程序 | shopxo-uniapp fork,票务页面 Vue 自研 | 完整迁移,小程序体验与 H5 一致 |
|
|||
|
|
| 座位图 | 原生 JS,< 200 行 | Vue 组件,含缩放/拖拽/动画 |
|
|||
|
|
| 观演人表单 | HTML + JS,支持动态增减 | Vue 组件化,数据校验 |
|
|||
|
|
| 核销 B 端 | 复用现有后台核销页面 | 新建小程序核销页面(扫码 + API) |
|
|||
|
|
| 交付周期 | 1 天(可上线 demo) | 2-3 周(完整票务流程) |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 最大技术风险点汇总
|
|||
|
|
|
|||
|
|
| 风险 | 严重程度 | 缓解措施 |
|
|||
|
|
|------|---------|---------|
|
|||
|
|
| OrderSplitService 拆单导致多座位订单被拆 | 高 | 最小方案走购物车绕过;理想方案用插件 hook 拦截 |
|
|||
|
|
| 座位级 SKU 未在后台创建 | 中 | 后台管理界面增加「批量生成座位规格」功能 |
|
|||
|
|
| shopxo-uniapp fork 同步成本 | 中 | 票务页面与商城页面目录隔离,改动隔离升级 |
|
|||
|
|
| 无代码服务无法生成高交互组件 | 低(已有认知) | 座位图等核心交互手写,静态区块用无代码辅助 |
|
|||
|
|
| ShopXO 版本不匹配 shopxo-diy | 低(不走 DIY) | 不使用 DIY 设计器 |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 关键文件清单
|
|||
|
|
|
|||
|
|
| 文件 | 用途 |
|
|||
|
|
|------|------|
|
|||
|
|
| `shopxo/app/service/BuyService.php` | 订单创建入口,多 SKU 关键代码 |
|
|||
|
|
| `shopxo/app/service/OrderSplitService.php` | 拆单逻辑,多座位订单被拆的风险点 |
|
|||
|
|
| `shopxo/app/plugins/vr_ticket/view/goods/ticket_detail.html` | 当前票务详情页模板,Q2 多 SKU Plan A 代码已在此 |
|
|||
|
|
| `docs/12_UNIAPP_FRONTEND_RESEARCH.md` | uni-app 调研存档,Q1/Q4 依赖此文档 |
|
|||
|
|
| `docs/02_FRONTEND_CUSTOMIZATION.md` | ShopXO DIY 设计器局限性证明 |
|
|||
|
|
| `docs/14_TEMPLATE_RENDER_INVESTIGATION.md` | 模板渲染机制调查 |
|
|||
|
|
| `docs/09_SHOPXO_HOOKS_REFERENCE.md` | 插件钩子清单,Q2 理想方案所需 hook 在此 |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 结论
|
|||
|
|
|
|||
|
|
1. **多座位下单可行**:走购物车路线,1 天内可上线多座位下单 Demo。
|
|||
|
|
2. **uni-app 是最终目标**:fork shopxo-uniapp 票务页面自研,商城页面复用,H5 预览 = 小程序编译效果。
|
|||
|
|
3. **无代码服务辅助有限**:适合静态展示区块,座位图等核心交互必须手写。
|
|||
|
|
4. **Immediate Action**:BackendArchitect 提交 Q2 Plan A(购物车路线),FrontendDev 启动 shopxo-uniapp fork 项目骨架。
|