REFactor => 移除安全区域相关功能和测试页面,简化导航组件,优化主入口文件结构。
This commit is contained in:
@@ -1,8 +1,7 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import React from "react";
|
||||
import { NavBar } from "antd-mobile";
|
||||
import { ArrowLeftOutlined } from "@ant-design/icons";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { getTopSafeAreaHeight, initSafeArea } from "@/utils/safeArea";
|
||||
|
||||
interface NavCommonProps {
|
||||
title: string;
|
||||
@@ -18,49 +17,8 @@ const NavCommon: React.FC<NavCommonProps> = ({
|
||||
left,
|
||||
}) => {
|
||||
const navigate = useNavigate();
|
||||
const [topBarHeight, setTopBarHeight] = useState<number>(0);
|
||||
|
||||
useEffect(() => {
|
||||
// 初始化安全区域
|
||||
initSafeArea();
|
||||
|
||||
// 计算顶部安全区域高度
|
||||
const calculateTopBarHeight = () => {
|
||||
const height = getTopSafeAreaHeight();
|
||||
setTopBarHeight(height);
|
||||
};
|
||||
|
||||
// 立即计算一次
|
||||
calculateTopBarHeight();
|
||||
|
||||
// 监听屏幕方向变化
|
||||
const handleOrientationChange = () => {
|
||||
setTimeout(calculateTopBarHeight, 100);
|
||||
};
|
||||
|
||||
// 监听窗口大小变化
|
||||
const handleResize = () => {
|
||||
calculateTopBarHeight();
|
||||
};
|
||||
|
||||
window.addEventListener("orientationchange", handleOrientationChange);
|
||||
window.addEventListener("resize", handleResize);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener("orientationchange", handleOrientationChange);
|
||||
window.removeEventListener("resize", handleResize);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className="mobile-top-bar"
|
||||
style={{
|
||||
height: `${topBarHeight}px`,
|
||||
backgroundColor: "#fff",
|
||||
}}
|
||||
></div>
|
||||
<NavBar
|
||||
back={null}
|
||||
style={{ background: "#fff" }}
|
||||
|
||||
@@ -2,10 +2,6 @@ import React from "react";
|
||||
import { createRoot } from "react-dom/client";
|
||||
import App from "./App";
|
||||
import "./styles/global.scss";
|
||||
import { initSafeArea } from "./utils/safeArea";
|
||||
|
||||
// 初始化安全区域
|
||||
initSafeArea();
|
||||
|
||||
// import VConsole from "vconsole";
|
||||
// new VConsole();
|
||||
|
||||
@@ -1,373 +0,0 @@
|
||||
# 测试页面使用说明
|
||||
|
||||
## 概述
|
||||
|
||||
这些测试页面只在开发环境中显示,用于开发和调试各种功能。所有测试页面都使用了统一的Layout布局组件。
|
||||
|
||||
## 访问方式
|
||||
|
||||
1. **开发环境**: 访问 `/test` 或 `/test/postMessage`
|
||||
2. **生产环境**: 这些页面不会显示,路由也不会注册
|
||||
|
||||
## 测试页面列表
|
||||
|
||||
### 1. 测试页面入口 (`/test`)
|
||||
|
||||
- 使用Layout布局
|
||||
- 提供测试页面的导航入口
|
||||
- 显示开发环境标识
|
||||
|
||||
### 2. UniApp桥接测试 (`/test/postMessage`)
|
||||
|
||||
测试UniApp WebView桥接功能,包括:
|
||||
|
||||
- ✅ 获取用户信息
|
||||
- ✅ 获取设备信息
|
||||
- ✅ 显示Toast提示
|
||||
- ✅ 显示Alert对话框
|
||||
- ✅ 显示Confirm确认框
|
||||
- ✅ 分享内容
|
||||
- ✅ 支付功能
|
||||
- ✅ 页面导航
|
||||
- ✅ 自定义消息发送
|
||||
- ✅ 桥接状态检查
|
||||
- ✅ 通信调试工具
|
||||
|
||||
**布局特性:**
|
||||
|
||||
- 使用Layout布局组件
|
||||
- 响应式设计(移动端适配)
|
||||
- 实时状态显示
|
||||
- 操作日志记录
|
||||
|
||||
### 3. 安全区域测试 (`/test/safeArea`)
|
||||
|
||||
测试安全区域高度计算功能:
|
||||
|
||||
- ✅ 设备类型检测(iOS/Android)
|
||||
- ✅ 刘海屏设备检测
|
||||
- ✅ 安全区域高度计算
|
||||
- ✅ 屏幕方向变化监听
|
||||
- ✅ CSS变量动态设置
|
||||
- ✅ 实时信息更新
|
||||
|
||||
**布局特性:**
|
||||
|
||||
- 使用Layout布局组件
|
||||
- 实时显示设备信息
|
||||
- 动态更新安全区域数据
|
||||
- 支持屏幕方向变化
|
||||
|
||||
### 4. 选择组件测试 (`/test/select`)
|
||||
|
||||
测试各种选择组件:
|
||||
|
||||
- 设备选择
|
||||
- 好友选择
|
||||
- 群组选择
|
||||
- 内容库选择
|
||||
- 账号选择
|
||||
|
||||
**布局特性:**
|
||||
|
||||
- 使用Layout布局组件
|
||||
- Tab标签页切换
|
||||
- 实时选择状态显示
|
||||
|
||||
## 通信流程
|
||||
|
||||
### UniApp ↔ React 通信流程
|
||||
|
||||
```
|
||||
1. UniApp注入桥接代码
|
||||
↓
|
||||
2. React检测桥接就绪
|
||||
↓
|
||||
3. React发送请求消息
|
||||
↓
|
||||
4. UniApp接收并处理
|
||||
↓
|
||||
5. UniApp发送响应消息
|
||||
↓
|
||||
6. React接收并处理响应
|
||||
```
|
||||
|
||||
### 消息类型映射
|
||||
|
||||
| React发送 | UniApp接收 | UniApp响应 | React接收 |
|
||||
| --------------- | --------------- | --------------- | --------------- |
|
||||
| `getUserInfo` | `getUserInfo` | `userInfo` | `userInfo` |
|
||||
| `getDeviceInfo` | `getDeviceInfo` | `deviceInfo` | `deviceInfo` |
|
||||
| `toast` | `toast` | - | - |
|
||||
| `alert` | `alert` | - | - |
|
||||
| `confirm` | `confirm` | `confirmResult` | `confirmResult` |
|
||||
| `share` | `share` | `shareResult` | `shareResult` |
|
||||
| `payment` | `payment` | `paymentResult` | `paymentResult` |
|
||||
|
||||
### 调试工具
|
||||
|
||||
#### 1. 桥接状态检查
|
||||
|
||||
- 检查桥接是否就绪
|
||||
- 检查桥接是否存在
|
||||
- 显示消息队列状态
|
||||
- 显示监听器数量
|
||||
|
||||
#### 2. 直接消息测试
|
||||
|
||||
- 绕过桥接工具直接发送消息
|
||||
- 测试桥接注入是否成功
|
||||
- 验证消息传递机制
|
||||
|
||||
#### 3. 监听器状态检查
|
||||
|
||||
- 检查事件监听器注册状态
|
||||
- 验证消息处理流程
|
||||
- 排查通信问题
|
||||
|
||||
## 布局组件
|
||||
|
||||
所有测试页面都使用了以下布局组件:
|
||||
|
||||
### Layout组件
|
||||
|
||||
- 提供统一的页面布局结构
|
||||
- 包含头部导航和内容区域
|
||||
- 支持移动端适配
|
||||
|
||||
### NavCommon组件
|
||||
|
||||
- 统一的导航头部
|
||||
- 支持返回按钮
|
||||
- 显示页面标题
|
||||
|
||||
## 环境配置
|
||||
|
||||
### 开发环境特性开关
|
||||
|
||||
在 `src/utils/env.ts` 中配置:
|
||||
|
||||
```typescript
|
||||
export const DEV_FEATURES = {
|
||||
SHOW_TEST_PAGES: true, // 显示测试页面
|
||||
ENABLE_DEBUG_LOGS: true, // 启用调试日志
|
||||
SHOW_DEV_TOOLS: true, // 显示开发工具
|
||||
ENABLE_MOCK_DATA: true, // 启用Mock数据
|
||||
};
|
||||
```
|
||||
|
||||
### 环境变量 (Vite)
|
||||
|
||||
在Vite项目中,使用 `import.meta.env` 访问环境变量:
|
||||
|
||||
```typescript
|
||||
// 环境检测
|
||||
export const isDevelopment = import.meta.env.DEV;
|
||||
export const isProduction = import.meta.env.PROD;
|
||||
|
||||
// 环境变量
|
||||
const apiUrl = import.meta.env.VITE_API_BASE_URL;
|
||||
const appTitle = import.meta.env.VITE_APP_TITLE;
|
||||
```
|
||||
|
||||
### 环境变量列表
|
||||
|
||||
- `import.meta.env.MODE`: 环境模式 (development/production)
|
||||
- `import.meta.env.DEV`: 是否为开发环境 (boolean)
|
||||
- `import.meta.env.PROD`: 是否为生产环境 (boolean)
|
||||
- `VITE_APP_TITLE`: 应用标题
|
||||
- `VITE_API_BASE_URL`: API基础URL
|
||||
- `VITE_APP_VERSION`: 应用版本
|
||||
|
||||
### 环境变量配置
|
||||
|
||||
在项目根目录创建 `.env` 文件:
|
||||
|
||||
```env
|
||||
# 开发环境
|
||||
VITE_APP_TITLE=存客宝
|
||||
VITE_API_BASE_URL=http://localhost:3000/api
|
||||
VITE_APP_VERSION=1.0.0
|
||||
```
|
||||
|
||||
## 通信问题排查
|
||||
|
||||
### 常见问题及解决方案
|
||||
|
||||
#### 1. 桥接初始化超时
|
||||
|
||||
**症状**: 页面显示"桥接初始化中..."
|
||||
**原因**: UniApp端桥接代码注入失败或延迟
|
||||
**解决**:
|
||||
|
||||
- 检查网络连接
|
||||
- 查看控制台错误信息
|
||||
- 使用"检查桥接状态"工具
|
||||
- 使用"强制重新初始化"按钮
|
||||
|
||||
#### 2. 消息发送失败
|
||||
|
||||
**症状**: 点击按钮无响应或显示错误
|
||||
**原因**: 桥接未就绪或消息类型不匹配
|
||||
**解决**:
|
||||
|
||||
- 确认桥接已就绪
|
||||
- 检查消息类型是否正确
|
||||
- 使用"测试直接发送消息"工具
|
||||
|
||||
#### 3. 响应消息未收到
|
||||
|
||||
**症状**: 发送请求后未收到响应
|
||||
**原因**: 监听器未正确注册或消息传递失败
|
||||
**解决**:
|
||||
|
||||
- 检查监听器注册状态
|
||||
- 查看UniApp端处理逻辑
|
||||
- 使用"检查监听器状态"工具
|
||||
|
||||
#### 4. 桥接注入失败
|
||||
|
||||
**症状**: window.uniAppBridge 不存在
|
||||
**原因**: UniApp端JavaScript注入失败
|
||||
**解决**:
|
||||
|
||||
- 检查UniApp端控制台日志
|
||||
- 确认web-view组件正常工作
|
||||
- 检查网络连接和URL配置
|
||||
|
||||
#### 5. webview或evalJS方法不存在
|
||||
|
||||
**症状**: "UniApp: webview或evalJS方法不存在"
|
||||
**原因**:
|
||||
|
||||
- 不在App环境下运行
|
||||
- web-view组件未正确初始化
|
||||
- 平台不支持evalJS方法
|
||||
**解决**:
|
||||
- 确认在App环境下运行
|
||||
- 检查web-view组件配置
|
||||
- 使用备用注入方案
|
||||
- 查看平台检测信息
|
||||
|
||||
### 调试步骤
|
||||
|
||||
1. **环境检测**
|
||||
- 点击"环境检测"按钮
|
||||
- 确认运行环境和平台信息
|
||||
- 检查是否在UniApp WebView中
|
||||
|
||||
2. **检查桥接状态**
|
||||
- 点击"检查桥接状态"按钮
|
||||
- 确认 `bridgeExists: true` 和 `isReady: true`
|
||||
|
||||
3. **测试直接通信**
|
||||
- 点击"测试直接发送消息"按钮
|
||||
- 查看控制台输出
|
||||
|
||||
4. **检查监听器**
|
||||
- 点击"检查监听器状态"按钮
|
||||
- 确认监听器数量正确
|
||||
|
||||
5. **强制重新初始化**
|
||||
- 点击"强制重新初始化"按钮
|
||||
- 观察重新初始化过程
|
||||
|
||||
6. **查看详细日志**
|
||||
- 打开浏览器开发者工具
|
||||
- 查看Console标签页
|
||||
- 搜索相关日志信息
|
||||
|
||||
7. **检查UniApp端日志**
|
||||
- 在UniApp开发工具中查看控制台
|
||||
- 确认桥接代码注入成功
|
||||
- 检查消息处理逻辑
|
||||
|
||||
### 环境检查清单
|
||||
|
||||
#### React端检查
|
||||
|
||||
- [ ] 页面在UniApp WebView中运行
|
||||
- [ ] 控制台无JavaScript错误
|
||||
- [ ] 桥接状态检查通过
|
||||
- [ ] 监听器正确注册
|
||||
|
||||
#### UniApp端检查
|
||||
|
||||
- [ ] web-view组件正常加载
|
||||
- [ ] 桥接代码注入成功
|
||||
- [ ] 消息处理逻辑正确
|
||||
- [ ] 响应消息正确发送
|
||||
|
||||
#### 网络检查
|
||||
|
||||
- [ ] URL配置正确
|
||||
- [ ] 网络连接正常
|
||||
- [ ] 无跨域问题
|
||||
- [ ] 防火墙未阻止
|
||||
|
||||
#### 平台检查
|
||||
|
||||
- [ ] 确认在App环境下运行
|
||||
- [ ] web-view组件支持evalJS
|
||||
- [ ] 平台支持postMessage通信
|
||||
- [ ] 无平台限制问题
|
||||
|
||||
### 常见错误及解决方案
|
||||
|
||||
#### 错误1: "window.uniAppBridge 不存在"
|
||||
|
||||
**解决方案**:
|
||||
|
||||
1. 检查UniApp端桥接注入是否成功
|
||||
2. 确认web-view组件正常工作
|
||||
3. 查看UniApp端控制台日志
|
||||
|
||||
#### 错误2: "桥接初始化超时"
|
||||
|
||||
**解决方案**:
|
||||
|
||||
1. 增加超时时间
|
||||
2. 检查网络连接
|
||||
3. 使用强制重新初始化
|
||||
|
||||
#### 错误3: "消息发送失败"
|
||||
|
||||
**解决方案**:
|
||||
|
||||
1. 确认桥接已就绪
|
||||
2. 检查消息类型匹配
|
||||
3. 验证postMessage方法存在
|
||||
|
||||
#### 错误4: "响应消息未收到"
|
||||
|
||||
**解决方案**:
|
||||
|
||||
1. 检查监听器注册
|
||||
2. 确认UniApp端响应逻辑
|
||||
3. 验证消息传递机制
|
||||
|
||||
#### 错误5: "webview或evalJS方法不存在"
|
||||
|
||||
**解决方案**:
|
||||
|
||||
1. 确认在App环境下运行
|
||||
2. 检查web-view组件配置
|
||||
3. 使用备用注入方案
|
||||
4. 查看平台检测信息
|
||||
|
||||
## 使用建议
|
||||
|
||||
1. **开发阶段**: 充分利用测试页面进行功能验证
|
||||
2. **调试桥接**: 使用UniApp桥接测试页面验证通信功能
|
||||
3. **组件测试**: 使用选择组件测试页面验证UI组件
|
||||
4. **生产部署**: 确保测试页面不会出现在生产环境中
|
||||
|
||||
## 注意事项
|
||||
|
||||
- 测试页面只在开发环境显示
|
||||
- 生产环境会自动隐藏所有测试相关功能
|
||||
- 调试日志只在开发环境输出
|
||||
- 确保在生产构建前移除所有测试代码
|
||||
- 所有测试页面都使用统一的Layout布局,保持UI一致性
|
||||
- 使用 `import.meta.env` 而不是 `process.env` 访问环境变量
|
||||
- 通信问题优先使用内置调试工具排查
|
||||
@@ -1,10 +1,6 @@
|
||||
import React from "react";
|
||||
import { Card, Button, Space, Typography, Tag } from "antd";
|
||||
import {
|
||||
MessageOutlined,
|
||||
SelectOutlined,
|
||||
SafetyOutlined,
|
||||
} from "@ant-design/icons";
|
||||
import { MessageOutlined, SelectOutlined } from "@ant-design/icons";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { isDevelopment } from "@/utils/env";
|
||||
import Layout from "@/components/Layout/Layout";
|
||||
@@ -48,15 +44,6 @@ const TestIndex: React.FC = () => {
|
||||
>
|
||||
选择组件测试
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
icon={<SafetyOutlined />}
|
||||
size="large"
|
||||
block
|
||||
onClick={() => navigate("/test/safeArea")}
|
||||
>
|
||||
安全区域测试
|
||||
</Button>
|
||||
</Space>
|
||||
</Card>
|
||||
|
||||
|
||||
@@ -1,176 +0,0 @@
|
||||
// 安全区域高度计算工具
|
||||
|
||||
/**
|
||||
* 获取设备的安全区域信息
|
||||
*/
|
||||
export interface SafeAreaInfo {
|
||||
top: number;
|
||||
bottom: number;
|
||||
left: number;
|
||||
right: number;
|
||||
statusBarHeight: number;
|
||||
navBarHeight: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算安全区域高度
|
||||
*/
|
||||
export function getSafeAreaHeight(): SafeAreaInfo {
|
||||
// 获取CSS环境变量
|
||||
const getCSSValue = (property: string): number => {
|
||||
const value = getComputedStyle(document.documentElement).getPropertyValue(
|
||||
property,
|
||||
);
|
||||
return parseInt(value) || 0;
|
||||
};
|
||||
|
||||
// 获取状态栏高度
|
||||
const statusBarHeight =
|
||||
getCSSValue("--status-bar-height") ||
|
||||
getCSSValue("--sat") ||
|
||||
getCSSValue("--safe-area-inset-top") ||
|
||||
0;
|
||||
|
||||
// 获取底部安全区域高度
|
||||
const bottomSafeArea = getCSSValue("--safe-area-inset-bottom") || 0;
|
||||
|
||||
// 获取左右安全区域
|
||||
const leftSafeArea = getCSSValue("--safe-area-inset-left") || 0;
|
||||
const rightSafeArea = getCSSValue("--safe-area-inset-right") || 0;
|
||||
|
||||
// 导航栏高度(通常是44px,但可能因设备而异)
|
||||
const navBarHeight = 44;
|
||||
|
||||
return {
|
||||
top: statusBarHeight,
|
||||
bottom: bottomSafeArea,
|
||||
left: leftSafeArea,
|
||||
right: rightSafeArea,
|
||||
statusBarHeight,
|
||||
navBarHeight,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取顶部安全区域高度(用于mobile-top-bar)
|
||||
*/
|
||||
export function getTopSafeAreaHeight(): number {
|
||||
const safeArea = getSafeAreaHeight();
|
||||
|
||||
// 如果状态栏高度为0,尝试其他方法
|
||||
if (safeArea.statusBarHeight === 0) {
|
||||
// 尝试从CSS变量获取
|
||||
const statusBar = getComputedStyle(
|
||||
document.documentElement,
|
||||
).getPropertyValue("--status-bar-height");
|
||||
if (statusBar) {
|
||||
return parseInt(statusBar) || 0;
|
||||
}
|
||||
|
||||
// 尝试从CSS变量获取安全区域
|
||||
const safeAreaTop = getComputedStyle(
|
||||
document.documentElement,
|
||||
).getPropertyValue("--safe-area-inset-top");
|
||||
if (safeAreaTop) {
|
||||
return parseInt(safeAreaTop) || 0;
|
||||
}
|
||||
|
||||
// 默认值(iPhone X及以后的设备通常为44px)
|
||||
return 44;
|
||||
}
|
||||
|
||||
return safeArea.statusBarHeight;
|
||||
}
|
||||
|
||||
/**
|
||||
* 动态设置CSS变量
|
||||
*/
|
||||
export function setSafeAreaCSSVariables(): void {
|
||||
const safeArea = getSafeAreaHeight();
|
||||
|
||||
// 设置CSS变量
|
||||
document.documentElement.style.setProperty(
|
||||
"--safe-area-top",
|
||||
`${safeArea.top}px`,
|
||||
);
|
||||
document.documentElement.style.setProperty(
|
||||
"--safe-area-bottom",
|
||||
`${safeArea.bottom}px`,
|
||||
);
|
||||
document.documentElement.style.setProperty(
|
||||
"--safe-area-left",
|
||||
`${safeArea.left}px`,
|
||||
);
|
||||
document.documentElement.style.setProperty(
|
||||
"--safe-area-right",
|
||||
`${safeArea.right}px`,
|
||||
);
|
||||
document.documentElement.style.setProperty(
|
||||
"--status-bar-height",
|
||||
`${safeArea.statusBarHeight}px`,
|
||||
);
|
||||
document.documentElement.style.setProperty(
|
||||
"--nav-bar-height",
|
||||
`${safeArea.navBarHeight}px`,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测设备类型
|
||||
*/
|
||||
export function getDeviceType(): "ios" | "android" | "unknown" {
|
||||
const userAgent = navigator.userAgent.toLowerCase();
|
||||
|
||||
if (/iphone|ipad|ipod/.test(userAgent)) {
|
||||
return "ios";
|
||||
} else if (/android/.test(userAgent)) {
|
||||
return "android";
|
||||
}
|
||||
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测是否为刘海屏设备
|
||||
*/
|
||||
export function isNotchDevice(): boolean {
|
||||
const deviceType = getDeviceType();
|
||||
|
||||
if (deviceType === "ios") {
|
||||
// iOS设备检测
|
||||
const screenHeight = window.screen.height;
|
||||
const screenWidth = window.screen.width;
|
||||
|
||||
// iPhone X及以后的设备
|
||||
return screenHeight >= 812 || screenWidth >= 812;
|
||||
} else if (deviceType === "android") {
|
||||
// Android设备检测
|
||||
const screenHeight = window.screen.height;
|
||||
const screenWidth = window.screen.width;
|
||||
|
||||
// 高分辨率设备可能是刘海屏
|
||||
return screenHeight >= 800 || screenWidth >= 800;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化安全区域
|
||||
*/
|
||||
export function initSafeArea(): void {
|
||||
// 设置CSS变量
|
||||
setSafeAreaCSSVariables();
|
||||
|
||||
// 监听屏幕方向变化
|
||||
window.addEventListener("orientationchange", () => {
|
||||
setTimeout(() => {
|
||||
setSafeAreaCSSVariables();
|
||||
}, 100);
|
||||
});
|
||||
|
||||
// 监听窗口大小变化
|
||||
window.addEventListener("resize", () => {
|
||||
setSafeAreaCSSVariables();
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user