# VR票务详情页面 UniApp 移植规划文档 > 创建时间:2026-05-13 > 状态:**Phase 3 导航优化完成(防闪烁/缓存预检查)** --- ## 📌 背景与目标 ### 背景 - 后端 VR Ticket 插件 (`vr_ticket`) 已实现票务商品详情页模板 `ticket_detail.html` - 前端参考项目 [show-start](file:///Users/bigemon/WorkSpace/vr-shopxo-uniapp/references/show-start/src/App.vue) 提供了高质量的 UI 设计 - 需要将参考项目的 UI 移植到 UniApp 环境中,并整合票务商品的4维选择逻辑 ### 用户需求 1. **保持 show-start 的 popup 交互模式** 作为购票弹窗基础 2. **点击"立即购票"按钮** 后弹出购票弹窗 3. 弹窗内容包含:场次选择、场馆/演播室/分区选择、"选座"按钮 4. **选座功能**:独立的全屏遮罩座位选择器,选完后返回已选座位 tags --- ## 📋 数据结构规范(Phase 2 核心) ### VR 票务扩展字段 从后端 API 返回的商品扩展数据中,票务商品会包含以下关键字段: ```typescript 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; // 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 移植 ✅ 已完成 - [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) ### Phase 2: 购票弹窗与4维选择(开发中) - [x] 购票弹窗基础结构(当前 popupType='buy') - [ ] 接入商品 API 数据(vr_config 解析) - [ ] 场次选择器实现(横向滚动卡片) - [ ] 场馆选择器实现 - [ ] 演播室/分区级联选择 - [ ] 座位选择器组件开发 - [ ] 已选座位 UI 展示 - [ ] 底部总价计算 ### Phase 3: 完整购票流程(待开发) - [ ] 观演人信息表单(每座必填) - [ ] 订单提交 API 集成 - [ ] 跳转支付流程 ### Phase 4: 优化与测试 - [ ] 滚动穿透处理(catchtouchmove) - [ ] 真机/小程序测试 - [ ] 性能优化 --- ## 📝 待办事项 ### ✅ 已完成 - [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) - [x] 购票弹窗基础框架(popupType='buy' 判断) ### ⏳ 后续待办 - [x] 在 `goods-detail.vue` 中添加跳转判断逻辑 - [ ] 解析商品数据中的 vr_config 等扩展字段 - [ ] 实现场次选择器(横向滚动卡片) - [ ] 实现场馆选择器(场次筛选后可用) - [ ] 实现演播室→分区级联选择 - [ ] 实现选座按钮与座位选择器(全屏遮罩) - [ ] 实现已选座位 tags 展示 - [ ] 实现重新选座功能 - [ ] 连接后端 BuyService API 获取动态票务数据 - [ ] 实现底部购买栏联动(已选座数、总价) - [ ] 添加 popup 滚动事件处理(:catchtouchmove) - [ ] 观演人信息表单(姓名/手机/身份证) - [ ] 订单提交入口 - [ ] 测试页面在真机/小程序环境运行 --- ## 🔌 API 接口规范 ### 商品详情接口(扩展字段) 请求商品详情时,后端返回 `vr_config` 扩展数据: ```javascript // 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": [...] } } } } ``` ### 订单提交接口 ```javascript // 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` 方法: ```javascript // 根据当前选择的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. 级联选择 - 选择「场次」后 → 重置「场馆/演播室/分区」,重新计算可选场馆 - 选择「场馆」后 → 筛选出��场馆下的演播室 - 选择「演播室」后 → 筛选出该演播室下的分区,显示分区选择器 ### 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](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) | 后端票务模板参考(4维选择+座位图) | | [pages.json](file:///Users/bigemon/WorkSpace/vr-shopxo-uniapp/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维规格选择、完整购票流程等高级功能。