| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462 |
- /**
- * H5与小程序通信桥接工具函数库
- * 提供统一的小程序原生功能调用接口
- */
- // 全局变量
- window.H5_PAGE_NAME = window.H5_PAGE_NAME || 'default'
- /**
- * 设置当前页面名称
- * @param {string} pageName 页面名称
- */
- function setPageName(pageName) {
- window.H5_PAGE_NAME = pageName
- }
- /**
- * 检查微信小程序环境
- * @returns {boolean} 是否在微信小程序环境
- */
- function checkEnvironment() {
- return typeof wx !== 'undefined' && wx.miniProgram
- }
- /**
- * 解析URL参数(兼容性处理)
- * @param {string} search URL查询字符串
- * @returns {Object} 参数对象
- */
- function parseUrlParams(search) {
- const params = {}
- if (search) {
- search.substring(1).split('&').forEach(param => {
- const [key, value] = param.split('=')
- if (key && value) {
- params[decodeURIComponent(key)] = decodeURIComponent(value)
- }
- })
- }
- return params
- }
- /**
- * 检查是否有返回的结果数据
- * @param {Function} onResult 结果处理回调
- */
- function checkResult(onResult) {
- const search = window.location.search
- console.log('🔍 检查URL参数:', search)
- if (search) {
- const params = parseUrlParams(search)
- console.log('🔍 解析的参数:', params)
- if (params.result) {
- try {
- const resultData = JSON.parse(decodeURIComponent(params.result))
- console.log('🔍 解析的结果数据:', resultData)
- // 调用回调处理结果
- if (onResult) {
- onResult(resultData)
- }
- } catch (error) {
- console.error('解析结果数据失败:', error)
- }
- } else {
- console.log('🔍 URL中没有result参数')
- }
- } else {
- console.log('🔍 URL中没有查询参数')
- }
- }
- /**
- * 调用小程序原生功能
- * @param {string} action 操作类型
- * @param {string} message 消息描述
- * @param {Object} data 附加数据
- */
- function callNative(action, message = '', data = null) {
- if (!checkEnvironment()) {
- console.error('❌ 不在微信小程序环境')
- return
- }
- const params = {
- action: action,
- message: message || '',
- source: window.H5_PAGE_NAME
- }
- // 处理附加数据
- if (data && typeof data === 'object') {
- try {
- const jsonString = JSON.stringify(data)
- params.data = encodeURIComponent(jsonString)
- console.log('📦 数据编码:', { original: data, json: jsonString, encoded: params.data })
- } catch (error) {
- console.error('❌ JSON序列化失败:', error)
- return
- }
- }
- // 构建URL参数
- const queryParts = []
- Object.keys(params).forEach(key => {
- if (params[key] !== undefined && params[key] !== null) {
- if (key === 'data') {
- queryParts.push(`${key}=${params[key]}`)
- } else {
- queryParts.push(`${key}=${encodeURIComponent(params[key])}`)
- }
- }
- })
- const controllerUrl = `/pages/common/h5-controller?${queryParts.join('&')}`
- console.log('🎛️ 跳转到控制器:', controllerUrl)
- // 调用微信小程序API,添加降级处理
- wx.miniProgram.navigateTo({
- url: controllerUrl,
- success: (res) => {
- console.log('✅ 成功跳转到控制器',res)
- },
- fail: (err) => {
- console.error('❌ 控制器调用失败,尝试降级方案:', err)
- // 降级方案:使用URL参数通信(兼容老版本webview)
- fallbackUrlCommunication(action, message, data)
- }
- })
- }
- /**
- * 降级通信方案:通过URL参数与webview通信
- * @param {string} action 操作类型
- * @param {string} message 消息描述
- * @param {Object} data 附加数据
- */
- function fallbackUrlCommunication(action, message = '', data = null) {
- console.log('🔄 使用降级通信方案')
- const params = {
- h5_action: action,
- h5_message: message,
- timestamp: Date.now()
- }
- // 如果有额外数据,编码后添加
- if (data && typeof data === 'object') {
- try {
- params.h5_data = encodeURIComponent(JSON.stringify(data))
- } catch (error) {
- console.error('❌ 数据编码失败:', error)
- }
- }
- // 保留原有的非指令参数
- const currentParams = parseUrlParams(window.location.search)
- Object.keys(currentParams).forEach(key => {
- if (!key.startsWith('h5_') && !key.startsWith('mp_') && key !== 'timestamp') {
- params[key] = currentParams[key]
- }
- })
- // 构建新URL
- const baseUrl = `${window.location.origin}${window.location.pathname}`
- const queryParts = []
- Object.keys(params).forEach(key => {
- if (params[key] !== undefined && params[key] !== null) {
- queryParts.push(`${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`)
- }
- })
- const url = `${baseUrl}?${queryParts.join('&')}`
- console.log('🔄 降级跳转URL:', url)
- // 使用location.href触发webview的load事件
- window.location.href = url
- }
- /**
- * 扫码
- * @param {string} message 消息描述
- * @returns {Promise} 调用结果
- */
- function scanCode(message = '扫描二维码') {
- return callNative('scanCode', message)
- }
- /**
- * 选择图片
- * @param {string} message 消息描述
- * @returns {Promise} 调用结果
- */
- function chooseImage(message = '选择图片') {
- return callNative('chooseImage', message)
- }
- /**
- * 拨打电话
- * @param {string} phoneNumber 电话号码
- * @param {string} message 消息描述
- * @returns {Promise} 调用结果
- */
- function makePhoneCall(phoneNumber, message = '拨打电话') {
- return callNative('makePhoneCall', message, { phoneNumber })
- }
- /**
- * 设置存储
- * @param {string} key 存储键
- * @param {*} value 存储值
- * @param {string} message 消息描述
- * @returns {Promise} 调用结果
- */
- function setStorage(key, value, message = '设置存储数据') {
- return callNative('setStorage', message, { key, value })
- }
- /**
- * 获取存储
- * @param {string} key 存储键
- * @param {string} message 消息描述
- * @returns {Promise} 调用结果
- */
- function getStorage(key, message = '获取存储数据') {
- return callNative('getStorage', message, { key })
- }
- /**
- * 返回小程序
- * @param {string} message 消息描述
- * @returns {Promise} 调用结果
- */
- function goBack(message = '返回小程序') {
- return callNative('goBack', message)
- }
- /**
- * 页面跳转
- * @param {string} page 目标页面
- * @param {Object} params 跳转参数
- */
- function navigateTo(page, params = {}) {
- const queryString = Object.keys(params).length > 0
- ? '?' + Object.keys(params).map(key => `${key}=${encodeURIComponent(params[key])}`).join('&')
- : ''
- window.location.href = `./${page}.html${queryString}`
- }
- /**
- * 保存数据到localStorage
- * @param {string} key 存储键
- * @param {*} data 存储数据
- */
- function saveData(key, data) {
- try {
- const storageKey = `h5_${window.H5_PAGE_NAME}_${key}`
- localStorage.setItem(storageKey, JSON.stringify(data))
- console.log('💾 数据已保存:', storageKey)
- } catch (error) {
- console.error('保存数据失败:', error)
- }
- }
- /**
- * 从localStorage加载数据
- * @param {string} key 存储键
- * @param {*} defaultValue 默认值
- * @returns {*} 加载的数据
- */
- function loadData(key, defaultValue = null) {
- try {
- const storageKey = `h5_${window.H5_PAGE_NAME}_${key}`
- const savedData = localStorage.getItem(storageKey)
- if (savedData) {
- return JSON.parse(savedData)
- }
- } catch (error) {
- console.error('加载数据失败:', error)
- }
- return defaultValue
- }
- /**
- * 清除保存的数据
- * @param {string} key 存储键
- */
- function clearData(key) {
- try {
- const storageKey = `h5_${window.H5_PAGE_NAME}_${key}`
- localStorage.removeItem(storageKey)
- console.log('🗑️ 数据已清除:', storageKey)
- } catch (error) {
- console.error('清除数据失败:', error)
- }
- }
- /**
- * 默认的结果处理函数
- * @param {Object} data 结果数据
- */
- function defaultResultHandler(data) {
- const { action, success, result, error } = data
- if (success) {
- console.log(`✅ ${action} 操作成功`, result)
- } else {
- console.error(`❌ ${action} 操作失败`, error)
- }
- }
- /**
- * 初始化页面(可选调用)
- * @param {string} pageName 页面名称
- * @param {Function} onResult 结果处理回调,不传则使用默认处理
- */
- function initPage(pageName, onResult) {
- setPageName(pageName)
- checkEnvironment()
- // 如果没有提供自定义处理,使用默认处理
- const resultHandler = onResult || defaultResultHandler
- checkResult(resultHandler)
- }
- // ==================== UI工具函数 ====================
- /**
- * 显示Toast效果(H5页面UI)
- * @param {string} message Toast消息
- * @param {number} duration 显示时长(毫秒)
- * @param {string} type 类型:success|error|warning|info
- */
- function showToastEffect(message, duration = 2000, type = 'success') {
- const colors = {
- success: 'rgba(40, 167, 69, 0.9)',
- error: 'rgba(220, 53, 69, 0.9)',
- warning: 'rgba(255, 193, 7, 0.9)',
- info: 'rgba(23, 162, 184, 0.9)'
- }
- const toast = document.createElement('div')
- toast.textContent = message
- toast.style.cssText = `
- position: fixed;
- top: 50%;
- left: 50%;
- transform: translate(-50%, -50%);
- background: ${colors[type] || colors.success};
- color: white;
- padding: 12px 24px;
- border-radius: 8px;
- z-index: 9999;
- font-size: 14px;
- pointer-events: none;
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
- `
- document.body.appendChild(toast)
- setTimeout(() => {
- if (toast.parentNode) {
- toast.parentNode.removeChild(toast)
- }
- }, duration)
- }
- /**
- * 显示加载中效果
- * @param {string} message 加载消息
- * @returns {Function} 关闭加载的函数
- */
- function showLoading(message = '加载中...') {
- const loading = document.createElement('div')
- loading.innerHTML = `
- <div style="
- display: flex;
- align-items: center;
- justify-content: center;
- flex-direction: column;
- color: white;
- ">
- <div style="
- width: 30px;
- height: 30px;
- border: 3px solid rgba(255, 255, 255, 0.3);
- border-top: 3px solid white;
- border-radius: 50%;
- animation: spin 1s linear infinite;
- margin-bottom: 12px;
- "></div>
- <div>${message}</div>
- </div>
- `
- loading.style.cssText = `
- position: fixed;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- background: rgba(0, 0, 0, 0.7);
- display: flex;
- align-items: center;
- justify-content: center;
- z-index: 10000;
- font-size: 14px;
- `
- // 添加旋转动画
- if (!document.querySelector('#loading-style')) {
- const style = document.createElement('style')
- style.id = 'loading-style'
- style.textContent = `
- @keyframes spin {
- 0% { transform: rotate(0deg); }
- 100% { transform: rotate(360deg); }
- }
- `
- document.head.appendChild(style)
- }
- document.body.appendChild(loading)
- return function closeLoading() {
- if (loading.parentNode) {
- loading.parentNode.removeChild(loading)
- }
- }
- }
- /**
- * 格式化日期
- * @param {Date|string|number} date 日期
- * @param {string} format 格式
- * @returns {string} 格式化后的日期
- */
- function formatDate(date, format = 'YYYY-MM-DD HH:mm:ss') {
- const d = new Date(date)
- const year = d.getFullYear()
- const month = String(d.getMonth() + 1).padStart(2, '0')
- const day = String(d.getDate()).padStart(2, '0')
- const hours = String(d.getHours()).padStart(2, '0')
- const minutes = String(d.getMinutes()).padStart(2, '0')
- const seconds = String(d.getSeconds()).padStart(2, '0')
- return format
- .replace('YYYY', year)
- .replace('MM', month)
- .replace('DD', day)
- .replace('HH', hours)
- .replace('mm', minutes)
- .replace('ss', seconds)
- }
|