17 KiB
17 KiB
VR票务详情页面 UniApp 移植规划文档
创建时间:2026-05-13
状态:Phase 3 导航优化完成(防闪烁/缓存预检查)
📌 背景与目标
背景
- 后端 VR Ticket 插件 (
vr_ticket) 已实现票务商品详情页模板ticket_detail.html - 前端参考项目 show-start 提供了高质量的 UI 设计
- 需要将参考项目的 UI 移植到 UniApp 环境中,并整合票务商品的4维选择逻辑
用户需求
- 保持 show-start 的 popup 交互模式 作为购票弹窗基础
- 点击"立即购票"按钮 后弹出购票弹窗
- 弹窗内容包含:场次选择、场馆/演播室/分区选择、"选座"按钮
- 选座功能:独立的全屏遮罩座位选择器,选完后返回已选座位 tags
📋 数据结构规范(Phase 2 核心)
VR 票务扩展字段
从后端 API 返回的商品扩展数据中,票务商品会包含以下关键字段:
interface VRTicketGoods {
id: number;
title: string;
images: string[];
is_vr_ticket: 1; // 票务商品标识
// VR 票务扩展数据(来自 vr_config 或 extension_data)
vr_config?: {
// 场次规格列表(从 goods_spec_data 解析)
spec_list?: Array<{
spec_name: string; // 场次名称,如 "2026-06-01 14:00"
price: number; // 价格
inventory: number; // 库存
}>;
// 座位规格映射(从 seat_spec_map 解析)
seat_spec_map?: Record<string, {
spec: Array<{ type: string; value: string }>; // 4维规格
price: number;
inventory: number;
section: { name: string; color: string };
}>;
// 座位模板(座位图数据)
seat_template?: {
venue: { name: string; address: string; location: { lng: string; lat: string } };
rooms: Array<{
id: string;
name: string;
map: string[]; // 座位矩阵,如 ["AAAAA", "AAB__AA"]
sections: Array<{ char: string; name: string; price: number; color: string }>;
}>;
};
};
}
4维规格体系
票务商品的选座流程采用 4维规格级联选择:
| 维度 | 字段名 | 说明 | 示例 |
|---|---|---|---|
| 1 | $vr-场次 |
演出场次/时间 | "2026-06-01 14:00" |
| 2 | $vr-场馆 |
演出场馆 | "北京凯迪拉克中心" |
| 3 | $vr-演播室 |
室内演出厅 | "主要展厅" |
| 4 | $vr-分区 |
座位分区 | "VIP区"、"普通区" |
选择级联逻辑:选择场次 → 筛选可用场馆 → 选择场馆 → 筛选可用演播室 → 选择演播室 → 筛选可用分区 → 选择分区 → 加载座位图
📐 UI/UX 设计概览
页面结构
┌─────────────────────────────────────┐
│ Header Banner (海报 + 标题 + 收藏) │
├─────────────────────────────────────┤
│ Location Card (最近场馆) │
├─────────────────────────────────────┤
│ Service Tags (服务说明) │
├─────────────────────────────────────┤
│ 演出详情 Section │
├─────────────────────────────────────┤
│ 购票须知 Section │
├─────────────────────────────────────┤
│ 观演须知 Section │
├─────────────────────────────────────┤
│ Bottom Bar (客服 + 立即购票) │ ← Fixed 底部
└─────────────────────────────────────┘
购票弹窗 Popup 交互(核心功能区)
┌─────────────────────────────────────┐
│ [场次选择 - 横向卡片滚动] │ ← vr-session-grid
├─────────────────────────────────────┤
│ [选择场馆] │ ← vr-booking-block
├─────────────────────────────────────┤
│ [选择演播室] → [选择分区] │ ← 级联选择
├─────────────────────────────────────┤
│ [选座] 按钮 ← 打开座位选择器 │
│ │
│ 未选座时:尚未选择 [立刻选座] │
│ 已选座时: │
│ ┌─────┐ ┌─────┐ ┌─────┐ │
│ │ A1 │ │ A2 │ │ A3 │ │ ← 已选座位 tags
│ └─────┘ └─────┘ └─────┘ │
│ 【重新选座】 │
├─────────────────────────────────────┤
│ 合计: ¥888 [确认购票] │ ← 底部购买栏
└─────────────────────────────────────┘
座位选择器(全屏遮罩)
┌─────────────────────────────────────┐
│ ← 返回 选择座位 [已完成 3 座] │
├─────────────────────────────────────┤
│ 🎵 舞 台 │ ← 舞台指示
├─────────────────────────────────────┤
│ A ■ ■ ■ ■ ■ │ ← 座位行(A排)
│ B ■ ■ ■ ■ ■ ■ ■ │ ← 可用座位:■ 点击选中
│ C ■ _ _ ■ ■ ■ │ ← 过道:_
│ D ■ ■ ■ ■ ■ │ ← 已售:灰色
├─────────────────────────────────────┤
│ 图例: [VIP] [普通] [已售] │
└─────────────────────────────────────┘
座位状态定义
- 可用座位:可点击选择,带分区颜色
- 已选座位:高亮边框,数字标识
- 已售座位:灰色,不可点击
- 过道/空位:占位符,不渲染
🔄 阶段进展记录
Phase 1: 基础 UI 移植 ✅ 已完成
- 在
pages.json中注册新页面路由 - 创建
pages/goods-vr-ticket/目录 - 搬运
App.vue内容到goods-vr-ticket.vue - 适配 UniApp 语法(scrollable、navigation 等)
- 替换内联 SVG → iconfont 组件(兼容<E585BC><E5AEB9>信小程序)
- 添加 popup 容器左右留白(padding: 16px)
- 修复 popup-content 溢出问题(overflow-x, word-break)
Phase 2: 购票弹窗与4维选择(已完成)
- 购票弹窗基础结构(当前 popupType='buy')
- 接入商品 Tree API 数据(vr_config 已废弃,使用 Tree API)
- 场次选择器实现(级联选择)
- 场馆选择器实现
- 演播室/分区级联选择
- 座位选择器组件开发
- 已选座位 UI 展示
- 底部总价计算
Phase 3: 完整购票流程(开发中)
- 观演人信息表单(每座必填)
- 订单提交 API 集成
- 跳转支付流程
Phase 4: 优化与测试
- 滚动穿透处理(catchtouchmove)
- 真机/小程序测试
- 性能优化
📝 待办事项
✅ 已完成
- 在
pages.json中注册新页面路由 - 创建
pages/goods-vr-ticket/目录 - 搬运
App.vue内容到goods-vr-ticket.vue - 适配 UniApp 语法(scrollable、navigation 等)
- 替换内联 SVG → iconfont 组件(兼容微信小程序)
- 添加 popup 容器左右留白(padding: 16px)
- 修复 popup-content 溢出问题(overflow-x, word-break)
- 购票弹窗基础框架(popupType='buy' 判断)
⏳ 后续待办
- 在
goods-detail.vue中添加跳转判断逻辑 - 解析商品数据中的 vr_config 等扩展字段 (已改为使用 Tree API)
- 实现场次选择器(级联卡片)
- 实现场馆选择器(场次筛选后可用)
- 实现演播室→分区级联选择
- 实现选座按钮与座位选择器(全屏遮罩)
- 实现已选座位 tags 展示
- 实现重新选座功能
- 连接后端 BuyService API 获取动态票务数据
- 实现底部购买栏联动(已选座数、总价)
- 添加 popup 滚动事件处理(:catchtouchmove)
- 观演人信息表单(姓名/手机/身份证)
- 订单提交入口
- 测试页面在真机/小程序环境运行
🔌 API 接口规范
商品详情接口(扩展字段)
请求商品详情时,后端返回 vr_config 扩展数据:
// GET /api/goods/detail?id=xxx
{
"code": 0,
"data": {
"id": 100,
"title": "王一浩「To U」巡演北京收官站",
"images": "[\"xxx.jpg\"]",
"is_vr_ticket": 1,
// VR 票务扩展
"vr_config": {
"goods_spec_data": [
{ "spec_name": "2026-06-01 14:00", "price": 580, "inventory": 100 },
{ "spec_name": "2026-06-01 20:00", "price": 680, "inventory": 50 }
],
"seat_spec_map": {
"room_001_A_1": {
"spec": [
{ "type": "$vr-场次", "value": "2026-06-01 14:00" },
{ "type": "$vr-场馆", "value": "北京凯迪拉克中心" },
{ "type": "$vr-演播室", "value": "主要展厅" },
{ "type": "$vr-分区", "value": "VIP区" }
],
"price": 580,
"inventory": 1,
"section": { "name": "VIP区", "color": "#ff4d4f" }
}
},
"vr_seat_template": {
"venue": { "name": "北京凯迪拉克中心", "address": "...", "location": { "lng": "116.4", "lat": "39.9" } },
"rooms": [...]
}
}
}
}
订单提交接口
// POST /api/buy/add
{
"goods_data": [
{
"goods_id": 100,
"spec": [...], // 4维规格数组
"stock": 1,
"order_base": {
"extension_data": {
"attendee": {
"real_name": "张三",
"phone": "13800138000",
"id_card": "110101199001011234"
}
}
}
}
],
"buy_type": "goods",
"address_id": "0",
"site_model": "2"
}
🛠️ 技术实现要点
1. 4维规格筛选逻辑
参考 ticket_detail.html 中的 filterSeats 方法:
// 根据当前选择的4维过滤可用座位
filterSeats: function() {
var matchSession = this.currentSession != null;
var matchVenue = this.currentVenue != null;
var matchRoom = this.currentRoom != null;
var matchSection = this.currentSection != null;
// 遍历所有座位,检查是否满足当前选择条件
for (var i = 0; i < seatInfo.spec.length; i++) {
if (seatInfo.spec[i].type === '$vr-场次' && seatInfo.spec[i].value === this.currentSession) {
matchSession = true;
}
// ... 同理其他维度
}
}
2. 座位状态管理
selectedSeats[]:已选座位数组soldSeats{}:已售座位映射- 切换场次时清空已选座位
3. 级联选择
- 选择「场次」后 → 重置「场馆/演播室/分区」,重新计算可选场馆
- 选择「场馆」后 → 筛选出<E98089><E587BA>场馆下的演播室
- 选择「演播室」后 → 筛选出该演播室下的分区,显示分区选择器
4. 座位渲染
座位图使用字符矩阵渲染:
A/B/...:座位字符_:过道/空位
📦 文件结构
pages/goods-vr-ticket/
├── goods-vr-ticket.vue # 主页面(已移植 Phase 1)
├── goods-vr-ticket.css # 独立样式(可选)
└── components/
├── vr-session-select/ # 场次选择器组件
├── vr-venue-select/ # 场馆选择器组件
├── vr-seat-selector/ # 座位选择器(全屏遮罩)
└── vr-attendee-form/ # 观演人表单
🔗 相关参考
| 文件 | 说明 |
|---|---|
| references/show-start/src/App.vue | 源 UI 参考(Vue 2) |
| pages/goods-detail/goods-detail.vue | 商品详情页(跳转入口) |
| vr_ticket/ticket_detail.html | 后端票务模板参考(4维选择+座位图) |
| pages.json | 页面路由配置 |
🎯 关键文件
| 文件 | 状态 | 说明 |
|---|---|---|
| pages/goods-vr-ticket/goods-vr-ticket.vue | ✅ 已移植 | 主页面(Phase 1 完成) |
| pages/goods-detail/goods-detail.vue | ⏳ 待修改 | 添加 is_vr_ticket 跳转判断 |
| components/iconfont/iconfont.vue | ✅ 已加固 | iconfont 组件 |
⚠️ 注意:当前 Phase 2 正在开发中,核心为 4维规格选择器 + 座位选择器。Phase 3 将实现观演人表单和订单提交。座时:尚未选择 [立刻选座] │ │ 已选座时: │ │ [A区-2排-3号] [A区-2排-4号] │ │ 【重新选座】 │ ├─────────────────────────────────────┤ │ [确认购票] │ └─────────────────────────────────────┘
点击[选座] → 打开座位选择器(全屏遮罩)
---
## 🗂️ 文件结构(Phase 1 完成后)
pages/goods-vr-ticket/ ├── goods-vr-ticket.vue # 主页面(移植自 App.vue) └── goods-vr-ticket.css # 独立样式文件(可选)
---
## 📝 待办事项
### ✅ 已完成
- [x] 在 `pages.json` 中注册新页面路由
- [x] 创建 `pages/goods-vr-ticket/` 目录
- [x] 搬运 `App.vue` 内容到 `goods-vr-ticket.vue`
- [x] 适配 UniApp 语法(scrollable、navigation 等)
- [x] 替换内联 SVG → iconfont 组件(兼容微信小程序)
- [x] 添加 popup 容器左右留白(padding: 16px)
- [x] 修复 popup-content 溢出问题(overflow-x, word-break)
### ⏳ 后续待办
- [ ] 在 `goods-detail.vue` 中添加跳转判断逻辑
- [ ] 对 商品数据里的vr_config 等数据进行解析,
- [ ] 实现购票弹窗 Popup 交互,移植老的4 维规格选择到新的购票弹窗 Popup 中。
- [ ] 实现购票弹窗内的选座逻辑
- [ ] 连接后端 BuyService API(动态票务数据)
- [ ] 优化 popup 滚动事件处理(:catchtouchmove)
- [ ] 测试页面在真机/小程序环境运行
---
## 🔄 阶段进展记录
### 2026-05-13 样式迁移完成
- ✅ 移除 inline SVG,改用 iconfont 组件
- ✅ map-icon 容器添加边框
- ✅ venue-popup-info 添加地址图标(icon-location)
- ✅ popup-container 添加 padding: 0 16px
- ✅ popup-content 修复溢出(word-break, box-sizing)
### 2026-05-14 导航优化完成
- ✅ goods-detail.vue 添加白色遮罩(初始覆盖,200ms后渐变消失)
- ✅ 添加缓存预检查(快速判断VR票务,无需等待API)
- ✅ goods-vr-ticket.vue 加载遮罩 + pageShow 显示控制
- ✅ 修复返回导航:智能判断页面栈,无历史则跳转首页
- ✅ goods-detail 使用 redirectTo 替代 navigateTo,避免循环
### 关键文件
| 文件 | 状态 |
|------|------|
| pages/goods-vr-ticket/goods-vr-ticket.vue | ✅ 已移植 |
| pages/goods-vr-ticket/goods-vr-ticket.css | ✅ 已移植 |
| pages/goods-vr-ticket/components/ticket-header/index.vue | ✅ 已移植 |
| pages/goods-vr-ticket/components/venue-card/index.vue | ✅ 已移植 |
| pages/goods-vr-ticket/components/ticket-popup/index.vue | ✅ 已移植 |
| components/iconfont/iconfont.vue | ✅ 已加固尺寸 |
| pages/goods-detail/goods-detail.vue | ✅ 已加固导航 |
---
## 🔗 相关参考
| 文件 | 说明 |
|------|------|
| [references/show-start/src/App.vue](file:///Users/bigemon/WorkSpace/vr-shopxo-uniapp/references/show-start/src/App.vue) | 源 UI 参考(Vue 2) |
| [pages/goods-detail/goods-detail.vue](file:///Users/bigemon/WorkSpace/vr-shopxo-uniapp/pages/goods-detail/goods-detail.vue) | 商品详情页(跳转入口) |
| [vr_ticket/ticket_detail.html](file:///Users/bigemon/WorkSpace/vr-shopxo-plugin/shopxo/app/plugins/vr_ticket/view/goods/ticket_detail.html) | 后端票务模板参考 |
| [pages.json](file:///Users/bigemon/WorkSpace/vr-shopxo-uniapp/pages.json) | 页面路由配置 |
---
> ⚠️ **注意**:当前为 Phase 1 规划,仅实现初步移植和测试环境搭建。后续 Phase 2-4 将逐步实现座位选择器、4维规格选择、完整购票流程等高级功能。