vr-shopxo-plugin/docs/PLAN_PHASE3_FRONTEND.md

260 lines
10 KiB
Markdown
Raw Permalink Normal View History

# Phase 3 前端模板开发计划
> 日期2026-04-20 | 状态:进行中
> 背景Council 调研结论 + CSS 样式机制确认 → Demo 快速落地
---
## 一、调研结论摘要Council 651e0bf2
### Q2 — 单订单多SKU多座位选择的前提
**结论:✅ 可行,走购物车路线**
ShopXO `BuyService.php:86` 循环处理 `goods_data` 数组,每行独立 `spec_base_id`。现有 `ticket_detail.html` Plan A 代码已写好,但 `submit()` 函数有 bug只把第一个座位编码进 URL后续座位丢失。
**最小改动Demo 1天可上线**
- 修复 `submit()`:将 `goodsParamsList` 整体编码POST 到购物车 `CartSave`,再跳转合并支付
- 绕过 `OrderSplitService` 拆单风险(购物车结算路径不触发按仓库拆单)
### Q1 — ShopXO 自定义模板最佳实践
**结论:原生 PHP + 内联 JS渐进增强**
- ShopXO view/goods/ 模板使用原生 PHP + 原生 JSsession/buy 控制器直接 render
- 不走 DIY 设计器(只支持静态 HTML 区块,无法参数化)
- H5 直接浏览器预览,无需构建
### Q3 — 第三方无代码构建服务
**结论:辅助有限,座位图等核心交互必须手写**
- 无代码服务适合静态展示区块(票务介绍、艺人信息图)
- 座位图等高交互组件无法用无代码工具精确生成
- 生成代码后需后处理:路径替换 + 变量注入
### Q4 — uni-app 兼容性技术栈选型
**结论fork shopxo-uniapp票务页面自研**
- fork `shopxo-uniapp``vr-shopxo-uniapp`
- 票务页面ticket-seat / ticket-wallet / ticket-verify自研 Vue 3 组件
- 商城标准页面复用 shopxo-uniapp 原生实现
- CSS 一致H5/小程序都基于 WebView
---
## 二、CSS 样式注入机制ShopXO 官方能力)
### 三层注入体系
| 层级 | 机制 | 甲方操作入口 |
|------|------|------------|
| **CSS 变量** | `header_style_root.html` 定义 `:root` 变量,后台主题配置可改 | ShopXO 后台「主题配色」 |
| **插件 CSS Hook** | `plugins_css_data` 钩子注入独立 CSS 文件 | 替换 `static/plugins/vr_ticket/css/ticket.css` |
| **内联 `<style>`** | 当前 `.vr-ticket-page` 样式块,完全隔离 | 直接修改 `ticket_detail.html` |
### CSS 变量体系ShopXO 官方)
`header_style_root.html` 定义了完整的 CSS 变量系统:
```css
/* 主色 */
--color-main: #E22C08; /* 可在后台改为甲方品牌色 */
--color-main-light: #ffe3de;
--color-main-hover: #EA6B52;
/* 圆角 */
--border-radius-sm: 0.2rem;
--border-radius: 0.4rem;
--border-radius-lg: 0.8rem;
/* 阴影 */
--box-shadow: 0 5px 20px rgba(50,55,58,0.1);
--box-shadow-sm: 0 2px 8px rgba(50,55,58,0.1);
--box-shadow-lg: 0 8px 34px rgba(50,55,58,0.1);
```
vr_ticket 模板内的 `.vr-ticket-page` 可以直接引用这些变量,实现主题色统一。例如:
```css
.vr-purchase-btn {
background: var(--color-main); /* 继承 ShopXO 主题色 */
border-radius: var(--border-radius-lg);
box-shadow: var(--box-shadow-sm);
}
```
### 插件 CSS Hook推荐方案
在插件 service 中注册 `plugins_css_data` 钩子,加载独立 CSS 文件:
```php
// plugins/vr_ticket/hook/ViewGoodsSpiderCss.php
public function handle()
{
return 'plugins/vr_ticket/css/ticket.css';
}
```
甲方样式微调时,只需替换 `static/plugins/vr_ticket/css/ticket.css`,不需要改 PHP 模板。
### 当前 ticket_detail.html 样式结构
```
ticket_detail.html
├── <style> .vr-ticket-page
├── HTML 结构(.vr-ticket-page #vrTicketApp
├── 内联 JSvrTicketApp 对象)
└── ModuleInclude('public/footer')
```
样式完全隔离,不受 ShopXO 升级影响。甲方设计师可以专注修改 CSS不需要理解 PHP 模板逻辑。
---
## 三、Demo 交付计划(最小可行方案)
### 目标1天内上线可演示的多座位下单 Demo
### 当前代码状态
- `ticket_detail.html` 已有 Plan A 代码submit 函数存在 URL 编码 bug
- 座位图渲染正常A/B/C 三排 + 舞台 + 颜色分区 + 选座 UI + 观演人表单)
- `loadSoldSeats()` 是 TODO需要后端配合
### Demo 交付清单
#### P0 — 必须完成Demo 当天)
| 任务 | 文件 | 说明 | 优先级 |
|------|------|------|--------|
| **修复 submit() bug** | `ticket_detail.html` | 当前只传第一个座位,需整体编码 goodsParamsList | 🔴 P0 |
| **购物车路由接通** | `ticket_detail.html` | 改用 `CartSave` API 提交多座位,跳转合并支付 | 🔴 P0 |
| **场次切换重置已选座位** | `ticket_detail.html` | `selectSession()` 调用座位重置逻辑(已有代码未调用) | 🔴 P0 |
| **座位类型图例** | `ticket_detail.html` | 已完成 ✅,确认正常显示 | ✅ 已完成 |
| **购买栏按钮状态联动** | `ticket_detail.html` | 已实现 ✅,`disabled` 状态根据选座数量变化 | ✅ 已完成 |
#### P1 — Demo 当天完成后继续
| 任务 | 文件 | 说明 | 优先级 |
|------|------|------|--------|
| **loadSoldSeats() 实现** | `ticket_detail.html` + 后端 | AJAX 调用后端接口,标记已售座位 | 🟡 P1 |
| **座位图缩放/拖拽交互** | `ticket_detail.html` | 原生 JS < 200 行实现 | 🟡 P1 |
| **CSS 样式文件分离** | `static/vr_ticket/css/ticket.css` | 从内联 `<style>` 抽离,通过 `plugins_css_data` 钩子注册 | 🟡 P1 |
| **甲方主题色变量接入** | `ticket_detail.html` <style> | 将硬编码色值改为 `var(--color-main)` 等变量 | 🟡 P1 |
#### P2 — 后续迭代
| 任务 | 说明 | 优先级 |
|------|------|--------|
| shopxo-uniapp fork | 建立 `vr-shopxo-uniapp` 项目骨架 | 🟢 P2 |
| ticket-seat.vue | uni-app 选座核心组件 | 🟢 P2 |
| B 端核销页 | 小程序扫码核销页面 | 🟢 P2 |
---
## 四、关键技术细节
### 4.1 submit() 修复方案
**当前 bug** `location.href = checkoutUrl + '&goods_params=' + encodeURIComponent(goodsParams)`
URL 方式只能传字符串,多座位数据会被截断或丢失。
**修复方案:** 走购物车 API + 合并支付
```javascript
submit: function() {
// 1. 收集所有座位数据(现有代码正常)
var goodsParamsList = this.selectedSeats.map(function(seat, i) {
var specBaseId = self.specBaseIdMap[seat.seatKey] || self.sessionSpecId;
var seatAttendee = attendeeData[i] || {};
return {
goods_id: self.goodsId,
spec_base_id: parseInt(specBaseId) || 0,
stock: 1,
extension_data: JSON.stringify({
attendee: seatAttendee,
seat: { seatKey: seat.seatKey, label: seat.label, price: seat.price }
})
};
});
// 2. 逐座提交到购物车
var deferreds = goodsParamsList.map(function(params) {
return $.post(__goods_cart_save_url__, {
goods_id: params.goods_id,
spec_id: params.spec_base_id,
stock: params.stock,
// extension_data 作为自定义字段存储
});
});
// 3. 全部成功后跳转合并支付
$.when.apply($, deferreds).done(function() {
location.href = __root__ + '?s=index/cart/index';
}).fail(function() {
alert('座位已被占用,请重新选择');
});
}
```
**为什么走购物车路线:**
- `BuyCart``BuyTypeGoodsList` → 直接调用 `BuyGoods`,完美支持多 `goods_data`
- 购物车结算路径不触发 `OrderSplitService` 按仓库拆单(只按商品拆)
- `plugins_service_order_pay_success_handle_end` 钩子正常触发QR 票生成不受影响
### 4.2 CSS 变量主题化方案
当前 `ticket_detail.html` 内联样式中的硬编码色值,全部改为 CSS 变量:
```css
/* 改造前 */
.vr-purchase-btn { background: linear-gradient(135deg, #409eff, #3b8ef8); }
/* 改造后 */
.vr-purchase-btn {
background: linear-gradient(135deg, var(--color-main), var(--color-main-hover));
border-radius: var(--border-radius-lg);
box-shadow: var(--box-shadow-sm);
}
```
甲方想要调整主题色时,有三条路:
1. **后台改**ShopXO 管理后台 → 主题配色 → 自动同步到所有 `var(--color-*)` 变量
2. **文件改**:替换 `static/plugins/vr_ticket/css/ticket.css`
3. **代码改**:直接修改 `ticket_detail.html``<style>`
### 4.3 specBaseIdMap 降级策略
```javascript
var specBaseId = self.specBaseIdMap[seat.seatKey] || self.sessionSpecId;
```
- 如果 `seatKey`(如 "A_1")在 `specBaseIdMap` 中有对应记录 → 座位级 SKU精确到每个座位
- 如果 `specBaseIdMap` 中没有(如后台未批量创建座位规格)→ 降级到 `sessionSpecId`Zone 级别,同一 zone 全部座位共享一个 SKU
**后台需要提前创建座位规格**vr_ticket 后台管理界面需增加「批量生成座位规格」功能。
---
## 五、文件清单
| 文件 | 当前状态 | 下一步 |
|------|---------|--------|
| `shopxo/app/plugins/vr_ticket/view/goods/ticket_detail.html` | Plan A 代码有 bug | 修复 submit(),接购物车 |
| `shopxo/app/plugins/vr_ticket/static/css/ticket.css` | 不存在 | 从 ticket_detail.html 抽离样式 |
| `docs/council-research-output.md` | ✅ 已完成 | 无需修改 |
| `shopxo/app/service/BuyService.php` | 参考 | 无需修改(走购物车路线) |
| `shopxo/app/service/SeatSkuService.php` | 有缺陷(单模板模式) | 等待 Issue #15/#16 修复 |
---
## 六、技术风险
| 风险 | 严重程度 | 缓解 |
|------|---------|------|
| `submit()` 只传第一座位(已发现) | 🔴 高 | 修复 submit(),改走购物车 API |
| OrderSplitService 拆单(多座位变多笔支付) | 🔴 高 | 购物车路线绕过 |
| 座位级 SKU 后台未创建specBaseIdMap 空) | 🟡 中 | 降级到 Zone 级别 + 后台增加批量生成功能 |
| 甲方主题色调整后样式不一致 | 🟡 中 | CSS 变量化,所有色值引用 `var(--color-*)` |
| shopxo-uniapp fork 官方更新同步成本 | 🟢 低 | 票务页面与商城页面目录隔离 |