docs: 新增 vr_ticket openid 问题与主题机制调研文档
- docs/WECHAT_OPENID_ERROR_REPORT.md: vr_ticket 购票流程 openid 未找到 Bug 排错报告 - docs/theme_research.md: 服务端配置联动 & 主题/配色机制 调研报告master
parent
80ea6bfdef
commit
eb45c977d6
|
|
@ -0,0 +1,108 @@
|
||||||
|
# ShopXO vr_ticket 购票流程 "openid 未找到" 问题分析报告
|
||||||
|
|
||||||
|
> 更新:2026-07-02 — 已锁定真实根因,非标准 ShopXO 问题,是 vr_ticket 二次开发的 Bug
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📋 完整出错调用链路(vr_ticket 购票 → 一键获取手机号)
|
||||||
|
|
||||||
|
```
|
||||||
|
用户点击「一键获取手机号」
|
||||||
|
→ onGetPhoneNumber(e)
|
||||||
|
→ uni.request POST /user/onekeyusermobilebind
|
||||||
|
{ code, encryptedData, iv, openid: user['weixin_openid'] || '' } ← openid 是空字符串!
|
||||||
|
→ AppMiniUserService::AppMiniOnekeyUserMobileBind()
|
||||||
|
→ AppMiniOnekeyUserMobileDecrypt()
|
||||||
|
→ ParamsChecked(openid) ← 校验失败!
|
||||||
|
→ 返回错误: "openid 未找到"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔴 根因(Bug 定位)
|
||||||
|
|
||||||
|
**文件:** `pages/goods-vr-ticket-order/goods-vr-ticket-order.vue` 第 553-554 行
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const user = this.app.globalData.get_login_cache_info() || {};
|
||||||
|
const clientType = this.app.globalData.application_client_type(); // → 'weixin'
|
||||||
|
...
|
||||||
|
openid: user[clientType + '_openid'] || '',
|
||||||
|
// 'weixin' + '_openid' = 'weixin_openid'
|
||||||
|
// 但 cache_user_login_key 里存的是 'openid'(无前缀)
|
||||||
|
// → undefined → '' → 后端校验失败
|
||||||
|
```
|
||||||
|
|
||||||
|
**三段论:**
|
||||||
|
|
||||||
|
| 层级 | 实际情况 |
|
||||||
|
|------|---------|
|
||||||
|
| 前端读取的缓存 | `cache_user_login_key`(通过 `get_login_cache_info()`) |
|
||||||
|
| 该缓存存储的字段 | `openid`(来自 ShopXO 后端返回的原始字段名) |
|
||||||
|
| 前端构造的字段名 | `user['weixin_openid']` = `undefined` → `''` |
|
||||||
|
| 后端校验 | `openid` 不能为空 → **报错 "openid 未找到"** |
|
||||||
|
|
||||||
|
### 为什么 `cache_user_login_key` 里存的是 `openid` 而不是 `weixin_openid`?
|
||||||
|
|
||||||
|
ShopXO 后端 `WeixinUserAuth` [AppMiniUserService.php:212] 的返回结构:
|
||||||
|
```php
|
||||||
|
// WeixinUserAuth 返回的是 $ret['data'],其中 'openid' 来自微信原始响应
|
||||||
|
// 数据库存的是 weixin_openid,但 API 返回的字段名是 'openid'
|
||||||
|
$ret = UserService::AuthUserProgram($params, 'weixin_openid');
|
||||||
|
// ↑ 存库用的字段 ↑ 返回用的字段
|
||||||
|
// $ret['data']['openid'] = 微信原始 openid
|
||||||
|
```
|
||||||
|
|
||||||
|
所以 `cache_user_login_key` 存的是 `{ ..., openid: '微信原始openid', ... }`,
|
||||||
|
前端用 `user['weixin_openid']` 去取 → 取不到 → 空字符串 → 报错。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔧 修复方案
|
||||||
|
|
||||||
|
### 修改文件:`pages/goods-vr-ticket-order/goods-vr-ticket-order.vue`
|
||||||
|
|
||||||
|
**第 553 行**,把:
|
||||||
|
```javascript
|
||||||
|
const user = this.app.globalData.get_login_cache_info() || {};
|
||||||
|
const clientType = this.app.globalData.application_client_type();
|
||||||
|
...
|
||||||
|
openid: user[clientType + '_openid'] || '',
|
||||||
|
```
|
||||||
|
|
||||||
|
改为:
|
||||||
|
```javascript
|
||||||
|
// 优先从登录缓存读 openid(ShopXO 存的是 'openid' 无前缀)
|
||||||
|
const loginData = this.app.globalData.get_login_cache_info() || {};
|
||||||
|
// 备用:从用户信息缓存读 weixin_openid(已登录用户)
|
||||||
|
const userData = this.app.globalData.get_user_cache_info() || {};
|
||||||
|
const openid = loginData.openid || userData.weixin_openid || '';
|
||||||
|
|
||||||
|
// 没有 openid → 提示用户先完成微信登录
|
||||||
|
if (!openid) {
|
||||||
|
this.app.globalData.showToast('请先完成微信登录后再授权手机号');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uni.request({
|
||||||
|
...
|
||||||
|
openid: openid,
|
||||||
|
...
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### 可选:后端放宽 openid 校验(针对微信小程序)
|
||||||
|
|
||||||
|
`AppMiniUserService.php` 的 `AppMiniOnekeyUserMobileDecrypt` 中,微信分支其实只需要 `code` 就能换手机号,`openid` 只用于用户关联。如果想解耦,可以让微信分支的 `openid` 校验变为 warn 级别而非 error——但这需要改动 ShopXO 核心代码,不推荐。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📁 相关代码文件索引
|
||||||
|
|
||||||
|
| 文件 | 作用 |
|
||||||
|
|------|------|
|
||||||
|
| `vr-shopxo-uniapp/pages/goods-vr-ticket-order/goods-vr-ticket-order.vue:553` | **Bug 所在**:错误读取缓存字段 |
|
||||||
|
| `vr-shopxo-plugin/shopxo/app/service/AppMiniUserService.php:824` | 后端 `AppMiniOnekeyUserMobileDecrypt` openid 校验 |
|
||||||
|
| `vr-shopxo-plugin/shopxo/app/service/AppMiniUserService.php:212` | `WeixinUserAuth` 返回字段分析 |
|
||||||
|
| `vr-shopxo-uniapp/App.vue:671` | `get_login_cache_info()` 读取 `cache_user_login_key` |
|
||||||
|
| `vr-shopxo-uniapp/App.vue:639` | `get_user_cache_info()` 读取 `cache_user_info_key` |
|
||||||
|
|
@ -0,0 +1,219 @@
|
||||||
|
# vr-shopxo-uniapp —— 服务端配置联动 & 主题/配色机制 调研报告
|
||||||
|
|
||||||
|
> 调研对象:`/Users/bigemon/WorkSpace/vr-shopxo-uniapp`(uniapp + 多端 shopxo 客户端)
|
||||||
|
> 目的:在为后续新增 VR 系列组件/页面提供可复用机制之前,把现有的「服务端下发 → 主题/配色 → 页面消费」整条链路说清楚。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 一、TL;DR
|
||||||
|
|
||||||
|
项目使用「**本地默认值 + 服务端一次下发 + 本地缓存**」三段式做配置联动,主题/配色只是这套机制里的一个特殊子集:
|
||||||
|
|
||||||
|
1. 客户端本地写死默认值(`App.vue globalData.data`,例如 `default_theme: 'red'`、`default_language: 'zh'`、`currency_symbol`、`static_url` 等),保证首屏可立即渲染。
|
||||||
|
2. 启动时调用 `init_config()` 远程拉取 `/api.php?s=common/base` 接口的完整配置 JSON,落盘 `uni.setStorageSync('cache_config_info_key', data)`。
|
||||||
|
3. 各业务模块通过 `app.globalData.get_config('a.b.c')` 从本地缓存读字段,多级 key 用 `.` 分割。
|
||||||
|
4. 主题是其中最特殊的一个:**它在响应结果中以 `plugins_themestyle_data` 字符串下发**(如 `red/yellow/...`),落地后由 `App.vue` 把它写到 `uni.storage.theme`,并触发 `set_tabbar(theme)` 重绘原生 tabbar。
|
||||||
|
5. 页面通过两个全局 helper 消费主题:
|
||||||
|
- `get_theme_value_view()` → 返回 `theme-red` 这种 class 字符串,挂到 `<view :class>` 的最外层;
|
||||||
|
- `get_theme_color()` → 返回该主题的主色/浅主色 hex,喂给 `:color="theme_color"` 这类需要动态取色的组件。
|
||||||
|
6. CSS 端则用 `common/css/theme.css` 里为 8 套主题(`red/yellow/black/green/orange/blue/brown/purple`)预制的工具类(`.bg-main / .cr-main / .br-main / .bg-main-light / .cr-main-light` ...)按主题自动换肤。
|
||||||
|
7. 静态图片资源也是按主题分目录组织:`static/images/{theme}/...`,tabbar 图、插件业务图都按主题选;插件专用主题资源走 `get_static_url(type, is_plugins)` 拼成 `static/app/{theme}/{type}/`。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 二、源码位置(全部已验证存在)
|
||||||
|
|
||||||
|
| 角色 | 位置 | 说明 |
|
||||||
|
|---|---|---|
|
||||||
|
| 默认配置中心 | [App.vue L6–L170](file:///Users/bigemon/WorkSpace/vr-shopxo-uniapp/App.vue#L6-L170) | `globalData.data` 中的所有默认值:`request_url`、`static_url`、`default_theme`、`default_language`、`currency_symbol`、`system_tabbar` 等 |
|
||||||
|
| 缓存 key 常量 | [App.vue L149–L166](file:///Users/bigemon/WorkSpace/vr-shopxo-uniapp/App.vue#L149-L166) | `cache_config_info_key: 'cache_config_info_key'` 等 |
|
||||||
|
| 启动入口 | [App.vue L3397](file:///Users/bigemon/WorkSpace/vr-shopxo-uniapp/App.vue#L3397) | `this.globalData.init_config()` 在 `onLaunch` 调用 |
|
||||||
|
| 远程拉取实现 | [App.vue L1558–L1627](file:///Users/bigemon/WorkSpace/vr-shopxo-uniapp/App.vue#L1558-L1627) | `init_config(num, object, method, params)`:先读本地缓存 → POST `/common/base` 拿全量 → 写入缓存 → 回调 |
|
||||||
|
| 通用读配置 | [App.vue L1509–L1549](file:///Users/bigemon/WorkSpace/vr-shopxo-uniapp/App.vue#L1509-L1549) | `get_config(key, default_value)`:支持 `a.b.c` 多级 key,缺失回落 `default_value` 或 `globalData` 默认值 |
|
||||||
|
| 主题落地 | [App.vue L1630–L1650](file:///Users/bigemon/WorkSpace/vr-shopxo-uniapp/App.vue#L1630-L1650) | `init_config_result_handle`:调用 `set_theme_value(data.plugins_themestyle_data)` 与 `set_tabbar(...)` |
|
||||||
|
| 主题读/写 | [App.vue L2762–L2780](file:///Users/bigemon/WorkSpace/vr-shopxo-uniapp/App.vue#L2762-L2780) | `get_theme_value / set_theme_value`:读写 `uni.storage.theme` |
|
||||||
|
| 主题色值表 | [App.vue L2725–L2755](file:///Users/bigemon/WorkSpace/vr-shopxo-uniapp/App.vue#L2725-L2755) | `get_theme_color(theme, is_light)`:返回主色 / 浅主色 hex |
|
||||||
|
| 主题 class | [App.vue L2758–L2760](file:///Users/bigemon/WorkSpace/vr-shopxo-uniapp/App.vue#L2758-L2760) | `get_theme_value_view()` → `theme-red` |
|
||||||
|
| Tabbar 主题化 | [App.vue L2783–L2837](file:///Users/bigemon/WorkSpace/vr-shopxo-uniapp/App.vue#L2783-L2837) | `set_tabbar(theme)`:`uni.setTabBarStyle` + `uni.setTabBarItem` 切换选中色/图标 |
|
||||||
|
| 静态资源主题路径 | [App.vue L2029–L2050](file:///Users/bigemon/WorkSpace/vr-shopxo-uniapp/App.vue#L2029-L2050) | `get_static_url(type, is_plugins)`:返回 `static/app/{theme}/{type}/` |
|
||||||
|
| 主题 CSS(8 套) | [common/css/theme.css](file:///Users/bigemon/WorkSpace/vr-shopxo-uniapp/common/css/theme.css) | 每套主题约 80 行工具类:`bg-main / bg-main-light / bg-main-pair / cr-* / br-* / .nav-active-line` ... |
|
||||||
|
| 主题图片资源 | `static/images/{black\|blue\|brown\|green\|orange\|purple\|red\|yellow}/` | tabbar 图、按钮背景等按主题分目录 |
|
||||||
|
| 接入示例页面 | `pages/{index,cart,goods-category,...}/...`、`pages/plugins/**` | 全部使用 `theme_view: app.globalData.get_theme_value_view()` + `theme_color: app.globalData.get_theme_color()` 模式 |
|
||||||
|
| VR 钱包插件 | `pages/plugins/vr-ticket-wallet/` | 已存在的 VR 系列插件,结构上跟主题机制完全契合 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 三、完整执行链路(可视化)
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
autonumber
|
||||||
|
participant App as App.vue onLaunch
|
||||||
|
participant Cache as uni.storage
|
||||||
|
participant API as 服务端 /api.php?s=common/base
|
||||||
|
participant Page as 业务页面 / 组件
|
||||||
|
participant CSS as common/css/theme.css
|
||||||
|
|
||||||
|
Note over App: 首屏立即使用 globalData.data 默认值渲染
|
||||||
|
App->>App: init_config()
|
||||||
|
App->>Cache: getStorageSync(cache_config_info_key)
|
||||||
|
alt 有缓存(上次启动留下的)
|
||||||
|
App->>App: init_config_result_handle(cache)
|
||||||
|
end
|
||||||
|
App->>API: POST common/base (is_key=1)
|
||||||
|
API-->>App: { code:0, data:{ plugins_themestyle_data, config.*, plugins_*, ... } }
|
||||||
|
App->>Cache: setStorageSync(cache_config_info_key, data)
|
||||||
|
App->>App: init_config_result_handle(data)
|
||||||
|
App->>Cache: setStorageSync('theme', data.plugins_themestyle_data)
|
||||||
|
App->>App: set_tabbar(theme) — uni.setTabBarStyle/Item
|
||||||
|
App-->>Page: 通知 common_data_init_config_back_list 回调
|
||||||
|
Page->>Cache: get_config('xxx') / get_theme_color() / get_theme_value_view()
|
||||||
|
Page->>Page: data() 重新计算 theme_view/theme_color
|
||||||
|
Page->>CSS: <view :class="theme_view"> 触发 .theme-red .bg-main 等规则
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 四、关键 API 一览(VR 组件直接复用即可)
|
||||||
|
|
||||||
|
### 1. `app.globalData.get_config(key, default_value?)`
|
||||||
|
```js
|
||||||
|
// 用法
|
||||||
|
app.globalData.get_config('config.home_site_name', '默认名')
|
||||||
|
app.globalData.get_config('plugins_themestyle_data') // → 'red'
|
||||||
|
app.globalData.get_config('plugins_base.thirdpartylogin.data')
|
||||||
|
```
|
||||||
|
- 数据源:`uni.storage['cache_config_info_key']`
|
||||||
|
- 支持 `.` 分级 key,命中失败会回落到 `default_value` 或 `globalData.data` 同名字段(如 `currency_symbol`)。
|
||||||
|
|
||||||
|
### 2. `app.globalData.get_theme_value_view()`
|
||||||
|
- 返回 `theme-red` / `theme-yellow` / ... 字符串,绑定到根 `<view :class>`。
|
||||||
|
- 自动应用 `theme.css` 里对应主题块的所有工具类。
|
||||||
|
|
||||||
|
### 3. `app.globalData.get_theme_color(theme?, is_light?)`
|
||||||
|
- 返回主题主色 hex 或浅主色 hex。
|
||||||
|
- 用于 `:color="theme_color"`、`<iconfont :color="theme_color">`、`<checkbox :color="theme_color">` 等不能走 class 的场景。
|
||||||
|
- 注意:**这是**「代码内 hardcode 的 8 套色值表」**与 `theme.css` 里的色值保持一致**,所以两套数据源要同时改(这是一个已知的耦合点,见下)。
|
||||||
|
|
||||||
|
### 4. `app.globalData.get_static_url(type, is_plugins)`
|
||||||
|
- 返回 `static_url + static/app/{theme}/{type}/` 或 `static_url + static/{theme}/{type}/`
|
||||||
|
- VR 组件需要主题化背景图时,直接 `image :src="app.globalData.get_static_url('vr-banner')"` 即可。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 五、对服务端字段的依赖约定(要清楚)
|
||||||
|
|
||||||
|
| 字段 | 出现位置 | 含义 | 默认值来源 |
|
||||||
|
|---|---|---|---|
|
||||||
|
| `plugins_themestyle_data` | 服务端下发的最外层字符串 | 主题名(8 选 1) | `App.vue` `default_theme = 'red'` |
|
||||||
|
| `config.*` | 站点设置(站点名、logo、协议 URL 等) | 站点级公共配置 | `App.vue` `application_title`、`home_site_*` |
|
||||||
|
| `plugins_*` | 插件配置 | 各业务插件开关/数据 | 各插件 `App.vue` 内的 hardcode |
|
||||||
|
| `is_key=1` | `common/base` 请求参数 | 服务端只返回 key 不返回大字段(精简包体) | — |
|
||||||
|
| 请求 URL 参数 `&theme=red` | `App.vue` L398–L399 | 启动时把当前主题回传给服务端,方便服务端按主题返回差异化数据(如 banner) | — |
|
||||||
|
|
||||||
|
> ⚠️ 现有实现默认假定后端会把主题字符串放在 `plugins_themestyle_data`,**新增 VR 系列组件如果要"自带主题"**(例如 VR 票券背景强制主题色),也建议沿用同一个 key,服务端不改;VR 组件只需要在 data() 里多写两个字段 `theme_view / theme_color`,零成本接入。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 六、页面侧固定模式("主题消费模板")
|
||||||
|
|
||||||
|
所有 40 个 `pages/plugins/**` 页面、首页、购物车、商品详情等都遵循完全相同的模式:
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<template>
|
||||||
|
<view :class="theme_view + ' page-fade-in'">
|
||||||
|
<!-- iconfont 等需要动态取色的 -->
|
||||||
|
<iconfont name="icon-add" :color="theme_color" />
|
||||||
|
<!-- class 自动换肤 -->
|
||||||
|
<view class="bg-main cr-main-light">按钮</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const app = getApp();
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
theme_view: app.globalData.get_theme_value_view(),
|
||||||
|
theme_color: app.globalData.get_theme_color(),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
onShow() {
|
||||||
|
// 极少数页面需要响应主题变化(切换主题/后台切回时)
|
||||||
|
this.setData({
|
||||||
|
theme_view: app.globalData.get_theme_value_view(),
|
||||||
|
theme_color: app.globalData.get_theme_color(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
`common/css/theme.css` 在 `App.vue` 顶部通过 `@import './common/css/theme.css';`([App.vue L3426](file:///Users/bigemon/WorkSpace/vr-shopxo-uniapp/App.vue#L3426))全局生效。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 七、给后续 VR 系列组件/页面的扩展建议
|
||||||
|
|
||||||
|
> 既然现有机制已经够用,**不要新增第二套主题系统**,否则会出现「同一页同时存在两种主题类名」的混乱。统一复用现有 8 套即可。
|
||||||
|
|
||||||
|
### 1. 接入"主题感知"成本极低(推荐做法)
|
||||||
|
|
||||||
|
对每个新增 VR 组件/页面:
|
||||||
|
|
||||||
|
- 在最外层 `<view :class="theme_view + ' page-fade-in'">`,享受 class 换肤。
|
||||||
|
- 在 `data()` 里加 `theme_view` 与 `theme_color` 两字段,从 `app.globalData.get_theme_*()` 取值。
|
||||||
|
- 需要用图片的地方,资源放 `static/app/{theme}/vr-xxx/`,组件内用 `get_static_url('vr-xxx')` 拼路径。
|
||||||
|
- 这样**不需要改 App.vue、不需要改 CSS**,服务端也无需额外配置。
|
||||||
|
|
||||||
|
### 2. 给"VR 主题"做差异化时(可选进阶)
|
||||||
|
|
||||||
|
如果某些 VR 场景需要「**主题色 + 品牌专属渐变**」叠加,建议两步:
|
||||||
|
|
||||||
|
- **服务端扩展字段**:在 `common/base` 返回里新增 `plugins_vr_theme_extras`(或在 `plugins_*` 下挂一个 VR 专属段),下发一组 hex 或图片 URL。
|
||||||
|
- **客户端扩展 helper**:在 `App.vue` 里新增 `get_vr_theme_assets(name)` 方法,从 `get_config('plugins_vr_theme_extras.xxx')` 取值。
|
||||||
|
- **CSS 不动**:所有新视觉差异都走「行内样式 / 数据驱动」,避免污染 `theme.css`。
|
||||||
|
|
||||||
|
### 3. 几个需要避坑的点
|
||||||
|
|
||||||
|
| 坑 | 说明 | 解法 |
|
||||||
|
|---|---|---|
|
||||||
|
| 主题色双源 | `App.vue get_theme_color()` 的 hex 表与 `theme.css` 里的 hex 重复 | 加新主题时**两处都要改**,建议改为「`theme.css` 单一来源,JS 通过 `getComputedStyle` 读 `--main-color`」——但这是大重构,需单独评估 |
|
||||||
|
| 主题切换不广播 | 当前没有事件总线,`set_theme_value` 后页面需要自己 `onShow` 重读 | 新增 VR 组件时若需要"运行中切主题也能立即生效",建议引入 `uni.$emit / uni.$on('theme-change', ...)`,在 `set_theme_value` 内 emit |
|
||||||
|
| 缓存时机 | 首屏渲染走默认值,远程配置到达后页面不会自动 re-render(数据写死到 `data()` 里) | VR 关键业务页面建议在 `onShow` 重读 `theme_view/theme_color`;列表类在 `onPullDownRefresh` 重新拉数据 |
|
||||||
|
| 离线/服务端挂 | `init_config` 已做 20 次重试 + 本地缓存 fallback([App.vue L1609](file:///Users/bigemon/WorkSpace/vr-shopxo-uniapp/App.vue#L1609)) | VR 组件不要自己再发 `common/base` 请求,**直接调 `get_config` 即可** |
|
||||||
|
|
||||||
|
### 4. 建议的最小 VR 主题规范(落地清单)
|
||||||
|
|
||||||
|
如果要为 VR 系列另起一份"主题约定文档",建议覆盖:
|
||||||
|
|
||||||
|
1. **页面骨架**:必含 `:class="theme_view"`,推荐放在页面最外层 `<view>`。
|
||||||
|
2. **取色**:动态色一律 `app.globalData.get_theme_color()`;静态色走 `theme.css` 工具类(`.bg-main` / `.cr-main` / `.bg-main-light` 等)。
|
||||||
|
3. **图片**:主题相关图放 `static/app/{theme}/vr-.../`,通过 `get_static_url('vr-...')` 取;与主题无关的图(如商品主图)放 `static/images/common/`。
|
||||||
|
4. **TabBar**:VR 系列新增 tab 项需要在 `App.vue` 的 `set_tabbar()` switch 里补一项 case(参考 [App.vue L2809–L2835](file:///Users/bigemon/WorkSpace/vr-shopxo-uniapp/App.vue#L2809-L2835))。
|
||||||
|
5. **服务端字段**:优先复用 `plugins_themestyle_data`,**不要**新增 `vr_theme` 等并行字段,否则主题永远只对一半生效。
|
||||||
|
6. **离线降级**:依赖任何 `get_config('...')` 字段时都加默认参数;不要假设 `plugins_vr_xxx` 一定存在。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 八、待用户确认的设计决策
|
||||||
|
|
||||||
|
> [!IMPORTANT]
|
||||||
|
> 是否要把"VR 主题"做成「复用 8 套主题」还是「新增第 9 套 VR 专属主题(vr-cyan / vr-purple-grad 之类)」?
|
||||||
|
|
||||||
|
> [!IMPORTANT]
|
||||||
|
> 是否要在本次顺便把"主题色双源(JS hex 表 vs theme.css)"统一为 CSS 变量单一来源?工作量约 1 个工作日。
|
||||||
|
|
||||||
|
> [!TIP]
|
||||||
|
> 是否需要新增 `theme-change` 事件总线,让运行中切主题能即时通知所有页面?
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 九、参考阅读清单
|
||||||
|
|
||||||
|
- [App.vue](file:///Users/bigemon/WorkSpace/vr-shopxo-uniapp/App.vue) —— 配置中心 / 主题注册 / TabBar
|
||||||
|
- [common/css/theme.css](file:///Users/bigemon/WorkSpace/vr-shopxo-uniapp/common/css/theme.css) —— 8 套主题的工具类
|
||||||
|
- [static/images/](file:///Users/bigemon/WorkSpace/vr-shopxo-uniapp/static/images) —— 按主题分目录的图片资源
|
||||||
|
- [pages/plugins/vr-ticket-wallet/records/index.vue](file:///Users/bigemon/WorkSpace/vr-shopxo-uniapp/pages/plugins/vr-ticket-wallet/records/index.vue) —— 已存在的 VR 插件页面(最佳参考模板)
|
||||||
|
- [pages/index/index.vue](file:///Users/bigemon/WorkSpace/vr-shopxo-uniapp/pages/index/index.vue) —— 标准主题消费模板
|
||||||
|
- [pages/buy/buy.vue](file:///Users/bigemon/WorkSpace/vr-shopxo-uniapp/pages/buy/buy.vue) —— 同时使用 `theme_view` + `theme_color` 的典型页面
|
||||||
Loading…
Reference in New Issue