268 lines
7.4 KiB
JavaScript
268 lines
7.4 KiB
JavaScript
/**
|
||
* 微信分享 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
|