Files
mp-xieyingeng/assets/scripts/utils/WxSDK.ts
2026-05-19 22:56:31 +08:00

563 lines
17 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { sys } from 'cc';
/**
* 微信分享配置
*/
export interface WxShareConfig {
/** 分享标题 */
title: string;
/** 分享图片 URL 或本地路径 */
imageUrl?: string;
/** 查询字符串,从这条转发消息进入后,可通过 wx.getLaunchOptionsSync 或 wx.onShow 获取 */
query?: string;
}
/**
* 微信朋友圈分享配置
*/
export interface WxShareTimelineConfig {
/** 分享标题 */
title: string;
/** 分享图片 URL 或本地路径 */
imageUrl?: string;
/** 查询字符串 */
query?: string;
}
export interface WxPrivacySettingResult {
/** true 表示需要用户确认隐私授权 */
needAuthorization: boolean;
}
/**
* 微信小游戏 SDK 工具类
* 封装微信平台相关 API非微信环境下静默降级
*/
export class WxSDK {
/** 隐私授权请求进行中时复用同一个 Promise避免启动链路重复弹窗 */
private static _privacyAuthorizePromise: Promise<boolean> | null = null;
/**
* 是否处于微信小游戏环境
*/
static isWechat(): boolean {
return sys.platform === sys.Platform.WECHAT_GAME;
}
/**
* 获取 wx 全局对象(仅微信环境下可用)
*/
static getWx(): any {
if (!WxSDK.isWechat()) return null;
return typeof wx !== 'undefined' ? wx : null;
}
// ==================== 登录相关 ====================
/**
* 微信登录,获取临时 code
* @returns Promise<string> 登录 code
*/
static login(): Promise<string> {
return new Promise((resolve, reject) => {
const wxApi = WxSDK.getWx();
if (!wxApi) {
reject(new Error('非微信环境,无法调用 wx.login'));
return;
}
wxApi.login({
success: (res: any) => {
if (res.code) {
console.log('[WxSDK] wx.login 成功,获取到 code');
resolve(res.code);
} else {
console.error('[WxSDK] wx.login 失败:', res.errMsg);
reject(new Error(res.errMsg || 'wx.login 失败'));
}
},
fail: (err: any) => {
console.error('[WxSDK] wx.login 调用失败:', err);
reject(new Error(err.errMsg || 'wx.login 调用失败'));
}
});
});
}
// ==================== 隐私授权相关 ====================
/**
* 检查用户是否需要隐私授权。
* 低版本或非微信环境没有隐私拦截能力,按已授权处理。
*/
static checkPrivacySetting(): Promise<WxPrivacySettingResult> {
return new Promise((resolve) => {
const wxApi = WxSDK.getWx();
if (!wxApi) {
resolve({ needAuthorization: false });
return;
}
if (typeof wxApi.getPrivacySetting !== 'function') {
console.warn('[WxSDK] 当前微信版本不支持 getPrivacySetting');
resolve({ needAuthorization: false });
return;
}
wxApi.getPrivacySetting({
success: (res: any) => {
const needAuthorization = !!res?.needAuthorization;
console.log('[WxSDK] 隐私授权检查结果:', res);
resolve({ needAuthorization });
},
fail: (err: any) => {
console.warn('[WxSDK] 隐私授权检查失败:', err);
resolve({ needAuthorization: true });
}
});
});
}
/**
* 主动触发微信小游戏隐私授权流程。
* 已授权用户会直接 success低版本或非微信环境直接视为通过。
*/
static requirePrivacyAuthorize(): Promise<boolean> {
const wxApi = WxSDK.getWx();
if (!wxApi) {
console.warn('[WxSDK] 非微信环境,跳过隐私授权');
return Promise.resolve(true);
}
if (typeof wxApi.requirePrivacyAuthorize !== 'function') {
console.warn('[WxSDK] 当前微信版本不支持 requirePrivacyAuthorize');
return Promise.resolve(true);
}
if (WxSDK._privacyAuthorizePromise) {
return WxSDK._privacyAuthorizePromise;
}
WxSDK._privacyAuthorizePromise = new Promise((resolve) => {
wxApi.requirePrivacyAuthorize({
success: () => {
console.log('[WxSDK] 用户已授权隐私');
resolve(true);
},
fail: (err: any) => {
console.warn('[WxSDK] 用户拒绝或授权失败:', err);
resolve(false);
},
complete: () => {
WxSDK._privacyAuthorizePromise = null;
}
});
});
return WxSDK._privacyAuthorizePromise;
}
/**
* 启动阶段使用的隐私授权入口:主动调用 requirePrivacyAuthorize
* 让微信在合适时机触发官方隐私弹窗逻辑。
*/
static ensurePrivacyAuthorized(): Promise<boolean> {
if (!WxSDK.isWechat()) {
return Promise.resolve(true);
}
return WxSDK.requirePrivacyAuthorize();
}
// ==================== 分享相关 ====================
/**
* 开启转发/分享菜单
* 调用后用户可通过右上角菜单进行转发
* @param withShareTicket 是否带 shareTicket用于获取群信息
*/
static showShareMenu(withShareTicket: boolean = true): void {
const wxApi = WxSDK.getWx();
if (!wxApi) return;
wxApi.showShareMenu({
withShareTicket,
menus: ['shareAppMessage', 'shareTimeline'],
success: () => {
console.log('[WxSDK] showShareMenu 成功');
},
fail: (err: any) => {
console.warn('[WxSDK] showShareMenu 失败', err);
}
});
}
/**
* 设置被动分享(右上角菜单 "转发给朋友")的内容
* 需要在页面加载后尽早调用,只需调用一次
* @param config 分享配置
*/
static onShareAppMessage(config: WxShareConfig): void {
const wxApi = WxSDK.getWx();
if (!wxApi) return;
wxApi.onShareAppMessage(() => ({
title: config.title,
imageUrl: config.imageUrl ?? '',
query: config.query ?? ''
}));
console.log('[WxSDK] onShareAppMessage 已设置');
}
/**
* 设置分享到朋友圈的内容
* @param config 朋友圈分享配置
*/
static onShareTimeline(config: WxShareTimelineConfig): void {
const wxApi = WxSDK.getWx();
if (!wxApi) return;
if (typeof wxApi.onShareTimeline !== 'function') {
console.warn('[WxSDK] 当前微信版本不支持 onShareTimeline');
return;
}
wxApi.onShareTimeline(() => ({
title: config.title,
imageUrl: config.imageUrl ?? '',
query: config.query ?? ''
}));
console.log('[WxSDK] onShareTimeline 已设置');
}
/**
* 主动触发转发(拉起分享面板)
* @param config 分享配置
*/
static shareAppMessage(config: WxShareConfig): void {
const wxApi = WxSDK.getWx();
if (!wxApi) return;
wxApi.shareAppMessage({
title: config.title,
imageUrl: config.imageUrl ?? '',
query: config.query ?? ''
});
console.log('[WxSDK] shareAppMessage 已触发');
}
/**
* 一键初始化分享功能
* 开启分享菜单 + 设置被动分享内容 + 设置朋友圈分享内容
* @param config 分享配置
*/
static initShare(config: WxShareConfig): void {
if (!WxSDK.isWechat()) {
console.log('[WxSDK] 非微信环境,跳过分享初始化');
return;
}
WxSDK.showShareMenu();
WxSDK.onShareAppMessage(config);
WxSDK.onShareTimeline({
title: config.title,
imageUrl: config.imageUrl,
query: config.query
});
console.log('[WxSDK] 分享功能初始化完成');
}
// ==================== 震动相关 ====================
/**
* 触发短震动15ms
* 用于轻量级反馈,如按钮点击
*/
static vibrateShort(): void {
const wxApi = WxSDK.getWx();
if (!wxApi) return;
wxApi.vibrateShort({
type: 'medium',
success: () => {
console.log('[WxSDK] 短震动成功');
},
fail: (err: any) => {
console.warn('[WxSDK] 短震动失败', err);
}
});
}
/**
* 触发长震动400ms
* 用于重要反馈,如错误提示
*/
static vibrateLong(): void {
const wxApi = WxSDK.getWx();
if (!wxApi) return;
wxApi.vibrateLong({
success: () => {
console.log('[WxSDK] 长震动成功');
},
fail: (err: any) => {
console.warn('[WxSDK] 长震动失败', err);
}
});
}
// ==================== 激励视频广告 ====================
/** 激励视频广告实例(复用) */
private static _rewardedVideoAd: any = null;
/**
* 展示激励视频广告
* 用户看完广告后返回 true中途退出或失败返回 false
* 非微信环境直接返回 true开发模式直接通过
* @param adUnitId 广告单元 ID默认使用项目配置的 ID
* @returns Promise<boolean> 是否看完广告
*/
static showRewardedVideoAd(adUnitId: string = ''): Promise<boolean> {
return new Promise((resolve) => {
const wxApi = WxSDK.getWx();
if (!wxApi) {
console.log('[WxSDK] 非微信环境,跳过激励视频广告');
resolve(true);
return;
}
if (typeof wxApi.createRewardedVideoAd !== 'function') {
console.warn('[WxSDK] 当前微信版本不支持激励视频广告');
resolve(true);
return;
}
try {
// 复用或创建广告实例
if (!WxSDK._rewardedVideoAd) {
WxSDK._rewardedVideoAd = wxApi.createRewardedVideoAd({
adUnitId: adUnitId,
});
}
const ad = WxSDK._rewardedVideoAd;
// 定义关闭回调(一次性)
const onClose = (res: any) => {
ad.offClose(onClose);
if (res && res.isEnded) {
console.log('[WxSDK] 激励视频广告观看完成');
resolve(true);
} else {
console.log('[WxSDK] 激励视频广告中途退出');
resolve(false);
}
};
// 定义错误回调(一次性)
const onError = (err: any) => {
ad.offError(onError);
ad.offClose(onClose);
console.error('[WxSDK] 激励视频广告错误:', err);
resolve(false);
};
ad.onClose(onClose);
ad.onError(onError);
// 先尝试 show如果广告未加载则先 load
ad.show().catch(() => {
ad.load().then(() => ad.show()).catch((loadErr: any) => {
ad.offClose(onClose);
ad.offError(onError);
console.error('[WxSDK] 激励视频广告加载失败:', loadErr);
resolve(false);
});
});
} catch (err) {
console.error('[WxSDK] 激励视频广告异常:', err);
resolve(false);
}
});
}
// ==================== 启动参数 ====================
/**
* 从启动参数中获取分享码
* @returns 分享码,不存在则返回 null
*/
static getShareCodeFromLaunch(): string | null {
const wxApi = WxSDK.getWx();
if (!wxApi) return null;
try {
const options = wxApi.getLaunchOptionsSync();
const code = WxSDK.extractShareCodeFromQuery(options?.query);
if (code) {
console.log('[WxSDK] 检测到分享码:', code);
return code;
}
} catch (err) {
console.warn('[WxSDK] 获取启动参数失败:', err);
}
return null;
}
/**
* 从查询对象(来自 launch options 或 onShow 回调)中提取 shareCode
*/
static extractShareCodeFromQuery(query: Record<string, any> | null | undefined): string | null {
const code = query?.shareCode;
return typeof code === 'string' && code.length > 0 ? code : null;
}
// ==================== 前后台生命周期 ====================
/**
* 监听小游戏切到前台事件。
* 同一个回调可重复注册多次:内部用 wx.onShow请确保业务层做幂等处理或在卸载时调用 offAppShow。
* @param callback 切前台时触发,包含本次显示对应的 query / scene 等参数
*/
static onAppShow(callback: (res: { query?: Record<string, any>; scene?: number; path?: string } | undefined) => void): void {
const wxApi = WxSDK.getWx();
if (!wxApi) return;
if (typeof wxApi.onShow !== 'function') {
console.warn('[WxSDK] 当前微信版本不支持 onShow');
return;
}
wxApi.onShow(callback);
}
/**
* 取消监听小游戏切到前台事件
*/
static offAppShow(callback: (res: any) => void): void {
const wxApi = WxSDK.getWx();
if (!wxApi) return;
if (typeof wxApi.offShow === 'function') {
wxApi.offShow(callback);
}
}
/**
* 监听小游戏切到后台事件
*/
static onAppHide(callback: () => void): void {
const wxApi = WxSDK.getWx();
if (!wxApi) return;
if (typeof wxApi.onHide !== 'function') {
console.warn('[WxSDK] 当前微信版本不支持 onHide');
return;
}
wxApi.onHide(callback);
}
/**
* 取消监听小游戏切到后台事件
*/
static offAppHide(callback: () => void): void {
const wxApi = WxSDK.getWx();
if (!wxApi) return;
if (typeof wxApi.offHide === 'function') {
wxApi.offHide(callback);
}
}
}
// ==================== 隐私授权相关 ====================
/**
* 检查用户是否已授权隐私
* @returns Promise<{ needAuthorization: boolean }> needAuthorization 为 false 表示已授权
*/
export async function checkPrivacySetting(): Promise<{ needAuthorization: boolean }> {
return WxSDK.checkPrivacySetting();
}
/**
* 引导用户进行隐私授权
* 调用后会弹出微信隐私授权弹窗
* @returns Promise<void>
*/
export async function requirePrivacyAuthorize(): Promise<void> {
const authorized = await WxSDK.requirePrivacyAuthorize();
if (!authorized) {
throw new Error('隐私授权失败');
}
}
// ==================== 用户信息相关 ====================
/**
* 用户信息(头像、昵称)
*/
export interface WxUserInfo {
avatarUrl: string;
nickName: string;
country?: string;
province?: string;
city?: string;
gender?: number;
}
/**
* 获取用户头像和昵称(需要用户主动授权)
* @returns Promise<WxUserInfo>
*/
export async function getUserProfile(): Promise<WxUserInfo> {
return new Promise((resolve, reject) => {
const wxApi = WxSDK.getWx();
if (!wxApi) {
// 非微信环境,返回 mock 数据
console.warn('[WxSDK] 非微信环境,返回 mock 用户信息');
resolve({
avatarUrl: '',
nickName: '微信用户'
});
return;
}
if (typeof wxApi.getUserProfile !== 'function') {
console.warn('[WxSDK] 当前微信版本不支持 getUserProfile');
reject(new Error('当前微信版本不支持 getUserProfile'));
return;
}
wxApi.getUserProfile({
desc: '用于完善用户资料',
success: (res: any) => {
if (res.userInfo) {
console.log('[WxSDK] 获取用户信息成功');
resolve({
avatarUrl: res.userInfo.avatarUrl,
nickName: res.userInfo.nickName,
country: res.userInfo.country,
province: res.userInfo.province,
city: res.userInfo.city,
gender: res.userInfo.gender
});
} else {
console.error('[WxSDK] 获取用户信息失败: 无 userInfo');
reject(new Error('获取用户信息失败'));
}
},
fail: (err: any) => {
console.error('[WxSDK] 获取用户信息失败:', err);
reject(new Error(err.errMsg || '获取用户信息失败'));
}
});
});
}