fix(partner): resolve WeChat image picker auth denied on store create
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -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 { uploadFileToOss, type OssMediaType } from '../lib/upload';
|
||||||
import { isWechatEnv, weixinSdk } from '../lib/weixin';
|
import { isWechatEnv, weixinSdk } from '../lib/weixin';
|
||||||
|
|
||||||
@@ -34,6 +34,13 @@ export default function OssUploadField({
|
|||||||
accept ?? (mediaType === 'VIDEO' ? 'video/*' : mediaType === 'FILE' ? 'image/*,.pdf' : 'image/*');
|
accept ?? (mediaType === 'VIDEO' ? 'video/*' : mediaType === 'FILE' ? 'image/*,.pdf' : 'image/*');
|
||||||
const useWechatPicker = isWechatEnv() && mediaType !== 'VIDEO';
|
const useWechatPicker = isWechatEnv() && mediaType !== 'VIDEO';
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!useWechatPicker) return;
|
||||||
|
void weixinSdk.init().catch(() => {
|
||||||
|
/* 点击上传时会再次初始化并提示 */
|
||||||
|
});
|
||||||
|
}, [useWechatPicker]);
|
||||||
|
|
||||||
async function uploadSelectedFile(file: File) {
|
async function uploadSelectedFile(file: File) {
|
||||||
if (file.size > DEFAULT_MAX_MB * 1024 * 1024) {
|
if (file.size > DEFAULT_MAX_MB * 1024 * 1024) {
|
||||||
setError(`文件不能超过 ${DEFAULT_MAX_MB}MB`);
|
setError(`文件不能超过 ${DEFAULT_MAX_MB}MB`);
|
||||||
@@ -66,11 +73,16 @@ export default function OssUploadField({
|
|||||||
await uploadSelectedFile(files[0]);
|
await uploadSelectedFile(files[0]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
setError('未能获取图片,请重试');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
const msg = e instanceof Error ? e.message : '无法打开相册';
|
const msg = e instanceof Error ? e.message : '无法打开相册';
|
||||||
if (!/cancel/i.test(msg)) setError(msg);
|
if (!/cancel/i.test(msg)) {
|
||||||
return;
|
setError(/permission|auth|denied|授权|拒绝/i.test(msg)
|
||||||
|
? '微信选图授权失败,请刷新页面后重试'
|
||||||
|
: msg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
inputRef.current?.click();
|
inputRef.current?.click();
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import OssUploadField from '../components/OssUploadField';
|
|||||||
import { request } from '../lib/api';
|
import { request } from '../lib/api';
|
||||||
import { resolveRegionBinding } from '../lib/china-region';
|
import { resolveRegionBinding } from '../lib/china-region';
|
||||||
import { fetchPartnerCities, type OpenCityOption } from '../lib/upload';
|
import { fetchPartnerCities, type OpenCityOption } from '../lib/upload';
|
||||||
|
import { isWechatEnv, weixinSdk } from '../lib/weixin';
|
||||||
import {
|
import {
|
||||||
clearStoreDraft,
|
clearStoreDraft,
|
||||||
defaultStoreForm,
|
defaultStoreForm,
|
||||||
@@ -41,6 +42,13 @@ export default function StoreCreatePage() {
|
|||||||
saveStoreDraft({ step, form });
|
saveStoreDraft({ step, form });
|
||||||
}, [step, form]);
|
}, [step, form]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (step !== 2 || !isWechatEnv()) return;
|
||||||
|
void weixinSdk.init().catch(() => {
|
||||||
|
/* OssUploadField 点击时会再次初始化 */
|
||||||
|
});
|
||||||
|
}, [step]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
void fetchPartnerCities()
|
void fetchPartnerCities()
|
||||||
.then((items) => {
|
.then((items) => {
|
||||||
|
|||||||
@@ -51,12 +51,17 @@ export async function chooseWechatImages(
|
|||||||
const sourceType = options.sourceType ?? ['album', 'camera'];
|
const sourceType = options.sourceType ?? ['album', 'camera'];
|
||||||
|
|
||||||
if (platform === 'wechat-h5') {
|
if (platform === 'wechat-h5') {
|
||||||
|
const pageUrl = typeof window !== 'undefined' ? window.location.href.split('#')[0] : '';
|
||||||
await ensureJssdkReady({
|
await ensureJssdkReady({
|
||||||
apiBase: config.apiBase ?? '/api/v1',
|
apiBase: config.apiBase ?? '/api/v1',
|
||||||
clientApp: config.clientApp,
|
clientApp: config.clientApp,
|
||||||
getAccessToken: config.getAccessToken,
|
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<string[]>((resolve, reject) => {
|
const localIds = await new Promise<string[]>((resolve, reject) => {
|
||||||
window.wx!.chooseImage!({
|
window.wx!.chooseImage!({
|
||||||
|
|||||||
@@ -5,6 +5,11 @@ const JSSDK_URL = 'https://res.wx.qq.com/open/js/jweixin-1.6.0.js';
|
|||||||
|
|
||||||
let scriptPromise: Promise<void> | null = null;
|
let scriptPromise: Promise<void> | null = null;
|
||||||
let configured = false;
|
let configured = false;
|
||||||
|
let configuredUrl: string | null = null;
|
||||||
|
|
||||||
|
function currentPageUrl() {
|
||||||
|
return typeof window !== 'undefined' ? window.location.href.split('#')[0] : '';
|
||||||
|
}
|
||||||
|
|
||||||
function loadScript(): Promise<void> {
|
function loadScript(): Promise<void> {
|
||||||
if (typeof document === 'undefined') return Promise.reject(new Error('非浏览器环境'));
|
if (typeof document === 'undefined') return Promise.reject(new Error('非浏览器环境'));
|
||||||
@@ -48,13 +53,16 @@ export async function initWechatJssdk(options: {
|
|||||||
jsApiList?: string[];
|
jsApiList?: string[];
|
||||||
}): Promise<void> {
|
}): Promise<void> {
|
||||||
const { apiBase, clientApp, getAccessToken } = options;
|
const { apiBase, clientApp, getAccessToken } = options;
|
||||||
const pageUrl = options.url ?? (typeof window !== 'undefined' ? window.location.href.split('#')[0] : '');
|
const pageUrl = options.url ?? currentPageUrl();
|
||||||
await loadScript();
|
await loadScript();
|
||||||
if (!window.wx) throw new Error('微信 JSSDK 不可用');
|
if (!window.wx) throw new Error('微信 JSSDK 不可用');
|
||||||
|
|
||||||
const config = await fetchJssdkConfig(apiBase, clientApp, pageUrl, getAccessToken?.());
|
const config = await fetchJssdkConfig(apiBase, clientApp, pageUrl, getAccessToken?.());
|
||||||
const jsApiList = options.jsApiList ?? [...DEFAULT_JS_API_LIST];
|
const jsApiList = options.jsApiList ?? [...DEFAULT_JS_API_LIST];
|
||||||
|
|
||||||
|
configured = false;
|
||||||
|
configuredUrl = null;
|
||||||
|
|
||||||
await new Promise<void>((resolve, reject) => {
|
await new Promise<void>((resolve, reject) => {
|
||||||
window.wx!.config({
|
window.wx!.config({
|
||||||
...config,
|
...config,
|
||||||
@@ -63,6 +71,7 @@ export async function initWechatJssdk(options: {
|
|||||||
});
|
});
|
||||||
window.wx!.ready(() => {
|
window.wx!.ready(() => {
|
||||||
configured = true;
|
configured = true;
|
||||||
|
configuredUrl = pageUrl;
|
||||||
resolve();
|
resolve();
|
||||||
});
|
});
|
||||||
window.wx!.error((err) => reject(new Error(err.errMsg || 'wx.config 失败')));
|
window.wx!.error((err) => reject(new Error(err.errMsg || 'wx.config 失败')));
|
||||||
@@ -70,15 +79,18 @@ export async function initWechatJssdk(options: {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function isJssdkReady(): boolean {
|
export function isJssdkReady(): boolean {
|
||||||
return configured && !!window.wx;
|
return configured && !!window.wx && configuredUrl === currentPageUrl();
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function ensureJssdkReady(options: {
|
export async function ensureJssdkReady(options: {
|
||||||
apiBase: string;
|
apiBase: string;
|
||||||
clientApp: string;
|
clientApp: string;
|
||||||
|
url?: string;
|
||||||
getAccessToken?: () => string | null;
|
getAccessToken?: () => string | null;
|
||||||
|
jsApiList?: string[];
|
||||||
}): Promise<void> {
|
}): Promise<void> {
|
||||||
|
const pageUrl = options.url ?? currentPageUrl();
|
||||||
if (!isJssdkReady()) {
|
if (!isJssdkReady()) {
|
||||||
await initWechatJssdk(options);
|
await initWechatJssdk({ ...options, url: pageUrl });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
+3
@@ -246,6 +246,9 @@ importers:
|
|||||||
class-validator:
|
class-validator:
|
||||||
specifier: ^0.14.1
|
specifier: ^0.14.1
|
||||||
version: 0.14.4
|
version: 0.14.4
|
||||||
|
express:
|
||||||
|
specifier: ^4.21.0
|
||||||
|
version: 4.22.1
|
||||||
ioredis:
|
ioredis:
|
||||||
specifier: ^5.4.1
|
specifier: ^5.4.1
|
||||||
version: 5.11.1
|
version: 5.11.1
|
||||||
|
|||||||
@@ -21,7 +21,8 @@ export class WechatController {
|
|||||||
@Get('jssdk-config')
|
@Get('jssdk-config')
|
||||||
async jssdkConfig(@Query('url') url: string) {
|
async jssdkConfig(@Query('url') url: string) {
|
||||||
if (!url) throw new BadRequestException('url 参数必填');
|
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')
|
@Get('oauth-url')
|
||||||
|
|||||||
Reference in New Issue
Block a user