diff --git a/docs/superpowers/specs/2026-07-30-tickets-client-design.md b/docs/superpowers/specs/2026-07-30-tickets-client-design.md new file mode 100644 index 0000000..4cdb978 --- /dev/null +++ b/docs/superpowers/specs/2026-07-30-tickets-client-design.md @@ -0,0 +1,245 @@ +# 游客端票务页面迁移设计 + +## 背景与目标 + +将 `/Users/gleen/OneFeel/xiaoqi-seji/需求交付物/前端原型.html` 中的“游客端预览”迁移到 `src/pages-service/tickets/`,并以用户提供的截图作为首页视觉基准。 + +本次实现覆盖游客端完整链路: + +1. 景区票务首页 +2. 商品详情 +3. 预订选择与实名游客填写 +4. 常用出行人管理 +5. 模拟支付结果 +6. 我的订单与订单详情 +7. 退款申请与退款结果 +8. 我的凭证 + +后台管理端和员工核销端不在本次范围内。页面不接真实接口,使用结构化默认数据演示完整交互。刷新或重新进入页面后恢复默认数据。 + +## 技术方案 + +采用“单 uni-app 路由 + 同目录组件化状态流”。 + +- `src/pages-service/tickets/index.vue` 是唯一页面入口,负责共享业务状态、页面状态切换、来源记录和返回规则。 +- 复杂视图拆为 `src/pages-service/tickets/components/` 下的 Vue 组件。 +- 默认数据集中放在 `src/pages-service/tickets/data.js`。 +- 价格、年龄、订单和退款等可独立验证的纯逻辑放在 `src/pages-service/tickets/model.mjs`。 +- 在 `src/pages.json` 的 `pages-service` 分包注册 `tickets/index`,使用自定义导航。 + +页面内部不为每个步骤新增 uni-app 路由,以避免跨页传递预订草稿、游客和订单状态。首页左上角在没有内部历史时调用 `uni.navigateBack`;其余页面按进入来源返回。 + +## 组件边界 + +| 模块 | 职责 | +| --- | --- | +| `index.vue` | 页面状态编排、共享默认数据、返回策略、下单和退款命令 | +| `TicketHome.vue` | 景区头图、分类导航、商品分组、商品卡片与分类滚动联动 | +| `ProductDetail.vue` | 商品图片、价格、权益、使用说明、购买须知和预订入口 | +| `BookingFlow.vue` | 日期、场次、数量、游客、联系人、保险与提交订单 | +| `TravelerManager.vue` | 常用出行人列表、新增与选择 | +| `PaymentResult.vue` | 模拟支付成功信息以及订单、凭证入口 | +| `OrderList.vue` | 默认订单和本次新订单列表 | +| `OrderDetail.vue` | 状态、场次、游客、权益、费用、凭证与退款入口 | +| `RefundFlow.vue` | 退款权益选择、原因、预计金额和提交结果 | +| `CredentialsView.vue` | 第三方票务凭证与自营园内权益凭证 | + +简单的商品卡片、状态标签和空状态可以继续拆为小组件,但不引入与当前需求无关的通用组件体系。 + +## 页面与交互 + +### 票务首页 + +- 顶部使用景区实景占位图,展示返回、常用出行人、我的订单、景区名称、等级、地址、联系电话和导航入口。 +- 电话按钮调用 `uni.makePhoneCall`;导航按钮调用 `uni.openLocation`,若缺少平台能力则提示用户。 +- 头图下方是一个统一圆角内容壳:左侧为固定分类栏,右侧为独立滚动商品区。 +- 默认分类为景点门票,包含景点门票、VIP 速通、直通车、门票+观光车、一日游和玩乐体验。 +- 点击左侧分类时将右侧对应分组标题滚动到内容区顶部;右侧滚动进入新分组时同步高亮左侧分类。 +- 点击商品图片或标题进入详情;点击“选购”直接进入预订选择。 + +### 商品详情与预订 + +- 详情展示图片、短标题、价格、销量、标签、费用包含、费用不含、详情和购买须知。 +- 预订先选择日期和场次,再选择数量、游客、联系人和可选保险。 +- 实名商品要求游客数量不少于购买数量;年龄限制商品根据证件信息和使用日期校验年龄。 +- 常用出行人可以直接勾选,也可以手动新增。新增数据只保留在当前页面会话。 +- 提交前重新校验日期、场次、库存、游客、联系方式和年龄规则。 + +### 支付、订单、退款与凭证 + +- “提交订单并模拟支付”不会调用支付接口;它会生成一条已支付订单并扣减当前会话内的展示库存。 +- 订单详情展示订单状态、使用场次、游客、联系手机、履约权益、费用、退款规则和支付时间。 +- 支持选择可退款权益、选择退款原因并提交模拟退款。提交后更新订单、权益和退款状态。 +- 第三方履约权益显示第三方票务凭证;自营权益显示园内权益凭证。二维码使用本地可渲染的视觉占位,不依赖远程二维码服务。 + +## 默认数据结构 + +### 景区 + +```js +{ + id, + name, + level, + heritage, + address, + phone, + latitude, + longitude, + heroImage +} +``` + +### 商品分组 + +```js +{ + id, + name, + subtitle, + description, + tag, + productIds, + sortOrder, + enabled +} +``` + +### 商品 + +```js +{ + id, + name, + shortTitle, + category, + description, + salePrice, + marketPrice, + stock, + sold, + status, + highlights, + ageRule, + included, + excluded, + detail, + purchaseNotice, + images, + reservation: { + requiresDate, + advanceDays, + requiresTimeSlot, + timeSlots, + realNameRequired, + idTypes, + maxQuantity, + contactPhoneRequired + }, + entitlements +} +``` + +### 出行人 + +```js +{ + id, + name, + idType, + idNumber, + phone +} +``` + +### 订单 + +```js +{ + id, + orderNo, + productId, + productName, + status, + visitDate, + timeSlot, + quantity, + travelers, + contactName, + phone, + productAmount, + insurance, + paidAmount, + paidAt, + entitlements, + gateCredential, + selfCredential, + refundStatus, + logs +} +``` + +### 页面状态 + +页面状态使用固定枚举: + +```js +home +detail +booking +travelers +paid +orders +orderDetail +refundApply +refundResult +credentials +``` + +`index.vue` 同时记录当前商品、当前订单、预订草稿和页面来源,避免组件自行修改跨页面业务数据。子组件只通过 props 接收数据,并通过 emits 发出明确事件。 + +## 视觉与样式 + +- 所有新增或调整的布局使用可静态扫描的 Tailwind CSS 完整类名。 +- 不新增 SCSS 布局类,不通过字符串拼接生成 Tailwind 类名。 +- 首页复刻截图中的大幅景区头图、深色渐变遮罩、白色景区信息、统一圆角票务内容壳、左侧绿色分类高亮、浅灰商品区域、白色商品卡片和橙色购买按钮。 +- 内页延续白色卡片、浅灰背景、蓝色信息标签和橙色主操作的视觉语言。 +- 景区头图和所有商品图片统一使用: + `https://one-feel-bucket.oss-cn-guangzhou.aliyuncs.com/oneFeel/2082673896938569729.jpg` +- 页面适配微信小程序常见手机宽度,并保留安全区底部间距。 + +## 校验、异常和空状态 + +- 未选择日期或场次、购买数量无效、游客数量不足、实名信息不完整、年龄不符、联系人缺失、退款权益未选择时禁止提交,并用 `uni.showToast` 提供明确提示。 +- 日期或场次无库存时显示售罄并不可选。 +- 商品、订单、出行人均提供空状态,避免默认数组被清空时页面失去反馈。 +- 电话、地图等平台能力失败时不阻断主流程,只显示失败提示。 +- 默认数据不需要网络加载,因此不设计加载失败重试。 + +## 测试与验收 + +采用 Node 内置测试能力验证 `model.mjs` 中的纯逻辑,并遵循测试先行: + +- 商品总价与保险价格计算 +- 身份证年龄与适用年龄判断 +- 预订草稿必填项验证 +- 模拟订单生成及权益拆分 +- 退款金额计算与状态更新 +- 页面状态返回规则 + +实现后执行: + +```bash +node --test src/pages-service/tickets/model.test.mjs +git diff --check +yarn build:mp-weixin +``` + +人工验收完整点击链路:首页分类联动、商品详情、直接选购、日期场次、游客选择/新增、模拟支付、订单列表、订单详情、凭证、退款申请和退款结果。 + +## 非目标 + +- 不迁移后台管理端或员工核销端。 +- 不接入商品、库存、支付、出票、退款和地图等真实服务。 +- 不实现本地持久化、登录校验或跨设备订单同步。 +- 不修改 `src/uni_modules/` 或现有票务以外业务页面。