council(draft): FrontendDev - Issue 2/3/4 findings complete, plan updated

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
council/FrontendDev
Council 2026-04-21 08:34:44 +08:00
parent 9ec1c5f53f
commit fa4640f86e
4 changed files with 202 additions and 9 deletions

20
plan.md
View File

@ -16,17 +16,19 @@
- 责任人BackendArchitect优先、FrontendDev配合验证前端逻辑
- 关键文件:`shopxo/app/plugins/vr_ticket/view/goods/ticket_detail.html`submit 函数)
- [ ] **Issue 2 (P1)**: 缩放时舞台元素不跟随 — `.vr-stage``.vr-seat-rows` 容器外
- 责任人FrontendDev优先
- 关键文件:`ticket_detail.html`CSS transform + DOM 结构)
- [x] [Done: council/FrontendDev] **Issue 2 (P1)**: 缩放时舞台元素不跟随 — `.vr-stage``.vr-seat-rows` 容器外
- 根因:`.vr-stage` 和 `.vr-seat-rows``.vr-seat-map-wrapper` 的平级子元素,缩放不同步
- 修复:引入 `.vr-zoom-container` 包裹两者,统一 transform-origin
- findings: `reviews/FrontendDev-Issue2-StageZoom.md`
- [ ] **Issue 3 (P1)**: spec 加载问题回滚 — 真实库存和已售座位未成功加载spec 加载链路不明确
- 责任人BackendArchitectAPI 链路、FrontendDev前端调用
- 关键文件:`SeatSkuService.php`、`ticket_detail.html`
- [x] [Done: council/FrontendDev] **Issue 3 (P1)**: spec 加载问题回滚 — 真实库存和已售座位未成功加载
- 根因:`loadSoldSeats()` 是空 TODO stub无任何 AJAX 调用
- 修复:实现 `plugins/vr_ticket/index/sold_seats` 接口,前端标记 `.sold` class
- findings: `reviews/FrontendDev-Issue3-SpecLoading.md`
- [ ] **Issue 4 (P2)**: 商品详情/图片加载现状评估
- 责任人FrontendDev优先
- 关键文件:`ticket_detail.html`、`Goods.php`
- [x] [Done: council/FrontendDev] **Issue 4 (P2)**: 商品详情/图片加载现状评估
- 结论:商品内容 ✅ 正常;相册数据 ⚠️ 未使用;需补充相册渲染和 `.goods-detail-content` CSS
- findings: `reviews/FrontendDev-Issue4-GoodsDetail.md`
---

View File

