Add WeChat miniprogram web-view shell
This commit is contained in:
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
## 文档列表
|
## 文档列表
|
||||||
|
|
||||||
|
- [WECHAT_MINIPROGRAM_WEB_VIEW_SHELL_2026-05-03.md](./WECHAT_MINIPROGRAM_WEB_VIEW_SHELL_2026-05-03.md):记录微信小程序 `web-view` 壳的最小接入范围、需要填写的 H5 业务域名、微信后台配置和后续原生化边界。
|
||||||
- [PRODUCTION_DEPLOYMENT_PLAN_2026-05-02.md](./PRODUCTION_DEPLOYMENT_PLAN_2026-05-02.md):冻结单机生产部署目标,从旧一体化启动脚本切到 Nginx、systemd 托管 SpacetimeDB 与 Rust `api-server`,并记录生产 Jenkins 流水线拆分计划和首批部署骨架。
|
- [PRODUCTION_DEPLOYMENT_PLAN_2026-05-02.md](./PRODUCTION_DEPLOYMENT_PLAN_2026-05-02.md):冻结单机生产部署目标,从旧一体化启动脚本切到 Nginx、systemd 托管 SpacetimeDB 与 Rust `api-server`,并记录生产 Jenkins 流水线拆分计划和首批部署骨架。
|
||||||
- [PUZZLE_RUNTIME_FRONTEND_LOGIC_REHOME_2026-05-02.md](./PUZZLE_RUNTIME_FRONTEND_LOGIC_REHOME_2026-05-02.md):记录拼图正式平台入口移动、交换、合并、拆分和通关裁决收回前端即时运行态,排行榜、下一关和游玩记录继续由后端持久化处理。
|
- [PUZZLE_RUNTIME_FRONTEND_LOGIC_REHOME_2026-05-02.md](./PUZZLE_RUNTIME_FRONTEND_LOGIC_REHOME_2026-05-02.md):记录拼图正式平台入口移动、交换、合并、拆分和通关裁决收回前端即时运行态,排行榜、下一关和游玩记录继续由后端持久化处理。
|
||||||
- [RPG_FOUNDATION_DRAFT_ROLE_DOSSIER_TIMEOUT_FALLBACK_2026-05-02.md](./RPG_FOUNDATION_DRAFT_ROLE_DOSSIER_TIMEOUT_FALLBACK_2026-05-02.md):记录 `agent-foundation-*-dossier-batch-*` 无搜索 Responses 请求超时后的本地养成档案兜底,避免底稿主链被尾部角色润色阶段阻断。
|
- [RPG_FOUNDATION_DRAFT_ROLE_DOSSIER_TIMEOUT_FALLBACK_2026-05-02.md](./RPG_FOUNDATION_DRAFT_ROLE_DOSSIER_TIMEOUT_FALLBACK_2026-05-02.md):记录 `agent-foundation-*-dossier-batch-*` 无搜索 Responses 请求超时后的本地养成档案兜底,避免底稿主链被尾部角色润色阶段阻断。
|
||||||
|
|||||||
@@ -0,0 +1,74 @@
|
|||||||
|
# 微信小程序 web-view 壳接入记录
|
||||||
|
|
||||||
|
日期:`2026-05-03`
|
||||||
|
|
||||||
|
## 1. 目标
|
||||||
|
|
||||||
|
本次先用微信小程序 `web-view` 承载现有 H5,不重写 React/Vite 主前端,也不把 SpacetimeDB SDK 或业务规则搬进小程序端。
|
||||||
|
|
||||||
|
当前小程序壳只承担三件事:
|
||||||
|
|
||||||
|
1. 提供微信开发者工具可识别的 `miniprogram/` 工程根目录。
|
||||||
|
2. 用一个全屏 `web-view` 打开现有 H5 入口。
|
||||||
|
3. 给 H5 URL 附加来源标记,便于后续识别 `wechat_mini_program` 宿主。
|
||||||
|
|
||||||
|
## 2. 文件入口
|
||||||
|
|
||||||
|
| 文件 | 说明 |
|
||||||
|
| --- | --- |
|
||||||
|
| `project.config.json` | 指定 `miniprogramRoot: "miniprogram/"`。 |
|
||||||
|
| `miniprogram/app.json` | 小程序全局配置,注册 `pages/web-view/index`。 |
|
||||||
|
| `miniprogram/config.js` | 业务域名入口配置,需要部署时填写。 |
|
||||||
|
| `miniprogram/pages/web-view/index.*` | 最小 web-view 页面。 |
|
||||||
|
|
||||||
|
## 3. 需要手工填写的配置
|
||||||
|
|
||||||
|
在 `miniprogram/config.js` 中填写:
|
||||||
|
|
||||||
|
```js
|
||||||
|
const WEB_VIEW_ENTRY_URL = 'https://你的H5业务域名/';
|
||||||
|
```
|
||||||
|
|
||||||
|
约束:
|
||||||
|
|
||||||
|
1. 必须是 `https`。
|
||||||
|
2. 不能是 `localhost` 或 IP。
|
||||||
|
3. 域名需要在微信小程序后台配置为业务域名。
|
||||||
|
4. H5 页面里的 API、图片、音视频、iframe 等外链也要满足微信侧域名与证书要求。
|
||||||
|
|
||||||
|
`WEB_VIEW_SOURCE_QUERY` 默认附加:
|
||||||
|
|
||||||
|
```text
|
||||||
|
clientType=mini_program
|
||||||
|
clientRuntime=wechat_mini_program
|
||||||
|
```
|
||||||
|
|
||||||
|
后续如果 H5 或 `api-server` 需要更严格地区分来源,应优先使用请求头或登录态中的客户端身份字段,不要只信任 URL query。
|
||||||
|
|
||||||
|
## 4. 微信后台配置
|
||||||
|
|
||||||
|
至少需要在小程序后台配置:
|
||||||
|
|
||||||
|
1. `业务域名`:承载 H5 的域名。
|
||||||
|
2. `request 合法域名`:H5 里如果仍有小程序原生层直接请求 API,才需要配置;当前 web-view 壳本身不直接请求业务 API。
|
||||||
|
3. `socket 合法域名`:若后续小程序原生层直连 WebSocket 才需要;当前不启用。
|
||||||
|
|
||||||
|
当前仓库的 H5 仍建议通过同域 `/api/*` 访问 Rust `api-server`,避免在小程序和 H5 中分别维护跨域白名单。
|
||||||
|
|
||||||
|
## 5. 当前不做的事
|
||||||
|
|
||||||
|
本次不做原生小程序页面迁移,原因是当前主前端依赖:
|
||||||
|
|
||||||
|
1. React DOM 挂载、浏览器 history 和 `window.location`。
|
||||||
|
2. `localStorage` / `sessionStorage`。
|
||||||
|
3. 浏览器 `fetch` 与 `ReadableStream` SSE。
|
||||||
|
4. DOM、Canvas、Three.js 等浏览器渲染能力。
|
||||||
|
|
||||||
|
这些能力不能稳定原样运行在原生小程序宿主中。后续如要原生化,应新建小程序端宿主,复用 `packages/shared` 契约和 `api-server` BFF,而不是把 `src/` 整体搬过去。
|
||||||
|
|
||||||
|
## 6. 验收口径
|
||||||
|
|
||||||
|
1. 微信开发者工具打开项目根目录后,识别 `miniprogram/` 为小程序源码目录。
|
||||||
|
2. 未填写 `WEB_VIEW_ENTRY_URL` 时,页面显示配置提示,不出现空白页。
|
||||||
|
3. 填写已配置业务域名后,首页全屏打开 H5。
|
||||||
|
4. H5 内登录、SSE、图片资源等能力按 H5 自身部署域名和 `/api/*` 代理链路验证。
|
||||||
10
miniprogram/app.js
Normal file
10
miniprogram/app.js
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
App({
|
||||||
|
globalData: {
|
||||||
|
launchOptions: null,
|
||||||
|
},
|
||||||
|
|
||||||
|
onLaunch(options) {
|
||||||
|
// 中文注释:保留启动参数,后续如果要把分享路径映射到 H5 深链,可以从这里统一读取。
|
||||||
|
this.globalData.launchOptions = options;
|
||||||
|
},
|
||||||
|
});
|
||||||
20
miniprogram/app.json
Normal file
20
miniprogram/app.json
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"pages": [
|
||||||
|
"pages/web-view/index"
|
||||||
|
],
|
||||||
|
"window": {
|
||||||
|
"navigationBarTitleText": "百梦",
|
||||||
|
"navigationBarBackgroundColor": "#0b0f14",
|
||||||
|
"navigationBarTextStyle": "white",
|
||||||
|
"backgroundColor": "#0b0f14",
|
||||||
|
"backgroundTextStyle": "light"
|
||||||
|
},
|
||||||
|
"networkTimeout": {
|
||||||
|
"request": 60000,
|
||||||
|
"connectSocket": 60000,
|
||||||
|
"uploadFile": 60000,
|
||||||
|
"downloadFile": 60000
|
||||||
|
},
|
||||||
|
"style": "v2",
|
||||||
|
"sitemapLocation": "sitemap.json"
|
||||||
|
}
|
||||||
5
miniprogram/app.wxss
Normal file
5
miniprogram/app.wxss
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
page {
|
||||||
|
min-height: 100vh;
|
||||||
|
background: #0b0f14;
|
||||||
|
color: #f5f7fb;
|
||||||
|
}
|
||||||
15
miniprogram/config.js
Normal file
15
miniprogram/config.js
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
// 中文注释:这里填写已经在“小程序后台-开发-开发设置-业务域名”配置过的 H5 入口。
|
||||||
|
// 示例:https://game.example.com/
|
||||||
|
// 注意:必须是 https 域名,不能是 localhost、IP 地址或未备案域名。
|
||||||
|
const WEB_VIEW_ENTRY_URL = 'https://dev.genarrative.world/';
|
||||||
|
|
||||||
|
// 中文注释:给 H5 加一个来源标记,便于后续前端或后端识别这是微信小程序 web-view 宿主。
|
||||||
|
const WEB_VIEW_SOURCE_QUERY = {
|
||||||
|
clientType: 'mini_program',
|
||||||
|
clientRuntime: 'wechat_mini_program',
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
WEB_VIEW_ENTRY_URL,
|
||||||
|
WEB_VIEW_SOURCE_QUERY,
|
||||||
|
};
|
||||||
56
miniprogram/pages/web-view/index.js
Normal file
56
miniprogram/pages/web-view/index.js
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
const { WEB_VIEW_ENTRY_URL, WEB_VIEW_SOURCE_QUERY } = require('../../config');
|
||||||
|
|
||||||
|
function isConfiguredEntryUrl(value) {
|
||||||
|
const trimmed = String(value || '').trim();
|
||||||
|
return /^https:\/\/[^/]+/i.test(trimmed);
|
||||||
|
}
|
||||||
|
|
||||||
|
function appendQuery(url, query) {
|
||||||
|
const pairs = Object.keys(query)
|
||||||
|
.filter((key) => query[key])
|
||||||
|
.map(
|
||||||
|
(key) =>
|
||||||
|
`${encodeURIComponent(key)}=${encodeURIComponent(String(query[key]))}`,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (pairs.length === 0) {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${url}${url.includes('?') ? '&' : '?'}${pairs.join('&')}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveWebViewUrl() {
|
||||||
|
const entryUrl = String(WEB_VIEW_ENTRY_URL || '').trim();
|
||||||
|
if (!isConfiguredEntryUrl(entryUrl)) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return appendQuery(entryUrl, WEB_VIEW_SOURCE_QUERY);
|
||||||
|
}
|
||||||
|
|
||||||
|
Page({
|
||||||
|
data: {
|
||||||
|
webViewUrl: '',
|
||||||
|
},
|
||||||
|
|
||||||
|
onLoad() {
|
||||||
|
// 中文注释:web-view 只能打开已配置业务域名;未配置时展示本地提示,避免空白页误判。
|
||||||
|
this.setData({
|
||||||
|
webViewUrl: resolveWebViewUrl(),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
handleWebViewLoad(event) {
|
||||||
|
console.info('[web-view] loaded', event.detail);
|
||||||
|
},
|
||||||
|
|
||||||
|
handleWebViewError(event) {
|
||||||
|
console.error('[web-view] load failed', event.detail);
|
||||||
|
},
|
||||||
|
|
||||||
|
handleWebViewMessage(event) {
|
||||||
|
// 中文注释:H5 如需和小程序壳通信,可通过 wx.miniProgram.postMessage 发送轻量消息。
|
||||||
|
console.info('[web-view] message', event.detail);
|
||||||
|
},
|
||||||
|
});
|
||||||
3
miniprogram/pages/web-view/index.json
Normal file
3
miniprogram/pages/web-view/index.json
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"usingComponents": {}
|
||||||
|
}
|
||||||
15
miniprogram/pages/web-view/index.wxml
Normal file
15
miniprogram/pages/web-view/index.wxml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<block wx:if="{{webViewUrl}}">
|
||||||
|
<web-view
|
||||||
|
src="{{webViewUrl}}"
|
||||||
|
bindload="handleWebViewLoad"
|
||||||
|
binderror="handleWebViewError"
|
||||||
|
bindmessage="handleWebViewMessage"
|
||||||
|
/>
|
||||||
|
</block>
|
||||||
|
|
||||||
|
<view wx:else class="setup-screen">
|
||||||
|
<view class="setup-card">
|
||||||
|
<view class="setup-title">需要配置 H5 入口</view>
|
||||||
|
<view class="setup-text">请先在 miniprogram/config.js 填写 WEB_VIEW_ENTRY_URL。</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
33
miniprogram/pages/web-view/index.wxss
Normal file
33
miniprogram/pages/web-view/index.wxss
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
.setup-screen {
|
||||||
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 48rpx;
|
||||||
|
background: #0b0f14;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setup-card {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 560rpx;
|
||||||
|
padding: 36rpx;
|
||||||
|
border: 1rpx solid rgba(255, 255, 255, 0.14);
|
||||||
|
border-radius: 12rpx;
|
||||||
|
background: rgba(255, 255, 255, 0.06);
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setup-title {
|
||||||
|
font-size: 34rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 1.35;
|
||||||
|
color: #f5f7fb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setup-text {
|
||||||
|
margin-top: 16rpx;
|
||||||
|
font-size: 26rpx;
|
||||||
|
line-height: 1.55;
|
||||||
|
color: rgba(245, 247, 251, 0.72);
|
||||||
|
}
|
||||||
8
miniprogram/sitemap.json
Normal file
8
miniprogram/sitemap.json
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"rules": [
|
||||||
|
{
|
||||||
|
"action": "allow",
|
||||||
|
"page": "*"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
26
project.config.json
Normal file
26
project.config.json
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"setting": {
|
||||||
|
"es6": true,
|
||||||
|
"postcss": true,
|
||||||
|
"minified": true,
|
||||||
|
"uglifyFileName": false,
|
||||||
|
"enhance": true,
|
||||||
|
"packNpmRelationList": [],
|
||||||
|
"babelSetting": {
|
||||||
|
"ignore": [],
|
||||||
|
"disablePlugins": [],
|
||||||
|
"outputPath": ""
|
||||||
|
},
|
||||||
|
"useCompilerPlugins": false,
|
||||||
|
"minifyWXML": true
|
||||||
|
},
|
||||||
|
"compileType": "miniprogram",
|
||||||
|
"miniprogramRoot": "miniprogram/",
|
||||||
|
"simulatorPluginLibVersion": {},
|
||||||
|
"packOptions": {
|
||||||
|
"ignore": [],
|
||||||
|
"include": []
|
||||||
|
},
|
||||||
|
"appid": "wx3da23ea14ca66b65",
|
||||||
|
"editorSetting": {}
|
||||||
|
}
|
||||||
14
project.private.config.json
Normal file
14
project.private.config.json
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"libVersion": "3.15.2",
|
||||||
|
"projectname": "Genarrative",
|
||||||
|
"setting": {
|
||||||
|
"urlCheck": true,
|
||||||
|
"coverView": true,
|
||||||
|
"lazyloadPlaceholderEnable": false,
|
||||||
|
"skylineRenderEnable": false,
|
||||||
|
"preloadBackgroundData": false,
|
||||||
|
"autoAudits": false,
|
||||||
|
"showShadowRootInWxmlPanel": true,
|
||||||
|
"compileHotReLoad": true
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user