合伙人端开店选照片
This commit is contained in:
@@ -7,6 +7,7 @@ import {
|
|||||||
needsWechatAuth,
|
needsWechatAuth,
|
||||||
type PartnerProfile,
|
type PartnerProfile,
|
||||||
} from '../lib/wechat-auth';
|
} from '../lib/wechat-auth';
|
||||||
|
import { formatChooseImageFailMessage } from '@dukang/weixin-sdk';
|
||||||
import { isWechatEnv, weixinSdk } from '../lib/weixin';
|
import { isWechatEnv, weixinSdk } from '../lib/weixin';
|
||||||
import type { ClientRuntimeConfig } from '@dukang/shared-types';
|
import type { ClientRuntimeConfig } from '@dukang/shared-types';
|
||||||
|
|
||||||
@@ -28,12 +29,11 @@ const DEFAULT_MAX_MB = 10;
|
|||||||
|
|
||||||
function formatWechatUploadError(e: unknown): string {
|
function formatWechatUploadError(e: unknown): string {
|
||||||
const msg = e instanceof Error ? e.message : '无法打开相册';
|
const msg = e instanceof Error ? e.message : '无法打开相册';
|
||||||
|
const formatted = formatChooseImageFailMessage(msg);
|
||||||
|
if (formatted) return formatted;
|
||||||
if (/invalid signature/i.test(msg)) {
|
if (/invalid signature/i.test(msg)) {
|
||||||
return '微信 JSSDK 签名校验失败:请确认公众号已配置 JS 接口安全域名为 user.runxian.top,并刷新页面后重试';
|
return '微信 JSSDK 签名校验失败:请确认公众号已配置 JS 接口安全域名为 user.runxian.top,并刷新页面后重试';
|
||||||
}
|
}
|
||||||
if (/permission|denied|拒绝/i.test(msg)) {
|
|
||||||
return '微信选图权限被拒绝,请在微信设置中允许相册/相机访问后重试';
|
|
||||||
}
|
|
||||||
return msg;
|
return msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,9 +116,10 @@ export default function OssUploadField({
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function pickWechatImage() {
|
async function pickWechatImage() {
|
||||||
|
await weixinSdk.init();
|
||||||
const files = await weixinSdk.chooseImages({
|
const files = await weixinSdk.chooseImages({
|
||||||
count: 1,
|
count: 1,
|
||||||
sourceType: ['album', 'camera'],
|
sourceType: ['album'],
|
||||||
});
|
});
|
||||||
if (files?.[0]) {
|
if (files?.[0]) {
|
||||||
await uploadSelectedFile(files[0]);
|
await uploadSelectedFile(files[0]);
|
||||||
@@ -143,7 +144,6 @@ export default function OssUploadField({
|
|||||||
const msg = e instanceof Error ? e.message : '无法打开相册';
|
const msg = e instanceof Error ? e.message : '无法打开相册';
|
||||||
if (/cancel/i.test(msg)) return;
|
if (/cancel/i.test(msg)) return;
|
||||||
setError(formatWechatUploadError(e));
|
setError(formatWechatUploadError(e));
|
||||||
openNativeFilePicker();
|
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { getRuntimePlatform } from './env';
|
import { getRuntimePlatform, isIosDevice, isWechatDevTools } from './env';
|
||||||
import { ensureJssdkReady, normalizeJssdkPageUrl, getJssdkSignUrl } from './jssdk';
|
import { ensureJssdkReady, normalizeJssdkPageUrl } from './jssdk';
|
||||||
import type { WeixinSdkConfig } from './types';
|
import type { WeixinSdkConfig } from './types';
|
||||||
|
|
||||||
export type ChooseWechatImageOptions = {
|
export type ChooseWechatImageOptions = {
|
||||||
@@ -7,6 +7,38 @@ export type ChooseWechatImageOptions = {
|
|||||||
sourceType?: Array<'album' | 'camera'>;
|
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(
|
async function reportChooseImageEvent(
|
||||||
config: WeixinSdkConfig,
|
config: WeixinSdkConfig,
|
||||||
payload: {
|
payload: {
|
||||||
@@ -67,7 +99,34 @@ function localIdToFile(localId: string): Promise<File> {
|
|||||||
reject(e instanceof Error ? e : new Error('图片解析失败'));
|
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'];
|
const sourceType = options.sourceType ?? ['album', 'camera'];
|
||||||
|
|
||||||
if (platform === 'wechat-h5') {
|
if (platform === 'wechat-h5') {
|
||||||
const pageUrl = typeof window !== 'undefined' ? getJssdkSignUrl() : '';
|
|
||||||
const sourceTypeKey = sourceType.join(',');
|
const sourceTypeKey = sourceType.join(',');
|
||||||
try {
|
try {
|
||||||
await ensureJssdkReady({
|
await ensureJssdkReady({
|
||||||
@@ -104,26 +162,38 @@ export async function chooseWechatImages(
|
|||||||
throw new Error(errMsg);
|
throw new Error(errMsg);
|
||||||
}
|
}
|
||||||
|
|
||||||
let localIds: string[];
|
if (isIosDevice() && !isWechatDevTools()) {
|
||||||
try {
|
await delay(500);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
if (!localIds.length) {
|
||||||
const errMsg = '未选择图片';
|
|
||||||
void reportChooseImageEvent(config, { status: 'fail', errMsg, sourceType: sourceTypeKey, stage: 'empty' });
|
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ export {
|
|||||||
export type { WechatLocationOutcome } from './location';
|
export type { WechatLocationOutcome } from './location';
|
||||||
export { scanQrCode } from './scan';
|
export { scanQrCode } from './scan';
|
||||||
export { invokeWechatPay } from './pay';
|
export { invokeWechatPay } from './pay';
|
||||||
export { chooseWechatImages, canUseWechatChooseImage } from './chooseImage';
|
export { chooseWechatImages, canUseWechatChooseImage, formatChooseImageFailMessage } from './chooseImage';
|
||||||
export type { ChooseWechatImageOptions } from './chooseImage';
|
export type { ChooseWechatImageOptions } from './chooseImage';
|
||||||
export {
|
export {
|
||||||
setWechatShareData,
|
setWechatShareData,
|
||||||
|
|||||||
Reference in New Issue
Block a user