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 | 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 登录 code */ static login(): Promise { 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 { 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 { 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 { 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 是否看完广告 */ static showRewardedVideoAd(adUnitId: string = ''): Promise { 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(); if (options?.query?.shareCode) { console.log('[WxSDK] 检测到分享码:', options.query.shareCode); return options.query.shareCode; } } catch (err) { console.warn('[WxSDK] 获取启动参数失败:', err); } return null; } } // ==================== 隐私授权相关 ==================== /** * 检查用户是否已授权隐私 * @returns Promise<{ needAuthorization: boolean }> needAuthorization 为 false 表示已授权 */ export async function checkPrivacySetting(): Promise<{ needAuthorization: boolean }> { return WxSDK.checkPrivacySetting(); } /** * 引导用户进行隐私授权 * 调用后会弹出微信隐私授权弹窗 * @returns Promise */ export async function requirePrivacyAuthorize(): Promise { 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 */ export async function getUserProfile(): Promise { 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 || '获取用户信息失败')); } }); }); }