From 9baa515a752bc4ea0afbb5035730646dd290f34e Mon Sep 17 00:00:00 2001 From: kdletters Date: Sun, 3 May 2026 16:29:42 +0800 Subject: [PATCH] Add WeChat miniprogram web-view shell --- docs/technical/README.md | 1 + ...T_MINIPROGRAM_WEB_VIEW_SHELL_2026-05-03.md | 74 +++++++++++++++++++ miniprogram/app.js | 10 +++ miniprogram/app.json | 20 +++++ miniprogram/app.wxss | 5 ++ miniprogram/config.js | 15 ++++ miniprogram/pages/web-view/index.js | 56 ++++++++++++++ miniprogram/pages/web-view/index.json | 3 + miniprogram/pages/web-view/index.wxml | 15 ++++ miniprogram/pages/web-view/index.wxss | 33 +++++++++ miniprogram/sitemap.json | 8 ++ project.config.json | 26 +++++++ project.private.config.json | 14 ++++ 13 files changed, 280 insertions(+) create mode 100644 docs/technical/WECHAT_MINIPROGRAM_WEB_VIEW_SHELL_2026-05-03.md create mode 100644 miniprogram/app.js create mode 100644 miniprogram/app.json create mode 100644 miniprogram/app.wxss create mode 100644 miniprogram/config.js create mode 100644 miniprogram/pages/web-view/index.js create mode 100644 miniprogram/pages/web-view/index.json create mode 100644 miniprogram/pages/web-view/index.wxml create mode 100644 miniprogram/pages/web-view/index.wxss create mode 100644 miniprogram/sitemap.json create mode 100644 project.config.json create mode 100644 project.private.config.json diff --git a/docs/technical/README.md b/docs/technical/README.md index 3e503e5e..2971b8e8 100644 --- a/docs/technical/README.md +++ b/docs/technical/README.md @@ -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 流水线拆分计划和首批部署骨架。 - [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 请求超时后的本地养成档案兜底,避免底稿主链被尾部角色润色阶段阻断。 diff --git a/docs/technical/WECHAT_MINIPROGRAM_WEB_VIEW_SHELL_2026-05-03.md b/docs/technical/WECHAT_MINIPROGRAM_WEB_VIEW_SHELL_2026-05-03.md new file mode 100644 index 00000000..cb8aea17 --- /dev/null +++ b/docs/technical/WECHAT_MINIPROGRAM_WEB_VIEW_SHELL_2026-05-03.md @@ -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/*` 代理链路验证。 diff --git a/miniprogram/app.js b/miniprogram/app.js new file mode 100644 index 00000000..c060f609 --- /dev/null +++ b/miniprogram/app.js @@ -0,0 +1,10 @@ +App({ + globalData: { + launchOptions: null, + }, + + onLaunch(options) { + // 中文注释:保留启动参数,后续如果要把分享路径映射到 H5 深链,可以从这里统一读取。 + this.globalData.launchOptions = options; + }, +}); diff --git a/miniprogram/app.json b/miniprogram/app.json new file mode 100644 index 00000000..358c9e88 --- /dev/null +++ b/miniprogram/app.json @@ -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" +} diff --git a/miniprogram/app.wxss b/miniprogram/app.wxss new file mode 100644 index 00000000..8cc4f9c8 --- /dev/null +++ b/miniprogram/app.wxss @@ -0,0 +1,5 @@ +page { + min-height: 100vh; + background: #0b0f14; + color: #f5f7fb; +} diff --git a/miniprogram/config.js b/miniprogram/config.js new file mode 100644 index 00000000..68c7872f --- /dev/null +++ b/miniprogram/config.js @@ -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, +}; diff --git a/miniprogram/pages/web-view/index.js b/miniprogram/pages/web-view/index.js new file mode 100644 index 00000000..d09779c1 --- /dev/null +++ b/miniprogram/pages/web-view/index.js @@ -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); + }, +}); diff --git a/miniprogram/pages/web-view/index.json b/miniprogram/pages/web-view/index.json new file mode 100644 index 00000000..8835af06 --- /dev/null +++ b/miniprogram/pages/web-view/index.json @@ -0,0 +1,3 @@ +{ + "usingComponents": {} +} \ No newline at end of file diff --git a/miniprogram/pages/web-view/index.wxml b/miniprogram/pages/web-view/index.wxml new file mode 100644 index 00000000..31feec90 --- /dev/null +++ b/miniprogram/pages/web-view/index.wxml @@ -0,0 +1,15 @@ + + + + + + + 需要配置 H5 入口 + 请先在 miniprogram/config.js 填写 WEB_VIEW_ENTRY_URL。 + + diff --git a/miniprogram/pages/web-view/index.wxss b/miniprogram/pages/web-view/index.wxss new file mode 100644 index 00000000..fd583d0c --- /dev/null +++ b/miniprogram/pages/web-view/index.wxss @@ -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); +} diff --git a/miniprogram/sitemap.json b/miniprogram/sitemap.json new file mode 100644 index 00000000..1de189d2 --- /dev/null +++ b/miniprogram/sitemap.json @@ -0,0 +1,8 @@ +{ + "rules": [ + { + "action": "allow", + "page": "*" + } + ] +} diff --git a/project.config.json b/project.config.json new file mode 100644 index 00000000..f526e96e --- /dev/null +++ b/project.config.json @@ -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": {} +} diff --git a/project.private.config.json b/project.private.config.json new file mode 100644 index 00000000..220cf2f7 --- /dev/null +++ b/project.private.config.json @@ -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 + } +} \ No newline at end of file