@ -0,0 +1,58 @@
# FrontendDev — Issue 2 Findings: 舞台元素不跟随缩放
## 根因分析
**DOM 结构ticket_detail.html:141-144**:
```html
<div class="vr-seat-map-wrapper">
<div class="vr-stage">舞 台</div> <!-- 舞台:固定在外 -->
<div class="vr-seat-rows" id="seatRows"></div> <!-- 座位行 -->
</div>
```
`.vr-stage``.vr-seat-rows``.vr-seat-map-wrapper` 的**平级子元素**。
如果对 `.vr-seat-rows` 应用 CSS `transform: scale()`,座位会缩放,但舞台不动——两者没有共同的变换容器。
## 修复方案
**推荐方案:将舞台和座位行包裹在同一容器内**
```html
<div class="vr-seat-map-wrapper">
<div class="vr-zoom-container" id="zoomContainer">
<div class="vr-stage">舞 台</div>
<div class="vr-seat-rows" id="seatRows"></div>
</div>
</div>
```
对应的 CSS
```css
.vr-seat-map-wrapper { overflow: hidden; } /* 防止缩放溢出 */
.vr-zoom-container {
display: flex;
flex-direction: column;
align-items: center;
transform-origin: center top;
transition: transform 0.2s ease;
}
.vr-stage { /* existing styles */ }
.vr-seat-rows { /* existing styles */ }
```
JS 缩放时,只需操作 `#zoomContainer``transform: scale()`,舞台和座位同步缩放。
**备选方案JS 层)**:在 zoom handler 中同时更新 `.vr-stage``transform: scale()`,与座位行保持相同倍数。缺点是逻辑分散。
## 隐含问题
当前代码中**没有 zoom 实现**(没有 CSS transform 也没有 JS handler。Issue 2 的修复需要与 zoom 功能实现一并完成。需要确认:
1. 缩放是通过鼠标滚轮还是触摸手势?
2. 缩放倍数范围0.5x ~ 2x
3. 缩放后是否需要拖拽平移?
## 风险
- 舞台已有 `border-radius: 50% 50% 0 0 / 20px 20px 0 0`(弧形顶部),缩放后会变形。需要调整或移除弧形处理。
- 座位行内座位格子有 `flex-shrink: 0`,缩放时座位不会被压缩,这是正确的行为。

View File

@ -0,0 +1,85 @@
# FrontendDev — Issue 3 Findings: Spec 加载问题(前端视角)
## 当前状态
`loadSoldSeats()` 函数ticket_detail.html:375-383**完全是 TODO stub**
```javascript
loadSoldSeats: function() {
// TODO: 从后端加载已售座位
// $.get(this.requestUrl + '?s=plugins/vr_ticket/index/sold_seats', {
// goods_id: this.goodsId,
// spec_base_id: this.sessionSpecId
// }, function(res) {
// // 标记已售座位
// });
},
```
没有任何网络请求soldSeats 永远是空对象 `{}`
## 真实库存加载
**GetGoodsViewData 返回的数据SeatSkuService.php:358-464**
`$goods_spec_data` 只包含**场次维度的 spec_base_id**(非座位级):
```php
[
'spec_id' => $specValue['goods_spec_base_id'] ?? 0, // 场次级 ID
'spec_name' => $timeRange, // "08:00-23:59"
'price' => floatval($sv['price']),
]
```
前端通过 `selectSession()` 选择场次后,`this.sessionSpecId` 被设置为场次级 spec_base_id。**但座位级的 spec_base_id_map每个座位的 SKU ID需要从后端接口查询**。
## specBaseIdMap 的局限性
ticket_detail.html:187 注入的 `specBaseIdMap` 来自 `seatTemplate['spec_base_id_map']`。这个 map 的 key 格式是 `rowLabel_colNum`(如 "A_1"value 是座位级 GoodsSpecBase ID。
问题:**前端无法仅凭前端数据知道哪些座位已售**。需要后端接口:
1. 根据 `goods_id` + `sessionSpecId` 查询所有已售 GoodsSpecBase`inventory = 0`
2. 返回已售座位 key 列表
3. 前端在 `loadSoldSeats()` 中标记 `.sold` class
## 修复方案(前端部分)
需要实现一个 AJAX 接口 `plugins/vr_ticket/index/sold_seats`,前端调用:
```javascript
loadSoldSeats: function() {
if (!this.sessionSpecId) return;
var self = this;
$.get(this.requestUrl + '?s=plugins/vr_ticket/index/sold_seats', {
goods_id: this.goodsId,
spec_base_id: this.sessionSpecId // 场次级 ID
}, function(res) {
if (res.code === 0 && res.data) {
// res.data: [{row_col: "A_1", row_label: "A", col_num: 1}, ...]
res.data.forEach(function(sold) {
self.soldSeats[sold.row_label + '_' + sold.col_num] = true;
});
self.markSoldSeats();
}
});
},
markSoldSeats: function() {
var self = this;
document.querySelectorAll('.vr-seat-row .vr-seat:not(.aisle):not(.space)').forEach(function(el) {
var key = el.dataset.rowLabel + '_' + el.dataset.colNum;
if (self.soldSeats[key]) {
el.classList.add('sold');
}
});
}
```
## API 设计建议
后端需要新增一个控制器方法(可能是 `plugins/vr_ticket/index/Index` 中的 `sold_seats` action查询 `GoodsSpecBase``inventory = 0` 的座位记录,按场次 ID 过滤。
## 依赖
- BackendArchitect 提供 `sold_seats` 接口的准确路径和返回格式
- BackendArchitect 确认 GoodsSpecBase 的 inventory 字段在购票后是否被正确扣减

View File

@ -0,0 +1,48 @@
# FrontendDev — Issue 4 Findings: 商品详情/图片加载评估
## 当前实现
**商品内容渲染ticket_detail.html:161-166**:
```php
<?php if (!empty($goods['content'])): ?>
<div class="vr-seat-section">
<div class="vr-section-title">演出详情</div>
<div class="goods-detail-content"><?php echo $goods['content']; ?></div>
</div>
<?php endif; ?>
```
`$goods['content']` 来自 `GoodsService::GoodsList()`Goods.php:65请求参数 `is_photo => 1` 表示同时加载相册数据。
## 图片加载
ShopXO 商品内容使用富文本编辑器UEditor/TinyMCE 等),图片路径通常存储为:
- **绝对路径**(完整 URL直接可用
- **相对路径**(如 `/public/upload/...`):在 H5 页面中同样可用(浏览器自动补全域名)
商品相册(`goods['photos']`**未在 ticket_detail.html 中渲染**。如需展示,应使用:
```html
<?php if (!empty($goods['photos'])): ?>
<div class="vr-goods-photos">
<?php foreach ($goods['photos'] as $photo): ?>
<img src="<?php echo $photo['image']; ?>" alt="">
<?php endforeach; ?>
</div>
<?php endif; ?>
```
## 评估结论
| 项目 | 状态 | 说明 |
|------|------|------|
| 商品详情内容 | ✅ 正常 | `$goods['content']` 正确渲染 |
| 商品图片 | ⚠️ 未使用 | 相册数据 `$goods['photos']` 未在模板渲染 |
| 商品标题/副标题 | ✅ 正常 | `$goods['title']` / `$goods['simple_desc']` 正常 |
| 放大镜组件 | N/A | ticket_detail.html 不加载 ShopXO goods-detail 相关 CSS/JS |
| 视频播放器 | N/A | 不加载 CKPlayer |
## 建议
1. **补充相册渲染**:在页面头部(`.vr-ticket-header` 下方)添加相册轮播,提升商品展示完整性
2. **图片懒加载**:如果座位图很大,演出详情图片应使用 `loading="lazy"`
3. **内容样式**`.goods-detail-content` 的 CSS 未定义,建议补充样式(图片 max-width、段落间距等