合伙人端开店选照片

This commit is contained in:
2026-07-09 17:26:24 +08:00
parent 3f60c37265
commit b506417f5e
3 changed files with 97 additions and 27 deletions
@@ -7,6 +7,7 @@ import {
needsWechatAuth,
type PartnerProfile,
} from '../lib/wechat-auth';
import { formatChooseImageFailMessage } from '@dukang/weixin-sdk';
import { isWechatEnv, weixinSdk } from '../lib/weixin';
import type { ClientRuntimeConfig } from '@dukang/shared-types';
@@ -28,12 +29,11 @@ const DEFAULT_MAX_MB = 10;
function formatWechatUploadError(e: unknown): string {
const msg = e instanceof Error ? e.message : '无法打开相册';
const formatted = formatChooseImageFailMessage(msg);
if (formatted) return formatted;
if (/invalid signature/i.test(msg)) {
return '微信 JSSDK 签名校验失败:请确认公众号已配置 JS 接口安全域名为 user.runxian.top,并刷新页面后重试';
}
if (/permission|denied|拒绝/i.test(msg)) {
return '微信选图权限被拒绝,请在微信设置中允许相册/相机访问后重试';
}
return msg;
}
@@ -116,9 +116,10 @@ export default function OssUploadField({
}
async function pickWechatImage() {
await weixinSdk.init();
const files = await weixinSdk.chooseImages({
count: 1,
sourceType: ['album', 'camera'],
sourceType: ['album'],
});
if (files?.[0]) {
await uploadSelectedFile(files[0]);
@@ -143,7 +144,6 @@ export default function OssUploadField({
const msg = e instanceof Error ? e.message : '无法打开相册';
if (/cancel/i.test(msg)) return;
setError(formatWechatUploadError(e));
openNativeFilePicker();
}
return;
}
+91 -21
View File
@@ -1,5 +1,5 @@
import { getRuntimePlatform } from './env';
import { ensureJssdkReady, normalizeJssdkPageUrl, getJssdkSignUrl } from './jssdk';
import { getRuntimePlatform, isIosDevice, isWechatDevTools } from './env';
import { ensureJssdkReady, normalizeJssdkPageUrl } from './jssdk';
import type { WeixinSdkConfig } from './types';
export type ChooseWechatImageOptions = {
@@ -7,6 +7,38 @@ export type ChooseWechatImageOptions = {
sourceType?: Array<'album' | 'camera'>;
};
function delay(ms: number): Promise<void> {
return new Promise((resolve) => window.setTimeout(resolve, ms));
}
/** 将微信 chooseImage / getLocalImgData fail 的 errMsg 转为用户可读文案 */
export function formatChooseImageFailMessage(errMsg: string): string {
const msg = errMsg.trim() || '无法打开相册';
if (/cancel/i.test(msg)) return '';
if (/offline verifying|permission value is offline/i.test(msg)) {
return '微信权限验证中,请稍候再试或刷新页面后重新选择图片';
}
if (/invalid signature|config:fail|signature/i.test(msg)) {
return '微信 JSSDK 签名校验失败,请刷新页面后重试';
}
if (/photo.*denied|photos.*denied|相册.*权限|无法访问相册|无相册权限/i.test(msg)) {
return '相册权限未开启,请在 iPhone「设置 → 隐私与安全性 → 照片」中允许微信访问相册';
}
if (/system.*camera|camera.*not.*allowed|无法访问相机|无相机权限/i.test(msg)) {
return '相机权限未开启,请在 iPhone「设置 → 隐私与安全性 → 相机」中允许微信使用摄像头';
}
if (/permission|auth|denied|授权|拒绝/i.test(msg)) {
return `微信选图接口未就绪(${msg}),请刷新页面后重试`;
}
return msg;
}
async function reportChooseImageEvent(
config: WeixinSdkConfig,
payload: {
@@ -67,7 +99,34 @@ function localIdToFile(localId: string): Promise<File> {
reject(e instanceof Error ? e : new Error('图片解析失败'));
}
},
fail: (err) => reject(new Error(err.errMsg || '读取图片失败')),
fail: (err) => {
const raw = err.errMsg || '读取图片失败';
const formatted = formatChooseImageFailMessage(raw);
reject(new Error(formatted || raw));
},
});
});
}
function invokeChooseImage(
count: number,
sourceType: Array<'album' | 'camera'>,
): Promise<string[]> {
return new Promise((resolve, reject) => {
window.wx!.chooseImage!({
count,
sizeType: ['compressed'],
sourceType,
success: (res) => resolve(res.localIds ?? []),
fail: (err) => {
const raw = err.errMsg || '无法打开相册';
if (/cancel/i.test(raw)) {
resolve([]);
return;
}
const formatted = formatChooseImageFailMessage(raw);
reject(new Error(formatted || raw));
},
});
});
}
@@ -84,7 +143,6 @@ export async function chooseWechatImages(
const sourceType = options.sourceType ?? ['album', 'camera'];
if (platform === 'wechat-h5') {
const pageUrl = typeof window !== 'undefined' ? getJssdkSignUrl() : '';
const sourceTypeKey = sourceType.join(',');
try {
await ensureJssdkReady({
@@ -104,26 +162,38 @@ export async function chooseWechatImages(
throw new Error(errMsg);
}
let localIds: string[];
try {
localIds = await new Promise<string[]>((resolve, reject) => {
window.wx!.chooseImage!({
count,
sizeType: ['compressed'],
sourceType,
success: (res) => resolve(res.localIds ?? []),
fail: (err) => reject(new Error(err.errMsg || '无法打开相册')),
});
});
} catch (e) {
const errMsg = e instanceof Error ? e.message : '无法打开相册';
void reportChooseImageEvent(config, { status: 'fail', errMsg, sourceType: sourceTypeKey, stage: 'choose' });
throw e;
if (isIosDevice() && !isWechatDevTools()) {
await delay(500);
}
let localIds: string[] = [];
const maxAttempts = 2;
let lastError: Error | null = null;
for (let attempt = 0; attempt < maxAttempts; attempt++) {
if (attempt > 0) {
await delay(800);
}
try {
localIds = await invokeChooseImage(count, sourceType);
lastError = null;
break;
} catch (e) {
lastError = e instanceof Error ? e : new Error('无法打开相册');
const raw = lastError.message;
const retryable = /offline verifying|权限验证中|接口未就绪/i.test(raw);
if (attempt < maxAttempts - 1 && retryable) continue;
void reportChooseImageEvent(config, {
status: 'fail',
errMsg: raw,
sourceType: sourceTypeKey,
stage: 'choose',
});
throw lastError;
}
}
if (lastError) throw lastError;
if (!localIds.length) {
const errMsg = '未选择图片';
void reportChooseImageEvent(config, { status: 'fail', errMsg, sourceType: sourceTypeKey, stage: 'empty' });
return [];
}
+1 -1
View File
@@ -19,7 +19,7 @@ export {
export type { WechatLocationOutcome } from './location';
export { scanQrCode } from './scan';
export { invokeWechatPay } from './pay';
export { chooseWechatImages, canUseWechatChooseImage } from './chooseImage';
export { chooseWechatImages, canUseWechatChooseImage, formatChooseImageFailMessage } from './chooseImage';
export type { ChooseWechatImageOptions } from './chooseImage';
export {
setWechatShareData,