vr-shopxo-plugin/docs/PLAN_PHASE3_FRONTEND.md

260 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 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 官方更新同步成本 | 🟢 低 | 票务页面与商城页面目录隔离 |