| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474 |
- /**
- * 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);
- }
|