diff --git a/apps/h5-partner/src/components/OssUploadField.tsx b/apps/h5-partner/src/components/OssUploadField.tsx index a14e3eb..1d5c440 100644 --- a/apps/h5-partner/src/components/OssUploadField.tsx +++ b/apps/h5-partner/src/components/OssUploadField.tsx @@ -1,4 +1,4 @@ -import { useId, useRef, useState } from 'react'; +import { useEffect, useId, useRef, useState } from 'react'; import { uploadFileToOss, type OssMediaType } from '../lib/upload'; import { isWechatEnv, weixinSdk } from '../lib/weixin'; @@ -34,6 +34,13 @@ export default function OssUploadField({ accept ?? (mediaType === 'VIDEO' ? 'video/*' : mediaType === 'FILE' ? 'image/*,.pdf' : 'image/*'); const useWechatPicker = isWechatEnv() && mediaType !== 'VIDEO'; + useEffect(() => { + if (!useWechatPicker) return; + void weixinSdk.init().catch(() => { + /* 点击上传时会再次初始化并提示 */ + }); + }, [useWechatPicker]); + async function uploadSelectedFile(file: File) { if (file.size > DEFAULT_MAX_MB * 1024 * 1024) { setError(`文件不能超过 ${DEFAULT_MAX_MB}MB`); @@ -66,11 +73,16 @@ export default function OssUploadField({ await uploadSelectedFile(files[0]); return; } + setError('未能获取图片,请重试'); } catch (e) { const msg = e instanceof Error ? e.message : '无法打开相册'; - if (!/cancel/i.test(msg)) setError(msg); - return; + if (!/cancel/i.test(msg)) { + setError(/permission|auth|denied|授权|拒绝/i.test(msg) + ? '微信选图授权失败,请刷新页面后重试' + : msg); + } } + return; } inputRef.current?.click(); diff --git a/apps/h5-partner/src/pages/StoreCreatePage.tsx b/apps/h5-partner/src/pages/StoreCreatePage.tsx index 5803c16..2603296 100644 --- a/apps/h5-partner/src/pages/StoreCreatePage.tsx +++ b/apps/h5-partner/src/pages/StoreCreatePage.tsx @@ -6,6 +6,7 @@ import OssUploadField from '../components/OssUploadField'; import { request } from '../lib/api'; import { resolveRegionBinding } from '../lib/china-region'; import { fetchPartnerCities, type OpenCityOption } from '../lib/upload'; +import { isWechatEnv, weixinSdk } from '../lib/weixin'; import { clearStoreDraft, defaultStoreForm, @@ -41,6 +42,13 @@ export default function StoreCreatePage() { saveStoreDraft({ step, form }); }, [step, form]); + useEffect(() => { + if (step !== 2 || !isWechatEnv()) return; + void weixinSdk.init().catch(() => { + /* OssUploadField 点击时会再次初始化 */ + }); + }, [step]); + useEffect(() => { void fetchPartnerCities() .then((items) => { diff --git a/packages/weixin-sdk/src/chooseImage.ts b/packages/weixin-sdk/src/chooseImage.ts index e467939..013d663 100644 --- a/packages/weixin-sdk/src/chooseImage.ts +++ b/packages/weixin-sdk/src/chooseImage.ts @@ -51,12 +51,17 @@ export async function chooseWechatImages( const sourceType = options.sourceType ?? ['album', 'camera']; if (platform === 'wechat-h5') { + const pageUrl = typeof window !== 'undefined' ? window.location.href.split('#')[0] : ''; await ensureJssdkReady({ apiBase: config.apiBase ?? '/api/v1', clientApp: config.clientApp, getAccessToken: config.getAccessToken, + url: pageUrl, + jsApiList: ['chooseImage', 'getLocalImgData'], }); - if (!window.wx?.chooseImage) return null; + if (!window.wx?.chooseImage) { + throw new Error('微信选图接口不可用,请刷新页面后重试'); + } const localIds = await new Promise((resolve, reject) => { window.wx!.chooseImage!({ diff --git a/packages/weixin-sdk/src/jssdk.ts b/packages/weixin-sdk/src/jssdk.ts index 1f239fc..7b56c36 100644 --- a/packages/weixin-sdk/src/jssdk.ts +++ b/packages/weixin-sdk/src/jssdk.ts @@ -5,6 +5,11 @@ const JSSDK_URL = 'https://res.wx.qq.com/open/js/jweixin-1.6.0.js'; let scriptPromise: Promise | null = null; let configured = false; +let configuredUrl: string | null = null; + +function currentPageUrl() { + return typeof window !== 'undefined' ? window.location.href.split('#')[0] : ''; +} function loadScript(): Promise { if (typeof document === 'undefined') return Promise.reject(new Error('非浏览器环境')); @@ -48,13 +53,16 @@ export async function initWechatJssdk(options: { jsApiList?: string[]; }): Promise { const { apiBase, clientApp, getAccessToken } = options; - const pageUrl = options.url ?? (typeof window !== 'undefined' ? window.location.href.split('#')[0] : ''); + const pageUrl = options.url ?? currentPageUrl(); await loadScript(); if (!window.wx) throw new Error('微信 JSSDK 不可用'); const config = await fetchJssdkConfig(apiBase, clientApp, pageUrl, getAccessToken?.()); const jsApiList = options.jsApiList ?? [...DEFAULT_JS_API_LIST]; + configured = false; + configuredUrl = null; + await new Promise((resolve, reject) => { window.wx!.config({ ...config, @@ -63,6 +71,7 @@ export async function initWechatJssdk(options: { }); window.wx!.ready(() => { configured = true; + configuredUrl = pageUrl; resolve(); }); window.wx!.error((err) => reject(new Error(err.errMsg || 'wx.config 失败'))); @@ -70,15 +79,18 @@ export async function initWechatJssdk(options: { } export function isJssdkReady(): boolean { - return configured && !!window.wx; + return configured && !!window.wx && configuredUrl === currentPageUrl(); } export async function ensureJssdkReady(options: { apiBase: string; clientApp: string; + url?: string; getAccessToken?: () => string | null; + jsApiList?: string[]; }): Promise { + const pageUrl = options.url ?? currentPageUrl(); if (!isJssdkReady()) { - await initWechatJssdk(options); + await initWechatJssdk({ ...options, url: pageUrl }); } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ac2c434..b89e0dc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -246,6 +246,9 @@ importers: class-validator: specifier: ^0.14.1 version: 0.14.4 + express: + specifier: ^4.21.0 + version: 4.22.1 ioredis: specifier: ^5.4.1 version: 5.11.1 diff --git a/server/dukang-api/src/modules/common/wechat.controller.ts b/server/dukang-api/src/modules/common/wechat.controller.ts index 6691df6..2a3d99c 100644 --- a/server/dukang-api/src/modules/common/wechat.controller.ts +++ b/server/dukang-api/src/modules/common/wechat.controller.ts @@ -21,7 +21,8 @@ export class WechatController { @Get('jssdk-config') async jssdkConfig(@Query('url') url: string) { if (!url) throw new BadRequestException('url 参数必填'); - return this.wechat.createJssdkConfig(url); + const pageUrl = decodeURIComponent(url).split('#')[0]; + return this.wechat.createJssdkConfig(pageUrl); } @Get('oauth-url')