fix(vr-ticket): 演出详情 content_app 内容渲染修复
- 新增 detailContent data 字段,从 tree API 的 apiData.goods.content_app 提取 - 参考 goods-detail.vue 的嵌套循环模式渲染 content_app(不用 rich-text) - 新增 detail-content/detail-item/content-items/item 等 CSS 样式 - 新增 docs/vr-ticket-work-record-20260612.md 工作留痕文档master
parent
8f2960576c
commit
8ce0e5b7d2
|
|
@ -0,0 +1,221 @@
|
|||
# 工作留痕 — VR 票务详情页演出详情内容渲染修复
|
||||
|
||||
> 创建时间:2026-06-12
|
||||
> 关联对话:2fb4912f-8100-45fc-b11f-d2ac37536abf
|
||||
> 涉及文件:
|
||||
> - `pages/goods-vr-ticket/goods-vr-ticket.vue`
|
||||
> - `pages/goods-vr-ticket/goods-vr-ticket.css`
|
||||
> - `docs/test.json`(后端返回的 tree API 示例数据)
|
||||
|
||||
---
|
||||
|
||||
## 一、问题描述
|
||||
|
||||
票务商品详情页 `/pages/goods-vr-ticket/goods-vr-ticket?id=121` 的"演出详情"区域完全不显示内容。
|
||||
|
||||
### 1.1 现象
|
||||
- 页面加载后,"演出详情" section 只有标题,无正文
|
||||
- 图片和文字详情均不渲染
|
||||
|
||||
### 1.2 根因分析
|
||||
|
||||
**原因 A(模板层面)**:演出详情 section 在模板中只有 `<section-header>`,没有渲染 body:
|
||||
|
||||
```html
|
||||
<!-- 原来的模板 -->
|
||||
<view class="section-card mt-10">
|
||||
<view class="section-header">
|
||||
<text class="section-title">演出详情</text>
|
||||
</view>
|
||||
<!-- ❌ 没有任何内容渲染 -->
|
||||
</view>
|
||||
```
|
||||
|
||||
**原因 B(数据层面)**:`handleTreeData` 方法从未从 tree API 响应中提取 `content_app` 字段。
|
||||
|
||||
### 1.3 数据来源确认
|
||||
|
||||
后端 `vr_ticket` 插件的 tree 接口已更新,`data.goods.content_app` 包含详情内容:
|
||||
|
||||
```
|
||||
tree API: plugins/index?pluginsname=vr_ticket&pluginscontrol=goods&pluginsaction=tree&...&goods_id=121
|
||||
```
|
||||
|
||||
响应结构(关键字段):
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"data": {
|
||||
"goods": {
|
||||
"id": 121,
|
||||
"title": "一个多日测试票务商品2",
|
||||
"content_app": [
|
||||
{
|
||||
"id": 13,
|
||||
"images": "http://localhost:10000/static/upload/images/goods/2026/05/29/1779992828577719.jpeg",
|
||||
"content": ["这是一条测试详情,这是一条测试详情,这是一条测试详情..."],
|
||||
"content_old": "这是一条测试详情,这是一条测试详情..."
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 二、修复方案
|
||||
|
||||
### 2.1 数据层:提取 `content_app`
|
||||
|
||||
**文件**:`pages/goods-vr-ticket/goods-vr-ticket.vue`
|
||||
|
||||
在 `handleTreeData` 方法中添加对 `apiData.goods.content_app` 的提取:
|
||||
|
||||
```javascript
|
||||
handleTreeData(apiData) {
|
||||
this.treeData = apiData.tree;
|
||||
this.seatTemplates = apiData.seat_templates;
|
||||
|
||||
// 提取会话元数据和同场次商品
|
||||
this.sessionMeta = apiData.session_meta || [];
|
||||
this.peerGoods = apiData.peer_goods || [];
|
||||
|
||||
// 提取 goods 属性中的 content_app(演出详情) ✅ 新增
|
||||
if (apiData.goods && apiData.goods.content_app) {
|
||||
this.detailContent = apiData.goods.content_app;
|
||||
console.log('[VR Ticket] detailContent loaded:', this.detailContent);
|
||||
}
|
||||
|
||||
// 提取场馆数据...
|
||||
}
|
||||
```
|
||||
|
||||
同步在 `data()` 中新增字段:
|
||||
```javascript
|
||||
data() {
|
||||
return {
|
||||
// ...
|
||||
detailContent: [], // 详情内容(从 tree API 的 goods.content_app 获取)
|
||||
// ...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2.2 渲染层:嵌套循环渲染
|
||||
|
||||
参考 `pages/goods-detail/goods-detail.vue`(lines 401-408)的渲染模式:
|
||||
|
||||
```html
|
||||
<!-- goods-detail.vue 原版 -->
|
||||
<view v-for="(item, index) in goods_content_app" :key="index" class="goods-detail-app">
|
||||
<image v-if="(item.images || null) != null" ... :src="item.images" mode="widthFix" />
|
||||
<view v-if="(item.content || null) != null" class="content-items">
|
||||
<view v-for="(items, index2) in item.content" :key="index2" class="item">{{ items }}</view>
|
||||
</view>
|
||||
</view>
|
||||
```
|
||||
|
||||
**关键点**:`item.content` 是一个字符串数组,不能直接传给 `rich-text`,而需要对每个字符串做嵌套循环渲染。
|
||||
|
||||
修复后的模板(`goods-vr-ticket.vue`):
|
||||
|
||||
```html
|
||||
<view class="detail-content" v-if="detailContent && detailContent.length > 0">
|
||||
<view v-for="(item, index) in detailContent" :key="index" class="detail-item">
|
||||
<image
|
||||
v-if="item.images"
|
||||
:src="item.images"
|
||||
class="detail-image"
|
||||
mode="widthFix"
|
||||
:lazy-load="true"
|
||||
/>
|
||||
<view v-if="item.content && item.content.length > 0" class="content-items">
|
||||
<view v-for="(text, i2) in item.content" :key="i2" class="item">{{ text }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view v-else class="detail-empty">暂无演出详情</view>
|
||||
```
|
||||
|
||||
### 2.3 样式层:CSS 类名对齐
|
||||
|
||||
**文件**:`pages/goods-vr-ticket/goods-vr-ticket.css`
|
||||
|
||||
新增样式(与 `goods-detail.vue` 的 `.goods-detail-app` / `.content-items` / `.item` 体系对齐):
|
||||
|
||||
```css
|
||||
.detail-empty {
|
||||
font-size: 13px;
|
||||
color: #999;
|
||||
padding: 20px 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.detail-content {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.detail-item {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.detail-image {
|
||||
width: 100%;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.content-items {
|
||||
padding: 0 4px;
|
||||
}
|
||||
|
||||
.content-items .item {
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
line-height: 1.8;
|
||||
text-align: left;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 三、变更文件清单
|
||||
|
||||
| 文件 | 变更类型 | 说明 |
|
||||
|------|---------|------|
|
||||
| `pages/goods-vr-ticket/goods-vr-ticket.vue` | 修改 | 新增 `detailContent` data 字段、`handleTreeData` 提取逻辑、模板渲染 body |
|
||||
| `pages/goods-vr-ticket/goods-vr-ticket.css` | 修改 | 新增 `.detail-content`、`.detail-item`、`.content-items`、`.item` 等样式 |
|
||||
| `docs/vr-ticket-work-record-20260612.md` | 新增 | 本工作留痕文档 |
|
||||
| `docs/test.json` | 修改 | 后端返回的 tree API 完整示例数据(goods.content_app 字段) |
|
||||
|
||||
---
|
||||
|
||||
## 四、验证方式
|
||||
|
||||
1. 启动 H5 编译:`npm run dev:h5`(端口 8082)
|
||||
2. 访问:`http://localhost:8082/#/pages/goods-vr-ticket/goods-vr-ticket?id=121`
|
||||
3. 滚动到"演出详情"区域,应能看到:
|
||||
- 图片(如有):宽度 100%,圆角 8px
|
||||
- 文字详情:每行文字独立渲染,14px,行高 1.8
|
||||
|
||||
---
|
||||
|
||||
## 五、技术备注
|
||||
|
||||
### 5.1 为什么不用 `rich-text`?
|
||||
`content_app` 中的 `item.content` 是字符串数组(如 `["文字1", "文字2"]`)。之前错误地将整个数组传给 `rich-text :nodes="item.content"`,导致渲染失败。正确做法是对数组中每个字符串做 `v-for` 嵌套循环渲染,与 `goods-detail.vue` 保持一致。
|
||||
|
||||
### 5.2 tree API vs detail API
|
||||
VR 票务商品详情页**不再调用** `goods/detail` 接口,直接通过 `goods/tree` (pluginsname=vr_ticket) 接口获取所有数据(含 `goods.content_app`),符合 vr_ticket 插件的数据自包含设计。
|
||||
|
||||
---
|
||||
|
||||
## 六、参考文件
|
||||
|
||||
| 文件 | 用途 |
|
||||
|------|------|
|
||||
| `pages/goods-detail/goods-detail.vue` (L389-L408) | content_app 渲染模式参考 |
|
||||
| `docs/test.json` | tree API 完整响应示例 |
|
||||
| `docs/vr-ticket-uniapp-supplement.md` | VR 票务 UniApp 移植完整文档 |
|
||||
|
|
@ -607,6 +607,39 @@
|
|||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.detail-empty {
|
||||
font-size: 13px;
|
||||
color: #999;
|
||||
padding: 20px 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.detail-content {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.detail-item {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.detail-image {
|
||||
width: 100%;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.content-items {
|
||||
padding: 0 4px;
|
||||
}
|
||||
|
||||
.content-items .item {
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
line-height: 1.8;
|
||||
text-align: left;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.round {
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,6 +51,21 @@
|
|||
<view class="section-header">
|
||||
<text class="section-title">演出详情</text>
|
||||
</view>
|
||||
<view class="detail-content" v-if="detailContent && detailContent.length > 0">
|
||||
<view v-for="(item, index) in detailContent" :key="index" class="detail-item">
|
||||
<image
|
||||
v-if="item.images"
|
||||
:src="item.images"
|
||||
class="detail-image"
|
||||
mode="widthFix"
|
||||
:lazy-load="true"
|
||||
/>
|
||||
<view v-if="item.content && item.content.length > 0" class="content-items">
|
||||
<view v-for="(text, i2) in item.content" :key="i2" class="item">{{ text }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view v-else class="detail-empty">暂无演出详情</view>
|
||||
</view>
|
||||
|
||||
<!-- Section: 购票须知 -->
|
||||
|
|
@ -186,6 +201,8 @@ export default {
|
|||
peerGoods: [],
|
||||
// 场次元数据(用于停售控制)
|
||||
sessionMeta: [],
|
||||
// 详情内容(从 tree API 的 goods.content_app 获取)
|
||||
detailContent: [],
|
||||
// 服务说明数据 (可从 API 获取)
|
||||
defaultServices: [
|
||||
{ title: '电子票', desc: '现场验票时,请观演人出示APP票夹中的电子票二维码验票入场,不支持截屏。', status: 'success' },
|
||||
|
|
@ -363,11 +380,17 @@ export default {
|
|||
handleTreeData(apiData) {
|
||||
this.treeData = apiData.tree;
|
||||
this.seatTemplates = apiData.seat_templates;
|
||||
|
||||
|
||||
// 提取会话元数据和同场次商品
|
||||
this.sessionMeta = apiData.session_meta || [];
|
||||
this.peerGoods = apiData.peer_goods || [];
|
||||
|
||||
// 提取 goods 属性中的 content_app(演出详情)
|
||||
if (apiData.goods && apiData.goods.content_app) {
|
||||
this.detailContent = apiData.goods.content_app;
|
||||
console.log('[VR Ticket] detailContent loaded:', this.detailContent);
|
||||
}
|
||||
|
||||
// 提取场馆数据
|
||||
const venuesMap = {};
|
||||
Object.values(apiData.seat_templates || {}).forEach(template => {
|
||||
|
|
|
|||
Loading…
Reference in New Issue