diff --git a/Appbuild/config.js b/Appbuild/config.js index 37bb7d4f..954f5baa 100644 --- a/Appbuild/config.js +++ b/Appbuild/config.js @@ -9,7 +9,7 @@ const config = { fallbackUrl: "https://kr-op.quwanzhi.com", // 测试页面URL(用于开发测试) - testUrl: "https://kr-op.quwanzhi.com", + testUrl: "https://kr-op.quwanzhi.com/test/postMessage", // testUrl: "http://localhost:3000/test/postMessage", // 是否启用HTTPS强制跳转 diff --git a/Appbuild/unpackage/dist/dev/app-plus/app-service.js b/Appbuild/unpackage/dist/dev/app-plus/app-service.js index 91c6a5b3..4ff7a787 100644 --- a/Appbuild/unpackage/dist/dev/app-plus/app-service.js +++ b/Appbuild/unpackage/dist/dev/app-plus/app-service.js @@ -46,7 +46,7 @@ if (uni.restoreGlobal) { // // 备用网站URL(如果主网站无法访问) fallbackUrl: "https://kr-op.quwanzhi.com", // 测试页面URL(用于开发测试) - testUrl: "https://kr-op.quwanzhi.com", + testUrl: "https://kr-op.quwanzhi.com/test/postMessage", // testUrl: "http://localhost:3000/test/postMessage", // 是否启用HTTPS强制跳转 forceHttps: false, diff --git a/nkebao/APP打包方案.md b/nkebao/APP打包方案.md new file mode 100644 index 00000000..82608265 --- /dev/null +++ b/nkebao/APP打包方案.md @@ -0,0 +1,133 @@ +# Cunkebao APP 打包方案 + +## 方案一:Capacitor(推荐) + +Capacitor 是 Ionic 团队开发的跨平台原生应用打包工具,可以将 React 项目打包成原生 Android/iOS 应用。 + +### 安装步骤 + +```bash +# 1. 安装 Capacitor +npm install @capacitor/core @capacitor/cli + +# 2. 初始化 Capacitor +npx cap init + +# 3. 添加平台 +npm run cap:add android +npm run cap:add ios +``` + +### 构建流程 + +```bash +# 1. 构建 React 项目 +npm run build + +# 2. 复制构建文件到原生项目 +npm run cap:copy + +# 3. 同步依赖 +npm run cap:sync + +# 4. 打开原生开发环境 +npm run cap:open:android # Android Studio +npm run cap:open:ios # Xcode +``` + +### 优势 + +- 真正的原生应用体验 +- 支持原生 API 调用 +- 性能优秀 +- 支持热更新 + +--- + +## 方案二:PWA(渐进式 Web 应用) + +将项目打包成 PWA,用户可以通过浏览器安装到桌面。 + +### 安装 Workbox + +```bash +npm install workbox-webpack-plugin +``` + +### 配置 PWA + +在 `vite.config.ts` 中添加 PWA 插件配置。 + +### 优势 + +- 无需应用商店审核 +- 跨平台兼容性好 +- 更新方便 + +--- + +## 方案三:Tauri + +Tauri 使用 Rust 后端,可以创建更轻量级的桌面应用。 + +### 安装 Tauri + +```bash +npm install @tauri-apps/cli +npm install @tauri-apps/api +``` + +### 初始化 + +```bash +npx tauri init +``` + +### 优势 + +- 应用体积小 +- 性能优秀 +- 安全性高 + +--- + +## 方案四:Electron + +传统的桌面应用打包方案。 + +### 安装 Electron + +```bash +npm install electron electron-builder --save-dev +``` + +### 优势 + +- 生态成熟 +- 文档丰富 +- 社区支持好 + +--- + +## 推荐方案 + +对于你的移动端项目,推荐使用 **Capacitor**: + +1. **最适合移动端**:你的项目使用了 antd-mobile,专门为移动端设计 +2. **原生体验**:可以获得真正的原生应用体验 +3. **跨平台**:一套代码可以打包成 Android 和 iOS 应用 +4. **性能优秀**:比 WebView 方案性能更好 + +### 快速开始 + +1. 安装 Capacitor 依赖 +2. 运行 `npm run cap:build:android` 或 `npm run cap:build:ios` +3. 使用 Android Studio 或 Xcode 打开生成的项目 +4. 构建并运行应用 + +### 注意事项 + +- Android 需要安装 Android Studio 和 Android SDK +- iOS 需要 macOS 系统和 Xcode +- 确保 `capacitor.config.ts` 中的配置正确 +- 可能需要根据实际需求调整应用图标、启动画面等 diff --git a/nkebao/capacitor.config.ts b/nkebao/capacitor.config.ts new file mode 100644 index 00000000..6b0267cf --- /dev/null +++ b/nkebao/capacitor.config.ts @@ -0,0 +1,23 @@ +import { CapacitorConfig } from "@capacitor/cli"; + +const config: CapacitorConfig = { + appId: "com.cunkebao.app", + appName: "Cunkebao", + webDir: "dist", + server: { + androidScheme: "https", + }, + plugins: { + SplashScreen: { + launchShowDuration: 2000, + backgroundColor: "#ffffff", + showSpinner: true, + spinnerColor: "#999999", + }, + StatusBar: { + style: "dark", + }, + }, +}; + +export default config; diff --git a/nkebao/package.json b/nkebao/package.json index a8f4b087..13fe9f36 100644 --- a/nkebao/package.json +++ b/nkebao/package.json @@ -3,6 +3,7 @@ "version": "3.0.0", "license": "MIT", "private": true, + "homepage": "./", "dependencies": { "@ant-design/icons": "^5.6.1", "antd": "^5.13.1", @@ -44,6 +45,13 @@ "lint": "eslint src --ext .js,.jsx,.ts,.tsx --fix", "format": "prettier --write \"src/**/*.{js,jsx,ts,tsx,json,scss,css}\"", "lint:check": "eslint src --ext .js,.jsx,.ts,.tsx", - "format:check": "prettier --check \"src/**/*.{js,jsx,ts,tsx,json,scss,css}\"" + "format:check": "prettier --check \"src/**/*.{js,jsx,ts,tsx,json,scss,css}\"", + "cap:add": "cap add", + "cap:copy": "cap copy", + "cap:sync": "cap sync", + "cap:open:android": "cap open android", + "cap:open:ios": "cap open ios", + "cap:build:android": "npm run build && cap copy && cap sync", + "cap:build:ios": "npm run build && cap copy && cap sync" } } diff --git a/nkebao/src/pages/mobile/mine/main/index.tsx b/nkebao/src/pages/mobile/mine/main/index.tsx index c4fe98c6..95fb4090 100644 --- a/nkebao/src/pages/mobile/mine/main/index.tsx +++ b/nkebao/src/pages/mobile/mine/main/index.tsx @@ -78,16 +78,6 @@ const Mine: React.FC = () => { bgColor: "#fff7e6", iconColor: "#fa8c16", }, - { - id: "test", - title: "测试", - description: "测试", - icon: , - count: 0, - path: "/test/postMessage", - bgColor: "#fff7e6", - iconColor: "#fa8c16", - }, ]; // 加载统计数据 diff --git a/nkebao/src/pages/mobile/test/postMessage.tsx b/nkebao/src/pages/mobile/test/postMessage.tsx deleted file mode 100644 index 7146e8d9..00000000 --- a/nkebao/src/pages/mobile/test/postMessage.tsx +++ /dev/null @@ -1,646 +0,0 @@ -import React, { useState, useEffect } from "react"; -import { - Button, - Card, - Space, - Divider, - Typography, - Alert, - Tag, - Avatar, - Row, - Col, -} from "antd"; -import { - UserOutlined, - MobileOutlined, - MessageOutlined, - ExclamationCircleOutlined, - CheckCircleOutlined, - ShareAltOutlined, - PayCircleOutlined, - SendOutlined, - InfoCircleOutlined, - LoadingOutlined, -} from "@ant-design/icons"; -import bridge, { - ResponseType, - type UserInfo, - type DeviceInfo, - type ShareData, - type PaymentData, -} from "@/utils/postMessage"; -import Layout from "@/components/Layout/Layout"; -import NavCommon from "@/components/NavCommon"; - -const { Text, Paragraph } = Typography; - -const PostMessageTest: React.FC = () => { - const [isReady, setIsReady] = useState(false); - const [userInfo, setUserInfo] = useState(null); - const [deviceInfo, setDeviceInfo] = useState(null); - const [loading, setLoading] = useState(false); - const [logs, setLogs] = useState([]); - const [lastResult, setLastResult] = useState(null); - - // 添加日志 - const addLog = (message: string) => { - const timestamp = new Date().toLocaleTimeString(); - setLogs(prev => [`[${timestamp}] ${message}`, ...prev.slice(0, 19)]); - }; - - // 初始化桥接 - useEffect(() => { - const initBridge = async () => { - try { - addLog("正在初始化UniApp桥接..."); - - // 立即检查桥接状态 - const initialStatus = bridge.getBridgeStatus(); - addLog(`初始桥接状态: ${JSON.stringify(initialStatus, null, 2)}`); - - // 设置超时处理 - const timeoutPromise = new Promise((_, reject) => { - setTimeout(() => reject(new Error("桥接初始化超时 (30秒)")), 30000); - }); - - // 等待桥接就绪,带超时 - await Promise.race([bridge.waitForReady(), timeoutPromise]); - - setIsReady(true); - addLog("✅ UniApp桥接初始化成功"); - - // 设置监听器 - bridge.on(ResponseType.USER_INFO, (data: UserInfo) => { - setUserInfo(data); - addLog(`收到用户信息: ${data.name}`); - }); - - bridge.on(ResponseType.DEVICE_INFO, (data: DeviceInfo) => { - setDeviceInfo(data); - addLog(`收到设备信息: ${data.platform} ${data.model}`); - }); - - bridge.on(ResponseType.SHARE_RESULT, (result: any) => { - setLastResult(result); - if (result.success) { - addLog("✅ 分享成功"); - } else { - addLog(`❌ 分享失败: ${result.error}`); - } - }); - - bridge.on(ResponseType.PAYMENT_RESULT, (result: any) => { - setLastResult(result); - if (result.success) { - addLog(`✅ 支付成功,订单ID: ${result.orderId}`); - } else { - addLog(`❌ 支付失败: ${result.error}`); - } - }); - - bridge.on(ResponseType.CONFIRM_RESULT, (result: any) => { - setLastResult(result); - addLog(`用户${result.confirmed ? "确认" : "取消"}了操作`); - }); - - // 通知页面准备就绪 - bridge.notifyPageReady({ - version: "1.0.0", - features: ["postMessage", "bridge"], - }); - addLog("已通知页面准备就绪"); - - // 延迟后再次检查状态 - setTimeout(() => { - const finalStatus = bridge.getBridgeStatus(); - addLog(`最终桥接状态: ${JSON.stringify(finalStatus, null, 2)}`); - }, 2000); - } catch (error) { - const errorMessage = - error instanceof Error ? error.message : String(error); - addLog(`❌ 桥接初始化失败: ${errorMessage}`); - - // 提供重试建议 - addLog("💡 建议检查:"); - addLog(" 1. 是否在UniApp WebView环境中运行"); - addLog(" 2. 网络连接是否正常"); - addLog(" 3. 尝试刷新页面重试"); - addLog(" 4. 检查UniApp端控制台日志"); - } - }; - - initBridge(); - - return () => { - bridge.destroy(); - }; - }, []); - - // 获取用户信息 - const handleGetUserInfo = async () => { - setLoading(true); - try { - bridge.getUserInfo(); - addLog("已请求用户信息"); - } catch (error) { - addLog(`获取用户信息失败: ${error}`); - } finally { - setLoading(false); - } - }; - - // 获取设备信息 - const handleGetDeviceInfo = async () => { - setLoading(true); - try { - bridge.getDeviceInfo(); - addLog("已请求设备信息"); - } catch (error) { - addLog(`获取设备信息失败: ${error}`); - } finally { - setLoading(false); - } - }; - - // 显示Toast - const handleShowToast = async () => { - try { - bridge.showToast("这是一个测试Toast消息!", 3000); - addLog("已显示Toast消息"); - } catch (error) { - addLog(`显示Toast失败: ${error}`); - } - }; - - // 显示Alert - const handleShowAlert = async () => { - try { - bridge.showAlert("测试提示", "这是一个测试Alert对话框!"); - addLog("已显示Alert对话框"); - } catch (error) { - addLog(`显示Alert失败: ${error}`); - } - }; - - // 显示Confirm - const handleShowConfirm = async () => { - setLoading(true); - try { - const confirmed = await bridge.showConfirm( - "确认操作", - "确定要执行此测试操作吗?", - ); - setLastResult({ confirmed }); - if (confirmed) { - addLog("用户确认了操作"); - } else { - addLog("用户取消了操作"); - } - } catch (error) { - addLog(`显示Confirm失败: ${error}`); - } finally { - setLoading(false); - } - }; - - // 分享内容 - const handleShare = async () => { - setLoading(true); - try { - const shareData: ShareData = { - title: "UniApp桥接测试", - content: "这是一个UniApp WebView桥接功能的测试页面", - url: window.location.href, - image: "https://via.placeholder.com/300x200/4CAF50/FFFFFF?text=Test", - }; - - const success = await bridge.share(shareData); - setLastResult({ success }); - if (success) { - addLog("分享成功"); - } else { - addLog("分享失败"); - } - } catch (error) { - addLog(`分享失败: ${error}`); - } finally { - setLoading(false); - } - }; - - // 支付测试 - const handlePayment = async () => { - setLoading(true); - try { - const paymentData: PaymentData = { - amount: 100, - orderId: "test_order_" + Date.now(), - description: "测试支付", - currency: "CNY", - }; - - const success = await bridge.payment(paymentData); - setLastResult({ success, orderId: paymentData.orderId }); - if (success) { - addLog(`支付成功,订单ID: ${paymentData.orderId}`); - } else { - addLog("支付失败"); - } - } catch (error) { - addLog(`支付失败: ${error}`); - } finally { - setLoading(false); - } - }; - - // 发送自定义消息 - const handleCustomMessage = async () => { - try { - bridge.sendCustomMessage("testAction", { - action: "test", - timestamp: Date.now(), - data: { message: "Hello from React!" }, - }); - addLog("已发送自定义消息"); - } catch (error) { - addLog(`发送自定义消息失败: ${error}`); - } - }; - - // 页面导航 - const handleNavigate = async () => { - try { - bridge.navigate("https://example.com"); - addLog("已请求页面导航"); - } catch (error) { - addLog(`页面导航失败: ${error}`); - } - }; - - return ( - }> - - {/* 状态指示器 */} - - {isReady ? ( - <> - - 桥接已就绪 - > - ) : ( - <> - - 桥接初始化中... - > - )} - - } - type={isReady ? "success" : "info"} - showIcon={false} - style={{ marginBottom: "20px" }} - /> - - - {/* 左侧:功能测试 */} - - - - } - onClick={handleGetUserInfo} - loading={loading} - block - > - 获取用户信息 - - - } - onClick={handleGetDeviceInfo} - loading={loading} - block - > - 获取设备信息 - - - - - } - onClick={handleShowToast} - block - > - 显示Toast - - - } - onClick={handleShowAlert} - block - > - 显示Alert - - - } - onClick={handleShowConfirm} - loading={loading} - block - > - 显示Confirm - - - - - } - onClick={handleShare} - loading={loading} - block - > - 分享内容 - - - } - onClick={handlePayment} - loading={loading} - block - > - 支付测试 - - - - - } - onClick={handleCustomMessage} - block - > - 发送自定义消息 - - - } - onClick={handleNavigate} - block - > - 页面导航 - - - - - } - onClick={() => { - bridge.checkBridgeStatus(); - const status = bridge.getBridgeStatus(); - addLog(`桥接状态: ${JSON.stringify(status, null, 2)}`); - }} - block - > - 检查桥接状态 - - - } - onClick={() => { - // 测试直接发送消息 - try { - if (window.uniAppBridge) { - window.uniAppBridge.postMessage("test", { - message: "测试消息", - }); - addLog("✅ 直接发送测试消息成功"); - } else { - addLog("❌ window.uniAppBridge 不存在"); - } - } catch (error) { - addLog(`❌ 直接发送消息失败: ${error}`); - } - }} - block - > - 测试直接发送消息 - - - } - onClick={() => { - // 测试监听器状态 - const status = bridge.getBridgeStatus(); - addLog(`监听器数量: ${status.listenersCount}`); - addLog(`消息队列长度: ${status.messageQueueLength}`); - addLog(`桥接就绪: ${status.isReady}`); - addLog(`桥接存在: ${status.bridgeExists}`); - }} - block - > - 检查监听器状态 - - - } - onClick={() => { - // 强制重新初始化桥接 - addLog("🔄 强制重新初始化桥接..."); - bridge.destroy(); - - // 重新创建桥接实例 - setTimeout(() => { - try { - // 重新初始化 - bridge - .waitForReady() - .then(() => { - setIsReady(true); - addLog("✅ 强制重新初始化成功"); - }) - .catch(error => { - addLog(`❌ 强制重新初始化失败: ${error}`); - }); - } catch (error) { - addLog(`❌ 强制重新初始化异常: ${error}`); - } - }, 1000); - }} - block - > - 强制重新初始化 - - - } - onClick={() => { - // 环境检测 - addLog("🔍 环境检测开始..."); - addLog(`用户代理: ${navigator.userAgent}`); - addLog(`当前URL: ${window.location.href}`); - addLog(`父窗口: ${!!window.parent}`); - addLog(`顶层窗口: ${!!window.top}`); - addLog(`是否在iframe中: ${window !== window.top}`); - addLog(`window.uniAppBridge: ${!!window.uniAppBridge}`); - - // 检查是否在UniApp环境中 - const isInUniApp = - navigator.userAgent.includes("uni-app") || - window.location.href.includes("uni-app") || - !!window.uniAppBridge; - addLog(`是否在UniApp环境: ${isInUniApp}`); - - // 检查平台信息 - if (window.uniAppBridge) { - addLog("✅ 桥接已存在,可以正常通信"); - } else { - addLog("❌ 桥接不存在,可能不在UniApp环境中"); - } - }} - block - > - 环境检测 - - - - - - {/* 右侧:信息显示 */} - - {/* 用户信息 */} - {userInfo && ( - - - } /> - - - {userInfo.name} - - - ID: {userInfo.id} - - {userInfo.phone && ( - - 电话: {userInfo.phone} - - )} - {userInfo.email && ( - - 邮箱: {userInfo.email} - - )} - - - - )} - - {/* 设备信息 */} - {deviceInfo && ( - - - - 平台:{" "} - {deviceInfo.platform} - - - 型号: {deviceInfo.model} - - - 版本: {deviceInfo.version} - - - 应用:{" "} - - {deviceInfo.appName} v{deviceInfo.appVersion} - - - - 屏幕:{" "} - - {deviceInfo.screenWidth} × {deviceInfo.screenHeight} - - - - - )} - - {/* 最后结果 */} - {lastResult && ( - - - {JSON.stringify(lastResult, null, 2)} - - - )} - - - - {/* 日志区域 */} - - - {logs.length === 0 ? ( - 暂无日志 - ) : ( - logs.map((log, index) => ( - - {log} - - )) - )} - - - - {/* 使用说明 */} - - - 功能说明: - - - - 此页面用于测试UniApp WebView桥接功能 - - - 点击按钮可以测试各种桥接方法 - - - 右侧会显示接收到的用户信息和设备信息 - - - 底部日志区域会显示所有操作记录 - - - 确保在UniApp WebView环境中使用以获得最佳效果 - - - - - - ); -}; - -export default PostMessageTest; diff --git a/nkebao/src/router/module/test.tsx b/nkebao/src/router/module/test.tsx index abd9baf5..becad3a3 100644 --- a/nkebao/src/router/module/test.tsx +++ b/nkebao/src/router/module/test.tsx @@ -16,11 +16,6 @@ const componentTestRoutes = DEV_FEATURES.SHOW_TEST_PAGES element: , auth: true, }, - { - path: "/test/postMessage", - element: , - auth: true, - }, ] : []; diff --git a/nkebao/src/utils/postMessage.ts b/nkebao/src/utils/postMessage.ts deleted file mode 100644 index 8ff281f9..00000000 --- a/nkebao/src/utils/postMessage.ts +++ /dev/null @@ -1,634 +0,0 @@ -// UniApp WebView 桥接工具类 -import { DEV_FEATURES } from "./env"; - -export interface BridgeMessage { - type: string; - data: any; - timestamp: number; -} - -export interface UserInfo { - id: string; - name: string; - avatar: string; - phone?: string; - email?: string; -} - -export interface DeviceInfo { - platform: string; - model: string; - version: string; - appVersion: string; - appName: string; - screenWidth: number; - screenHeight: number; - statusBarHeight: number; -} - -export interface ShareData { - title: string; - content: string; - url: string; - image?: string; -} - -export interface PaymentData { - amount: number; - orderId: string; - description?: string; - currency?: string; -} - -export interface ToastData { - message: string; - duration?: number; -} - -export interface AlertData { - title: string; - content: string; -} - -export interface ConfirmData { - title: string; - content: string; -} - -// 消息回调函数类型 -export type MessageCallback = (data: any) => void; - -// 消息类型枚举 -export enum MessageType { - GET_USER_INFO = "getUserInfo", - GET_DEVICE_INFO = "getDeviceInfo", - TOAST = "toast", - ALERT = "alert", - CONFIRM = "confirm", - SHARE = "share", - PAYMENT = "payment", - NAVIGATE = "navigate", - PAGE_LOADED = "pageLoaded", - PAGE_READY = "pageReady", - CUSTOM = "custom", -} - -// 响应消息类型 -export enum ResponseType { - USER_INFO = "userInfo", - DEVICE_INFO = "deviceInfo", - SHARE_RESULT = "shareResult", - PAYMENT_RESULT = "paymentResult", - CONFIRM_RESULT = "confirmResult", - NAVIGATE_RESULT = "navigateResult", -} - -class UniAppBridge { - private messageQueue: BridgeMessage[] = []; - private isReady = false; - private listeners: Map = new Map(); - private bridgeCheckInterval: number | null = null; - - constructor() { - (this as any)._startTime = Date.now(); - this.initBridge(); - } - - /** - * 初始化桥接 - */ - private initBridge(): void { - if (DEV_FEATURES.ENABLE_DEBUG_LOGS) { - console.log("🔧 开始初始化UniApp桥接"); - console.log("🔧 当前环境:", { - userAgent: navigator.userAgent, - location: window.location.href, - windowParent: !!window.parent, - windowTop: !!window.top, - }); - } - - // 检查桥接是否已存在 - if (window.uniAppBridge) { - if (DEV_FEATURES.ENABLE_DEBUG_LOGS) { - console.log("✅ 桥接已存在,直接使用"); - } - this.isReady = true; - this.setupMessageListener(); - return; - } - - if (DEV_FEATURES.ENABLE_DEBUG_LOGS) { - console.log("⏳ 桥接不存在,开始等待注入"); - } - - // 等待桥接注入 - this.waitForBridge(); - } - - /** - * 等待桥接注入 - */ - private waitForBridge(): void { - let retryCount = 0; - const maxRetries = 3; - const checkInterval = 200; // 增加检查间隔到200ms - const timeoutDuration = 30000; // 增加到30秒超时 - - this.bridgeCheckInterval = window.setInterval(() => { - if (window.uniAppBridge) { - this.isReady = true; - this.setupMessageListener(); - if (this.bridgeCheckInterval) { - clearInterval(this.bridgeCheckInterval); - this.bridgeCheckInterval = null; - } - if (DEV_FEATURES.ENABLE_DEBUG_LOGS) { - console.log("✅ UniApp桥接已就绪"); - } - return; - } - - // 增加重试逻辑 - retryCount++; - if (retryCount % 50 === 0) { - // 每10秒输出一次日志 - if (DEV_FEATURES.ENABLE_DEBUG_LOGS) { - console.log( - `⏳ 等待UniApp桥接注入... (${(retryCount * checkInterval) / 1000}s)`, - ); - } - } - }, checkInterval); - - // 设置超时 - setTimeout(() => { - if (this.bridgeCheckInterval) { - clearInterval(this.bridgeCheckInterval); - this.bridgeCheckInterval = null; - console.warn("❌ UniApp桥接初始化超时 (30秒)"); - - // 尝试手动触发桥接注入 - this.tryManualBridgeInjection(); - } - }, timeoutDuration); - } - - /** - * 尝试手动触发桥接注入 - */ - private tryManualBridgeInjection(): void { - if (DEV_FEATURES.ENABLE_DEBUG_LOGS) { - console.log("🔄 尝试手动触发桥接注入..."); - } - - // 发送页面准备就绪消息,可能触发UniApp重新注入桥接 - try { - window.parent.postMessage( - { - type: "pageReady", - data: { - url: window.location.href, - timestamp: Date.now(), - retry: true, - }, - }, - "*", - ); - } catch (error) { - console.error("手动触发桥接注入失败:", error); - } - - // 延迟后重新检查 - setTimeout(() => { - if (window.uniAppBridge) { - this.isReady = true; - this.setupMessageListener(); - if (DEV_FEATURES.ENABLE_DEBUG_LOGS) { - console.log("✅ 手动触发后桥接已就绪"); - } - } else { - console.error("❌ 手动触发后桥接仍未就绪"); - } - }, 2000); - } - - /** - * 设置消息监听器 - */ - private setupMessageListener(): void { - if (DEV_FEATURES.ENABLE_DEBUG_LOGS) { - console.log("🔧 设置UniApp消息监听器"); - } - - window.addEventListener("uniAppMessage", (event: any) => { - if (DEV_FEATURES.ENABLE_DEBUG_LOGS) { - console.log("📨 收到uniAppMessage事件:", event); - } - - try { - const { type, data } = event.detail; - if (DEV_FEATURES.ENABLE_DEBUG_LOGS) { - console.log("📨 解析消息:", type, data); - } - this.handleMessage(type, data); - } catch (error) { - console.error("❌ 处理uniAppMessage事件失败:", error); - } - }); - - // 同时监听原生message事件作为备用 - window.addEventListener("message", (event: any) => { - if (DEV_FEATURES.ENABLE_DEBUG_LOGS) { - console.log("📨 收到原生message事件:", event.data); - } - - try { - if (event.data && event.data.type) { - const { type, data } = event.data; - - // 处理注入代码消息 - if (type === "injectCode") { - this.handleInjectCode(data); - return; - } - - if (DEV_FEATURES.ENABLE_DEBUG_LOGS) { - console.log("📨 解析原生消息:", type, data); - } - this.handleMessage(type, data); - } - } catch (error) { - console.error("❌ 处理原生message事件失败:", error); - } - }); - } - - /** - * 处理注入代码消息 - */ - private handleInjectCode(data: any): void { - if (DEV_FEATURES.ENABLE_DEBUG_LOGS) { - console.log("🔧 处理注入代码消息:", data); - } - - try { - if (data.code) { - // 执行注入的代码 - const script = document.createElement("script"); - script.textContent = data.code; - document.head.appendChild(script); - document.head.removeChild(script); - - if (DEV_FEATURES.ENABLE_DEBUG_LOGS) { - console.log("✅ 代码注入成功"); - } - } - } catch (error) { - console.error("❌ 代码注入失败:", error); - } - } - - /** - * 处理接收到的消息 - */ - private handleMessage(type: string, data: any): void { - if (DEV_FEATURES.ENABLE_DEBUG_LOGS) { - console.log("📨 处理UniApp消息:", type, data); - } - - // 触发对应的事件监听器 - if (this.listeners.has(type)) { - const callbacks = this.listeners.get(type); - if (DEV_FEATURES.ENABLE_DEBUG_LOGS) { - console.log(`📨 触发${type}事件监听器, 回调数量:`, callbacks?.length); - } - - callbacks?.forEach((callback, index) => { - try { - if (DEV_FEATURES.ENABLE_DEBUG_LOGS) { - console.log(`📨 执行回调${index + 1}:`, callback); - } - callback(data); - } catch (error) { - console.error(`❌ 处理消息回调失败 (${type}):`, error); - } - }); - } else { - if (DEV_FEATURES.ENABLE_DEBUG_LOGS) { - console.log(`📨 未找到${type}事件的监听器`); - } - } - } - - /** - * 发送消息到UniApp - */ - private postMessage(type: string, data: any = {}): void { - if (!this.isReady) { - if (DEV_FEATURES.ENABLE_DEBUG_LOGS) { - console.warn("⚠️ 桥接未就绪,消息已加入队列:", type, data); - } - this.messageQueue.push({ - type, - data, - timestamp: Date.now(), - }); - return; - } - - try { - if (DEV_FEATURES.ENABLE_DEBUG_LOGS) { - console.log("📤 发送消息到UniApp:", type, data); - } - - if (!window.uniAppBridge) { - console.error("❌ window.uniAppBridge 不存在"); - return; - } - - if (!window.uniAppBridge.postMessage) { - console.error("❌ window.uniAppBridge.postMessage 方法不存在"); - return; - } - - window.uniAppBridge.postMessage(type, data); - - if (DEV_FEATURES.ENABLE_DEBUG_LOGS) { - console.log("✅ 消息发送成功"); - } - } catch (error) { - console.error("❌ 发送消息失败:", error); - } - } - - /** - * 处理消息队列 - */ - private processMessageQueue(): void { - while (this.messageQueue.length > 0) { - const message = this.messageQueue.shift(); - if (message) { - this.postMessage(message.type, message.data); - } - } - } - - /** - * 添加消息监听器 - */ - public on(type: string, callback: MessageCallback): void { - if (!this.listeners.has(type)) { - this.listeners.set(type, []); - } - this.listeners.get(type)?.push(callback); - } - - /** - * 移除消息监听器 - */ - public off(type: string, callback?: MessageCallback): void { - if (!callback) { - this.listeners.delete(type); - } else { - const callbacks = this.listeners.get(type); - if (callbacks) { - const index = callbacks.indexOf(callback); - if (index > -1) { - callbacks.splice(index, 1); - } - } - } - } - - /** - * 获取用户信息 - */ - public getUserInfo(): void { - this.postMessage(MessageType.GET_USER_INFO); - } - - /** - * 获取设备信息 - */ - public getDeviceInfo(): void { - this.postMessage(MessageType.GET_DEVICE_INFO); - } - - /** - * 显示Toast提示 - */ - public showToast(message: string, duration: number = 2000): void { - this.postMessage(MessageType.TOAST, { message, duration }); - } - - /** - * 显示Alert对话框 - */ - public showAlert(title: string, content: string): void { - this.postMessage(MessageType.ALERT, { title, content }); - } - - /** - * 显示Confirm确认框 - */ - public showConfirm(title: string, content: string): Promise { - return new Promise(resolve => { - const handleConfirmResult = (data: any) => { - this.off(ResponseType.CONFIRM_RESULT, handleConfirmResult); - resolve(data.confirmed || false); - }; - - this.on(ResponseType.CONFIRM_RESULT, handleConfirmResult); - this.postMessage(MessageType.CONFIRM, { title, content }); - }); - } - - /** - * 分享内容 - */ - public share(data: ShareData): Promise { - return new Promise(resolve => { - const handleShareResult = (result: any) => { - this.off(ResponseType.SHARE_RESULT, handleShareResult); - resolve(result.success || false); - }; - - this.on(ResponseType.SHARE_RESULT, handleShareResult); - this.postMessage(MessageType.SHARE, data); - }); - } - - /** - * 支付 - */ - public payment(data: PaymentData): Promise { - return new Promise(resolve => { - const handlePaymentResult = (result: any) => { - this.off(ResponseType.PAYMENT_RESULT, handlePaymentResult); - resolve(result.success || false); - }; - - this.on(ResponseType.PAYMENT_RESULT, handlePaymentResult); - this.postMessage(MessageType.PAYMENT, data); - }); - } - - /** - * 页面导航 - */ - public navigate(url: string): void { - this.postMessage(MessageType.NAVIGATE, { url }); - } - - /** - * 发送自定义消息 - */ - public sendCustomMessage(type: string, data: any): void { - this.postMessage(type, data); - } - - /** - * 通知页面已准备就绪 - */ - public notifyPageReady(data?: any): void { - this.postMessage(MessageType.PAGE_READY, { - title: document.title, - url: window.location.href, - ...data, - }); - } - - /** - * 检查桥接是否就绪 - */ - public isBridgeReady(): boolean { - return this.isReady; - } - - /** - * 等待桥接就绪 - */ - public waitForReady(): Promise { - return new Promise((resolve, reject) => { - if (this.isReady) { - resolve(); - return; - } - - const maxWaitTime = 30000; // 30秒最大等待时间 - const checkInterval = 200; // 200ms检查间隔 - let elapsedTime = 0; - - const checkReady = () => { - if (this.isReady) { - resolve(); - return; - } - - elapsedTime += checkInterval; - if (elapsedTime >= maxWaitTime) { - reject(new Error(`桥接初始化超时 (${maxWaitTime / 1000}秒)`)); - return; - } - - setTimeout(checkReady, checkInterval); - }; - checkReady(); - }); - } - - /** - * 销毁桥接 - */ - public destroy(): void { - if (this.bridgeCheckInterval) { - clearInterval(this.bridgeCheckInterval); - this.bridgeCheckInterval = null; - } - this.listeners.clear(); - this.messageQueue = []; - this.isReady = false; - } - - /** - * 获取桥接状态信息 - */ - public getBridgeStatus(): { - isReady: boolean; - bridgeExists: boolean; - messageQueueLength: number; - listenersCount: number; - uptime: number; - } { - return { - isReady: this.isReady, - bridgeExists: !!window.uniAppBridge, - messageQueueLength: this.messageQueue.length, - listenersCount: this.listeners.size, - uptime: Date.now() - (this as any)._startTime || 0, - }; - } - - /** - * 手动检查桥接状态 - */ - public checkBridgeStatus(): void { - const status = this.getBridgeStatus(); - if (DEV_FEATURES.ENABLE_DEBUG_LOGS) { - console.log("🔍 桥接状态检查:", status); - } - - if (!status.bridgeExists) { - console.warn("⚠️ window.uniAppBridge 不存在"); - } - - if (!status.isReady && status.bridgeExists) { - console.warn("⚠️ 桥接存在但未就绪"); - } - } -} - -// 创建全局桥接实例 -const bridge = new UniAppBridge(); - -// 导出桥接实例和类型 -export default bridge; -export { UniAppBridge }; - -// 导出便捷方法 -export const { - getUserInfo, - getDeviceInfo, - showToast, - showAlert, - showConfirm, - share, - payment, - navigate, - sendCustomMessage, - notifyPageReady, - isBridgeReady, - waitForReady, - on, - off, - destroy, - getBridgeStatus, - checkBridgeStatus, -} = bridge; - -// 声明全局类型 -declare global { - interface Window { - uniAppBridge?: { - postMessage: (type: string, data: any) => void; - getUserInfo: () => void; - getDeviceInfo: () => void; - showToast: (message: string, duration?: number) => void; - showAlert: (title: string, content: string) => void; - showConfirm: (title: string, content: string) => void; - share: (data: ShareData) => void; - payment: (data: PaymentData) => void; - }; - } -} diff --git a/nkebao/vite-pwa.config.ts b/nkebao/vite-pwa.config.ts new file mode 100644 index 00000000..f1c5cb7d --- /dev/null +++ b/nkebao/vite-pwa.config.ts @@ -0,0 +1,56 @@ +import { defineConfig } from "vite"; +import react from "@vitejs/plugin-react"; +import { VitePWA } from "vite-plugin-pwa"; + +export default defineConfig({ + plugins: [ + react(), + VitePWA({ + registerType: "autoUpdate", + workbox: { + globPatterns: ["**/*.{js,css,html,ico,png,svg}"], + runtimeCaching: [ + { + urlPattern: /^https:\/\/api\./, + handler: "NetworkFirst", + options: { + cacheName: "api-cache", + expiration: { + maxEntries: 100, + maxAgeSeconds: 60 * 60 * 24 * 7, // 7 days + }, + }, + }, + ], + }, + manifest: { + name: "Cunkebao", + short_name: "Cunkebao", + description: "Cunkebao Mobile App", + theme_color: "#ffffff", + background_color: "#ffffff", + display: "standalone", + orientation: "portrait", + scope: "/", + start_url: "/", + icons: [ + { + src: "favicon.ico", + sizes: "64x64 32x32 24x24 16x16", + type: "image/x-icon", + }, + { + src: "pwa-192x192.png", + sizes: "192x192", + type: "image/png", + }, + { + src: "pwa-512x512.png", + sizes: "512x512", + type: "image/png", + }, + ], + }, + }), + ], +});
- {JSON.stringify(lastResult, null, 2)} -