From fa4640f86e4c5f33a6a8ea7c312516a9a45181f0 Mon Sep 17 00:00:00 2001 From: Council Date: Tue, 21 Apr 2026 08:34:44 +0800 Subject: [PATCH] council(draft): FrontendDev - Issue 2/3/4 findings complete, plan updated Co-Authored-By: Claude Sonnet 4.6 --- plan.md | 20 +++--- reviews/FrontendDev-Issue2-StageZoom.md | 58 ++++++++++++++++ reviews/FrontendDev-Issue3-SpecLoading.md | 85 +++++++++++++++++++++++ reviews/FrontendDev-Issue4-GoodsDetail.md | 48 +++++++++++++ 4 files changed, 202 insertions(+), 9 deletions(-) create mode 100644 reviews/FrontendDev-Issue2-StageZoom.md create mode 100644 reviews/FrontendDev-Issue3-SpecLoading.md create mode 100644 reviews/FrontendDev-Issue4-GoodsDetail.md diff --git a/plan.md b/plan.md index d932a33..6c80454 100644 --- a/plan.md +++ b/plan.md @@ -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 加载链路不明确 - - 责任人:BackendArchitect(API 链路)、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` --- diff --git a/reviews/FrontendDev-Issue2-StageZoom.md b/reviews/FrontendDev-Issue2-StageZoom.md new file mode 100644 index 0000000..c8e81bf --- /dev/null +++ b/reviews/FrontendDev-Issue2-StageZoom.md @@ -0,0 +1,58 @@ +# FrontendDev — Issue 2 Findings: 舞台元素不跟随缩放 + +## 根因分析 + +**DOM 结构(ticket_detail.html:141-144)**: +```html +
+
舞 台
+
+
+``` + +`.vr-stage` 和 `.vr-seat-rows` 是 `.vr-seat-map-wrapper` 的**平级子元素**。 + +如果对 `.vr-seat-rows` 应用 CSS `transform: scale()`,座位会缩放,但舞台不动——两者没有共同的变换容器。 + +## 修复方案 + +**推荐方案:将舞台和座位行包裹在同一容器内** + +```html +
+
+
舞 台
+
+
+
+``` + +对应的 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`,缩放时座位不会被压缩,这是正确的行为。 diff --git a/reviews/FrontendDev-Issue3-SpecLoading.md b/reviews/FrontendDev-Issue3-SpecLoading.md new file mode 100644 index 0000000..a082134 --- /dev/null +++ b/reviews/FrontendDev-Issue3-SpecLoading.md @@ -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 字段在购票后是否被正确扣减 diff --git a/reviews/FrontendDev-Issue4-GoodsDetail.md b/reviews/FrontendDev-Issue4-GoodsDetail.md new file mode 100644 index 0000000..5765ce8 --- /dev/null +++ b/reviews/FrontendDev-Issue4-GoodsDetail.md @@ -0,0 +1,48 @@ +# FrontendDev — Issue 4 Findings: 商品详情/图片加载评估 + +## 当前实现 + +**商品内容渲染(ticket_detail.html:161-166)**: +```php + +
+
演出详情
+
+
+ +``` + +`$goods['content']` 来自 `GoodsService::GoodsList()`(Goods.php:65),请求参数 `is_photo => 1` 表示同时加载相册数据。 + +## 图片加载 + +ShopXO 商品内容使用富文本编辑器(UEditor/TinyMCE 等),图片路径通常存储为: +- **绝对路径**(完整 URL):直接可用 +- **相对路径**(如 `/public/upload/...`):在 H5 页面中同样可用(浏览器自动补全域名) + +商品相册(`goods['photos']`)**未在 ticket_detail.html 中渲染**。如需展示,应使用: +```html + +
+ + + +
+ +``` + +## 评估结论 + +| 项目 | 状态 | 说明 | +|------|------|------| +| 商品详情内容 | ✅ 正常 | `$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、段落间距等)