feat(mini): v4.0.8 客服卡片主包落地、定位误报修复,合伙人微信内预览保存海报

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-09-01 15:23:52 +08:00
parent cdc4b82931
commit 283e6651aa
25 changed files with 291 additions and 93 deletions
+4 -2
View File
@@ -130,10 +130,12 @@ export function toast(title: string, icon: 'success' | 'error' | 'none' = 'none'
const text = title.trim();
if (!text) return;
if (icon !== 'success' && showFloatingToast(text)) {
void Taro.hideToast();
void Promise.resolve(Taro.hideToast() as void | Promise<unknown>).catch(() => {});
return;
}
Taro.showToast({ title: text, icon, duration: 1800 });
void Promise.resolve(
Taro.showToast({ title: text, icon, duration: 1800 }) as void | Promise<unknown>,
).catch(() => {});
}
export type SessionPayload = {
+17 -6
View File
@@ -21,6 +21,14 @@ export type ClientErrorPayload = {
extra?: Record<string, unknown>;
};
/** 微信预期失败,不应按 P1 未捕获上报 */
const IGNORED_REJECTION =
/getLocation:fail|hideToast:fail:toast can't be found|showToast:fail|authorize:fail auth deny/i;
function shouldIgnoreRejection(message: string): boolean {
return IGNORED_REJECTION.test(message);
}
function currentPagePath(): string | undefined {
try {
const pages = Taro.getCurrentPages();
@@ -97,6 +105,7 @@ export function installClientErrorReporting(): void {
: typeof reason === 'string'
? reason
: JSON.stringify(reason);
if (shouldIgnoreRejection(message || '')) return;
const stack = reason instanceof Error ? reason.stack : undefined;
reportClientError({
level: 'error',
@@ -121,15 +130,17 @@ export function installClientErrorReporting(): void {
});
window.addEventListener('unhandledrejection', (ev) => {
const reason = ev.reason;
const message =
reason instanceof Error
? reason.message
: typeof reason === 'string'
? reason
: 'unhandledrejection';
if (shouldIgnoreRejection(message)) return;
reportClientError({
level: 'error',
category: 'unhandled_rejection',
message:
reason instanceof Error
? reason.message
: typeof reason === 'string'
? reason
: 'unhandledrejection',
message,
stack: reason instanceof Error ? reason.stack : undefined,
});
});
+1 -7
View File
@@ -14,13 +14,7 @@ export async function tryGetClientGpsLocation(): Promise<ClientGpsLocation | nul
if (process.env.TARO_ENV === 'weapp') {
try {
const Taro = (await import('@tarojs/taro')).default;
const loc = await new Promise<{ latitude: number; longitude: number }>((resolve, reject) => {
Taro.getLocation({
type: 'gcj02',
success: resolve,
fail: reject,
});
});
const loc = await Taro.getLocation({ type: 'gcj02' });
return { latitude: loc.latitude, longitude: loc.longitude };
} catch {
return null;
@@ -11,13 +11,7 @@ export type ClientGpsLocation = {
export async function tryGetClientGpsLocation(): Promise<ClientGpsLocation | null> {
try {
const loc = await new Promise<{ latitude: number; longitude: number }>((resolve, reject) => {
Taro.getLocation({
type: 'gcj02',
success: resolve,
fail: reject,
});
});
const loc = await Taro.getLocation({ type: 'gcj02' });
return { latitude: loc.latitude, longitude: loc.longitude };
} catch {
return null;
+10 -5
View File
@@ -1,13 +1,18 @@
import Taro from '@tarojs/taro';
import { fetchClientConfig } from './pay-wechat';
/** 与 package.json version 同步,供服务端 minClientVersion 比对 */
export const APP_VERSION = '3.5.16';
/** 与 package.json version 同步Taro defineConstants 注入),供 minClientVersion 比对 */
export const APP_VERSION =
(typeof TARO_APP_VERSION !== 'undefined' && String(TARO_APP_VERSION).trim()) || '4.0.4';
export const APP_VERSION_LABEL = `v${APP_VERSION}`;
export const APP_VERSION_LABEL = `v${APP_VERSION.replace(/^v/i, '')}`;
function normalizeVersion(v: string): string {
return String(v || '').trim().replace(/^v/i, '');
}
function parseSemver(v: string): number[] {
return v.split('.').map((n) => parseInt(n, 10) || 0);
return normalizeVersion(v).split('.').map((n) => parseInt(n, 10) || 0);
}
export function compareSemver(a: string, b: string): number {
@@ -89,7 +94,7 @@ async function enforceMinClientVersion(min: string) {
export async function checkClientVersionGate() {
try {
const config = await fetchClientConfig();
const min = config.minClientVersion?.trim();
const min = normalizeVersion(config.minClientVersion ?? '');
if (min && compareSemver(APP_VERSION, min) < 0) {
await enforceMinClientVersion(min);
}
+25 -9
View File
@@ -59,6 +59,23 @@ function clearLocationDenied() {
}
}
function wxErrorMessage(err: unknown): string {
if (!err) return '';
if (typeof err === 'string') return err;
if (err instanceof Error) return err.message;
if (typeof err === 'object') {
const o = err as { errMsg?: unknown; message?: unknown };
if (typeof o.errMsg === 'string' && o.errMsg) return o.errMsg;
if (typeof o.message === 'string' && o.message) return o.message;
try {
return JSON.stringify(err);
} catch {
return '';
}
}
return String(err);
}
function isDenyMessage(errMsg?: string): boolean {
return /auth deny|authorize|permission|denied|拒绝|用户拒绝|getLocation:fail/i.test(
errMsg || '',
@@ -179,14 +196,13 @@ async function promptLocationAuthOnce() {
}).catch(() => {});
}
function getMiniLocation(): Promise<Taro.getLocation.SuccessCallbackResult> {
return new Promise((resolve, reject) => {
Taro.getLocation({
type: 'gcj02',
success: resolve,
fail: reject,
});
});
async function getMiniLocation(): Promise<Taro.getLocation.SuccessCallbackResult> {
const setting = await Taro.getSetting().catch(() => null);
if (setting?.authSetting?.['scope.userLocation'] === false) {
throw new Error('getLocation:fail auth deny');
}
// 只用返回的 Promise,避免 callback + Promise 双重 reject 变成未捕获
return Taro.getLocation({ type: 'gcj02' });
}
async function resolveViaH5Jssdk(): Promise<ResolvedUserCity | null> {
@@ -263,7 +279,7 @@ export async function resolveUserCity(force = false): Promise<ResolvedUserCity>
return resolved;
}
} catch (err) {
const errMsg = err instanceof Error ? err.message : String(err);
const errMsg = wxErrorMessage(err);
const denied = isDenyMessage(errMsg);
if (denied && !isLocationDenied()) {
await promptLocationAuthOnce();
+25 -9
View File
@@ -57,6 +57,23 @@ function clearLocationDenied() {
}
}
function wxErrorMessage(err: unknown): string {
if (!err) return '';
if (typeof err === 'string') return err;
if (err instanceof Error) return err.message;
if (typeof err === 'object') {
const o = err as { errMsg?: unknown; message?: unknown };
if (typeof o.errMsg === 'string' && o.errMsg) return o.errMsg;
if (typeof o.message === 'string' && o.message) return o.message;
try {
return JSON.stringify(err);
} catch {
return '';
}
}
return String(err);
}
function isDenyMessage(errMsg?: string): boolean {
return /auth deny|authorize|permission|denied|拒绝|用户拒绝|getLocation:fail/i.test(
errMsg || '',
@@ -175,14 +192,13 @@ async function promptLocationAuthOnce() {
}).catch(() => {});
}
function getMiniLocation(): Promise<Taro.getLocation.SuccessCallbackResult> {
return new Promise((resolve, reject) => {
Taro.getLocation({
type: 'gcj02',
success: resolve,
fail: reject,
});
});
async function getMiniLocation(): Promise<Taro.getLocation.SuccessCallbackResult> {
const setting = await Taro.getSetting().catch(() => null);
if (setting?.authSetting?.['scope.userLocation'] === false) {
throw new Error('getLocation:fail auth deny');
}
// 只用返回的 Promise,避免 callback + Promise 双重 reject 变成未捕获
return Taro.getLocation({ type: 'gcj02' });
}
export async function resolveUserCity(force = false): Promise<ResolvedUserCity> {
@@ -211,7 +227,7 @@ export async function resolveUserCity(force = false): Promise<ResolvedUserCity>
return resolved;
}
} catch (err) {
const errMsg = err instanceof Error ? err.message : String(err);
const errMsg = wxErrorMessage(err);
const denied = isDenyMessage(errMsg);
if (denied && !isLocationDenied()) {
await promptLocationAuthOnce();
+14 -9
View File
@@ -4,6 +4,17 @@ export type WecomCsSessionContext = {
from?: string;
};
/** 客服消息卡片必须落主包页;分包(订单详情)作启动页会白屏 */
export function buildCsSharePath(orderId?: string): string {
const id = (orderId || '').trim();
return id ? `pages/open/index?id=${encodeURIComponent(id)}` : 'pages/home/index';
}
export function buildCsShareTitle(orderNo?: string): string {
const no = (orderNo || '').trim();
return no ? `订单 ${no}` : '杜康好客';
}
type OpenCsChatOption = {
extInfo: { url: string };
corpId: string;
@@ -97,16 +108,10 @@ export async function openWecomCustomerServiceChat(params: {
const option: OpenCsChatOption = {
extInfo: { url },
corpId,
showMessageCard: true,
sendMessageTitle: buildCsShareTitle(params.session?.orderNo),
sendMessagePath: buildCsSharePath(params.session?.orderId),
};
if (params.session?.orderNo || params.session?.orderId) {
option.showMessageCard = true;
option.sendMessageTitle = params.session.orderNo
? `订单 ${params.session.orderNo}`
: '订单咨询';
if (params.session.orderId) {
option.sendMessagePath = `pages/order-detail/index?id=${params.session.orderId}`;
}
}
await new Promise<void>((resolve, reject) => {
wxApi({