Files
wxSDK/index.js
2025-08-17 00:15:47 +08:00

268 lines
7.4 KiB
JavaScript
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.

/**
* 微信分享 SDK 封装
* @param {Object} options 配置选项
* @returns {Promise} 返回 Promise 对象
*/
const wxSDK = (options = {}) => {
return new Promise((resolve, reject) => {
// 环境检测
const globalThis = typeof global !== 'undefined' ? global : self
const w = typeof window !== 'undefined' ? window : globalThis
if (!w.document) {
return reject(new Error('wxSDK只能在浏览器环境下使用'))
}
// 默认配置
const defaultConfig = {
apiUrl: null,
sdk: 'https://res.wx.qq.com/open/js/jweixin-1.6.0.js',
title: ['分享至朋友圈', '分享至好友'],
desc: '万事皆虚,万物皆允',
shareIcon: `https://src.pandorastudio.cn/favicon.jpg`,
shareLinks: w.location.href,
debug: false,
jsApiList: [],
openTagList: [],
timeout: 5000,
callback: {
ready: null,
success: null,
error: null,
},
}
// 深度合并配置
const config = {
...defaultConfig,
...options,
callback: {
...defaultConfig.callback,
...(options.callback || {}),
},
}
const { apiUrl, sdk, title, desc, shareLinks, debug, jsApiList, openTagList, callback, timeout } = config
// 参数校验
if (!apiUrl) {
return reject(new Error('apiUrl为必填参数'))
}
// 处理分享图标
let { shareIcon } = config
try {
const favicon = document.querySelector('link[rel*="icon"]')
if (favicon && favicon.href) {
shareIcon = favicon.href
}
} catch (e) {
console.warn('网站图标检测失败:', e)
}
// 常量定义
const SCRIPT_ID = 'wxShareSDK'
const BASE_API_LIST = ['updateTimelineShareData', 'updateAppMessageShareData']
const DEFAULT_OPEN_TAGS = ['wx-open-launch-app']
// 清理资源
let scriptTag = document.getElementById(SCRIPT_ID)
let timeoutId
const cleanup = () => {
clearTimeout(timeoutId)
if (scriptTag) {
scriptTag.onload = null
scriptTag.onerror = null
}
}
// 加载微信SDK
const loadSDK = () => {
cleanup()
scriptTag = document.createElement('script')
scriptTag.id = SCRIPT_ID
scriptTag.src = sdk
scriptTag.async = true
scriptTag.defer = true
return new Promise((loadResolve, loadReject) => {
// 加载超时处理
timeoutId = setTimeout(() => {
cleanup()
document.head.removeChild(scriptTag)
loadReject(new Error(`微信JSSDK加载超时 (${timeout}ms)`))
}, timeout)
scriptTag.onload = () => {
cleanup()
loadResolve()
}
scriptTag.onerror = err => {
cleanup()
document.head.removeChild(scriptTag)
loadReject(new Error('微信JSSDK加载失败'))
}
document.head.appendChild(scriptTag)
})
}
// 获取微信签名
const fetchSignature = async () => {
const controller = new AbortController()
const fetchTimeout = setTimeout(() => controller.abort(), timeout)
try {
const response = await fetch(`${apiUrl}${encodeURIComponent(w.location.href.split('#')[0])}`, {
signal: controller.signal,
})
clearTimeout(fetchTimeout)
if (!response.ok) {
throw new Error(`网络请求失败 (${response.status})`)
}
const res = await response.json()
const { appId, timestamp, nonceStr, signature } = res || {}
if (!appId || !timestamp || !nonceStr || !signature) {
throw new Error('接口响应格式错误')
}
return { appId, timestamp, nonceStr, signature }
} catch (err) {
clearTimeout(fetchTimeout)
throw new Error(`签名接口请求错误: ${err.message}`)
}
}
// 配置微信分享
const configureWeChat = async () => {
if (!w.wx || typeof w.wx.config !== 'function') {
throw new Error('微信JSSDK未正确加载')
}
// 获取签名
const signatureData = await fetchSignature()
// 准备分享配置
const timelineConfig = {
title: Array.isArray(title) ? title[0] : title,
link: Array.isArray(shareLinks) ? shareLinks[0] : shareLinks.split('#')[0],
imgUrl: Array.isArray(shareIcon) ? shareIcon[0] : shareIcon,
}
const messageConfig = {
title: Array.isArray(title) ? title[1] : title,
link: Array.isArray(shareLinks) ? shareLinks[1] : shareLinks.split('#')[0],
imgUrl: Array.isArray(shareIcon) ? shareIcon[1] : shareIcon,
desc,
}
// 合并API列表
const fullApiList = [...new Set([...BASE_API_LIST, ...jsApiList])]
const fullOpenTagList = [...new Set([...DEFAULT_OPEN_TAGS, ...openTagList])]
// 初始化微信配置
w.wx.config({
debug,
...signatureData,
jsApiList: fullApiList,
openTagList: fullOpenTagList,
})
// 返回封装后的wx对象
return new Promise((wxResolve, wxReject) => {
w.wx.error(wxReject)
w.wx.ready(() => {
const shareOperations = []
// 配置朋友圈分享
if (w.wx.updateTimelineShareData) {
shareOperations.push(
new Promise(shareResolve => {
w.wx.updateTimelineShareData({
...timelineConfig,
success: () => {
callback.success?.('timeline')
shareResolve()
},
fail: err => {
callback.error?.(err)
shareResolve() // 不阻断流程
},
})
})
)
}
// 配置好友分享
if (w.wx.updateAppMessageShareData) {
shareOperations.push(
new Promise(shareResolve => {
w.wx.updateAppMessageShareData({
...messageConfig,
success: () => {
callback.success?.('message')
shareResolve()
},
fail: err => {
callback.error?.(err)
shareResolve() // 不阻断流程
},
})
})
)
}
// 等待所有分享配置完成
Promise.all(shareOperations)
.then(() => {
callback.ready?.()
wxResolve(w.wx)
})
.catch(e => {
console.error('分享设置部分失败:', e)
callback.ready?.()
wxResolve(w.wx) // 仍返回wx对象
})
})
})
}
// 主执行流程
const initialize = async () => {
try {
// 加载SDK如果需要
if (!w.wx || typeof w.wx.config !== 'function') {
if (scriptTag && scriptTag.parentNode) {
// 等待正在加载的SDK
await new Promise((resolve, reject) => {
scriptTag.onload = resolve
scriptTag.onerror = () => reject(new Error('SDK加载失败'))
})
} else {
await loadSDK()
}
}
// 配置微信功能
const wxInstance = await configureWeChat()
resolve(wxInstance)
} catch (err) {
callback.error?.(err)
reject(err)
}
}
// 启动初始化
initialize()
})
}
export default wxSDK