feat(mobile): 实现全局路由守卫功能,自动处理用户未登录跳转
✨ 新增功能: - 全局路由守卫模块 (route-guard.js) - 多层次权限验证 (登录状态+Token存在+Token过期检查) - 自动拦截机制 (页面级+跳转级+HTTP级+应用级) - 401响应自动处理和跳转 - 路由守卫测试页面 🔧 技术实现: - checkPageAccess() 全局页面权限检查 - guardedNavigate/guardedSwitchTab 带权限检查的跳转 - handleUnauthorized() HTTP拦截器401处理 - 公开页面配置管理 - 优雅的错误提示和跳转逻辑 📱 页面集成: - App.vue 初始化路由守卫 - profile/workbench 页面添加权限检查 - index 页面优化跳转逻辑 - 新增测试页面验证功能 📚 文档完善: - 移动端路由守卫使用指南 - 详细的变更记录和技术说明 - 最佳实践和注意事项 🎯 核心效果: 用户登录失效或未登录时,访问任何需要权限的页面都自动跳转到登录页,提升安全性和用户体验。
This commit is contained in:
1 parent
5589cba249
commit
afe3e234e9
33 files changed
+5023
-356
No files matched your search
@@ -0,0 +1,63 @@
|
||||
# 原型文件管理规范
|
||||
|
||||
## 📁 目录结构
|
||||
```
|
||||
docs/原型文件/
|
||||
├── 移动端/ # 移动端原型文件(Axure/Figma/Sketch等)
|
||||
├── web端/ # Web端原型文件
|
||||
└── README.md # 本说明文档
|
||||
```
|
||||
|
||||
## 📝 管理规范
|
||||
|
||||
### 1. 文件命名规则
|
||||
- **格式**: `[功能模块]_v[版本号]_[日期].[扩展名]`
|
||||
*示例: `登录模块_v1.2_20250105.fig`*
|
||||
- **版本号**: 主版本.次版本(重大变更改主版本,小调整改次版本)
|
||||
- **日期格式**: YYYYMMDD
|
||||
|
||||
### 2. 版本控制要求
|
||||
- 每次修改必须保留历史版本(禁止覆盖)
|
||||
- 版本变更需在`更新日志`中记录
|
||||
|
||||
### 3. 文件类型
|
||||
- **推荐格式**:
|
||||
- Figma (.fig)
|
||||
- Axure (.rp)
|
||||
- 可交互HTML
|
||||
- PDF截图(需包含版本信息)
|
||||
|
||||
### 4. 关联文档
|
||||
- 每个原型文件必须关联到:
|
||||
- `docs/需求文档/`中的对应需求文档
|
||||
- `docs/业务模块/`中的相关业务说明
|
||||
|
||||
### 5. 更新流程
|
||||
```mermaid
|
||||
graph TD
|
||||
A[需求变更] --> B[更新原型]
|
||||
B --> C[版本号+1]
|
||||
C --> D[更新日志记录]
|
||||
D --> E[同步关联文档]
|
||||
```
|
||||
|
||||
## 🗂️ 子目录说明
|
||||
|
||||
### 移动端原型
|
||||
- 存放位置: `移动端/`
|
||||
- 特殊要求:
|
||||
- 需包含各屏幕尺寸适配方案
|
||||
- 标注全局手势操作
|
||||
- 提供标准组件库文件
|
||||
|
||||
### Web端原型
|
||||
- 存放位置: `web端/`
|
||||
- 特殊要求:
|
||||
- 注明响应式断点
|
||||
- 包含权限状态展示
|
||||
- 提供配色规范文件
|
||||
|
||||
## 📅 更新日志
|
||||
| 版本 | 日期 | 修改人 | 变更说明 |
|
||||
|------|------------|----------|--------------------------|
|
||||
| v1.0 | 2025-01-05 | 系统生成 | 初始创建文档 |
|
||||
@@ -0,0 +1,62 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>校园管理系统 - 移动端登录</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"
|
||||
/>
|
||||
<style>
|
||||
.brand-blue {
|
||||
background-color: rgb(93, 135, 255);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-gray-50 min-h-screen flex items-center justify-center p-4">
|
||||
<div class="w-full max-w-md bg-white rounded-2xl shadow-xl overflow-hidden">
|
||||
<div class="brand-blue p-6 text-center">
|
||||
<i class="fas fa-graduation-cap text-white text-4xl mb-2"></i>
|
||||
<h1 class="text-2xl font-bold text-white">校园管理系统</h1>
|
||||
</div>
|
||||
<div class="p-8">
|
||||
<form>
|
||||
<div class="mb-6">
|
||||
<label class="block text-gray-700 text-sm font-medium mb-2">
|
||||
<i class="fas fa-user mr-2 text-blue-500"></i>
|
||||
账号
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
class="w-full px-4 py-3 rounded-lg border border-gray-300 focus:ring-2 focus:ring-blue-500"
|
||||
placeholder="请输入学工号"
|
||||
/>
|
||||
</div>
|
||||
<div class="mb-8">
|
||||
<label class="block text-gray-700 text-sm font-medium mb-2">
|
||||
<i class="fas fa-lock mr-2 text-blue-500"></i>
|
||||
密码
|
||||
</label>
|
||||
<input
|
||||
type="password"
|
||||
class="w-full px-4 py-3 rounded-lg border border-gray-300 focus:ring-2 focus:ring-blue-500"
|
||||
placeholder="请输入密码"
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
type="submit"
|
||||
class="w-full brand-blue text-white py-3 px-4 rounded-lg font-medium text-lg hover:bg-blue-600 transition shadow-md"
|
||||
>
|
||||
<i class="fas fa-sign-in-alt mr-2"></i>
|
||||
登录
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
<div class="bg-gray-50 px-6 py-4 text-center text-gray-500 text-sm">
|
||||
© 2025 校园管理系统 v3.0
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,77 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"
|
||||
/>
|
||||
<title>移动端登录原型</title>
|
||||
<!-- Tailwind CSS CDN -->
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<!-- FontAwesome CDN -->
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css"
|
||||
/>
|
||||
<style>
|
||||
body {
|
||||
background: linear-gradient(135deg, #5d87ff 0%, #a3bfff 100%);
|
||||
}
|
||||
.brand-color {
|
||||
color: #5d87ff;
|
||||
}
|
||||
.brand-bg {
|
||||
background-color: #5d87ff;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body
|
||||
class="min-h-screen flex flex-col justify-center items-center bg-gradient-to-br from-[#5D87FF] to-[#a3bfff]"
|
||||
>
|
||||
<div
|
||||
class="w-full max-w-xs mx-auto bg-white/80 rounded-2xl shadow-lg p-8 flex flex-col items-center"
|
||||
>
|
||||
<!-- Logo -->
|
||||
<div class="mb-6">
|
||||
<i class="fa-solid fa-graduation-cap text-5xl brand-color"></i>
|
||||
</div>
|
||||
<!-- 标题 -->
|
||||
<h1 class="text-2xl font-bold text-gray-800 mb-2">智慧校园</h1>
|
||||
<p class="text-sm text-gray-500 mb-8">杭州第一技师学院</p>
|
||||
<!-- 登录表单 -->
|
||||
<form class="w-full flex flex-col gap-4">
|
||||
<div class="relative">
|
||||
<span class="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400">
|
||||
<i class="fa-regular fa-user"></i>
|
||||
</span>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="账号"
|
||||
class="pl-10 pr-3 py-2 w-full rounded-lg border border-gray-200 focus:border-[#5D87FF] focus:ring-2 focus:ring-[#5D87FF]/30 outline-none bg-white text-gray-800 transition"
|
||||
/>
|
||||
</div>
|
||||
<div class="relative">
|
||||
<span class="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400">
|
||||
<i class="fa-solid fa-lock"></i>
|
||||
</span>
|
||||
<input
|
||||
type="password"
|
||||
placeholder="密码"
|
||||
class="pl-10 pr-3 py-2 w-full rounded-lg border border-gray-200 focus:border-[#5D87FF] focus:ring-2 focus:ring-[#5D87FF]/30 outline-none bg-white text-gray-800 transition"
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
type="submit"
|
||||
class="w-full py-2 mt-2 rounded-lg brand-bg text-white font-semibold text-lg shadow hover:opacity-90 transition"
|
||||
>
|
||||
登录
|
||||
</button>
|
||||
</form>
|
||||
<!-- 版权信息 -->
|
||||
<div class="mt-8 text-xs text-gray-400 text-center w-full">
|
||||
© 2025 杭州第一技师学院 All Rights Reserved.
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,391 @@
|
||||
# 代码调整记录 - 2025-01-03 - wuzilong
|
||||
|
||||
## 📋 变更时间倒序排列
|
||||
|
||||
### 🧩 登录页面输入组件升级为 uview-plus - 2025-01-03 18:45:00
|
||||
|
||||
**变更类型**: 组件升级
|
||||
**影响范围**: 移动端登录页面
|
||||
|
||||
#### 变更内容
|
||||
|
||||
1. **替换原生 input 为 uview-plus 组件**
|
||||
|
||||
- 账号输入框:使用`u-input`组件,添加用户图标前缀
|
||||
- 密码输入框:使用`u-input`组件,添加密码锁图标前缀
|
||||
- 登录按钮:使用`u-button`组件,保持图标+文字布局
|
||||
|
||||
2. **保持 SVG 图标系统**
|
||||
|
||||
- 继续使用`icon-user`、`icon-lock`、`icon-login`图标
|
||||
- 通过`template #prefix`插槽添加前缀图标
|
||||
- 保持原有的颜色和视觉效果
|
||||
|
||||
3. **样式配置优化**
|
||||
- 通过`inputStyle`和`buttonStyle`对象配置组件样式
|
||||
- 保持与原设计一致的视觉效果
|
||||
- 优化图标布局和间距
|
||||
|
||||
#### 技术细节
|
||||
|
||||
- **组件升级**: 原生`input` → `u-input`,原生`button` → `u-button`
|
||||
- **图标集成**: 使用`template #prefix`插槽集成 SVG 图标
|
||||
- **样式配置**: 通过`:custom-style`属性传递样式对象
|
||||
- **交互保持**: 保持原有的表单验证和提示逻辑
|
||||
|
||||
#### 文件变更
|
||||
|
||||
- `mobile/src/pages/login/index.vue`: 完整替换输入组件和按钮组件
|
||||
|
||||
#### 预期效果
|
||||
|
||||
- 更好的组件一致性,统一使用 uview-plus 组件库
|
||||
- 保持原有视觉设计和用户体验
|
||||
- 提升组件的可维护性和扩展性
|
||||
|
||||
---
|
||||
|
||||
### 🎨 移动端图标使用规范规范化 - 2025-01-03 18:30:00
|
||||
|
||||
**变更类型**: 规范文档更新
|
||||
**影响范围**: 移动端开发规范、图标系统
|
||||
|
||||
#### 变更内容
|
||||
|
||||
1. **将 SVG Data URI 图标方案写入 cursorrules 规范**
|
||||
|
||||
- 在`.cursor/cursorrules-mobile.md`中添加第 5 章"图标使用规范"
|
||||
- 详细说明 SVG Data URI 方案的背景、优势和使用方法
|
||||
- 提供完整的图标库列表(17 个图标)
|
||||
- 明确强制性规范要求
|
||||
|
||||
2. **规范核心要求**
|
||||
|
||||
- 禁止使用 uview-plus 的 u-icon 组件
|
||||
- 统一使用 SVG Data URI 方案
|
||||
- 图标类名必须使用`icon-`前缀
|
||||
- 通过 CSS filter 实现颜色自定义
|
||||
|
||||
3. **更新章节编号**
|
||||
- 调整原有章节编号(第 5 章及以后)
|
||||
- 确保规范文档结构清晰
|
||||
|
||||
#### 技术细节
|
||||
|
||||
- **配置文件位置**: `mobile/src/static/fonts/iconfont.css`
|
||||
- **全局引入**: 在`main.js`中导入样式
|
||||
- **可用图标**: 基础图标 4 个、操作图标 5 个、导航图标 4 个、联系图标 2 个
|
||||
- **颜色方案**: 提供主题色、成功色、警告色、错误色、灰色的 filter 值
|
||||
|
||||
#### 文档更新
|
||||
|
||||
- 更新知识库 memory,记录规范化的图标方案
|
||||
- 确保后续移动端开发都遵循统一的图标使用规范
|
||||
|
||||
#### 预期效果
|
||||
|
||||
- 后续 Cursor AI 生成移动端代码时自动采用 SVG Data URI 图标方案
|
||||
- 解决 uview-plus 图标显示异常问题
|
||||
- 提供稳定、高效、跨平台的图标解决方案
|
||||
|
||||
---
|
||||
|
||||
## 🎉 SVG 图标方案最终成功 - 2025-01-03 18:15:42
|
||||
|
||||
### 📋 变更概述
|
||||
|
||||
**目的**: 经过多次尝试(Font Awesome、iconfont 项目),最终采用 SVG Data URI 方案成功解决图标显示问题,实现稳定可靠的图标系统。
|
||||
|
||||
### 🔧 最终技术实现
|
||||
|
||||
#### 1. **SVG Data URI 图标** (`mobile/src/static/fonts/iconfont.css`)
|
||||
|
||||
**核心原理**:
|
||||
|
||||
```css
|
||||
.iconfont {
|
||||
display: inline-block;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
/* 用户图标 */
|
||||
.icon-user {
|
||||
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor'%3E%3Cpath d='M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z'/%3E%3C/svg%3E");
|
||||
}
|
||||
```
|
||||
|
||||
#### 2. **技术优势**
|
||||
|
||||
- ✅ **零依赖**: 不依赖任何外部字体文件或 CDN
|
||||
- ✅ **加载稳定**: SVG 直接嵌入 CSS,无 404 错误
|
||||
- ✅ **跨平台兼容**: 所有设备都能正常显示
|
||||
- ✅ **性能优异**: 无网络请求,加载速度快
|
||||
- ✅ **可定制**: 通过 CSS filter 调整颜色
|
||||
|
||||
#### 3. **颜色自定义示例**
|
||||
|
||||
```css
|
||||
/* 主题色 #5d87ff */
|
||||
.icon-custom {
|
||||
filter: brightness(0) saturate(100%) invert(38%) sepia(87%) saturate(6400%) hue-rotate(225deg)
|
||||
brightness(104%) contrast(104%);
|
||||
}
|
||||
|
||||
/* 灰色 #b0b8c9 */
|
||||
.icon-gray {
|
||||
filter: brightness(0) saturate(100%) invert(67%) sepia(8%) saturate(1179%) hue-rotate(195deg)
|
||||
brightness(95%) contrast(88%);
|
||||
}
|
||||
```
|
||||
|
||||
### 📊 解决方案对比
|
||||
|
||||
| 方案 | 依赖性 | 稳定性 | 加载速度 | 自定义性 | 兼容性 |
|
||||
| ---------------- | ----------- | ----------- | -------- | ------------- | ------- |
|
||||
| **SVG Data URI** | 🟢 无依赖 | 🟢 100%稳定 | 🟢 极快 | 🟢 CSS filter | 🟢 完美 |
|
||||
| Font Awesome CDN | 🔴 CDN 依赖 | 🟡 网络依赖 | 🟡 一般 | 🟢 CSS 属性 | 🟢 良好 |
|
||||
| iconfont 项目 | 🔴 字体文件 | 🔴 404 错误 | 🔴 失败 | 🟢 CSS 属性 | 🔴 失败 |
|
||||
| Emoji | 🟢 无依赖 | 🟡 设备差异 | 🟢 快 | 🔴 无法定制 | 🟡 一般 |
|
||||
|
||||
### 🎯 成功验证
|
||||
|
||||
1. **图标演示页面**: http://localhost:5173/#/pages/iconfont-demo/index ✅
|
||||
2. **登录页面图标**: http://localhost:5173/#/pages/login/index ✅
|
||||
3. **17 个图标**: 基础、操作、导航、联系四大类全部正常显示 ✅
|
||||
|
||||
### 📱 最终使用方式
|
||||
|
||||
```vue
|
||||
<!-- 基础用法 -->
|
||||
<text class="iconfont icon-user"></text>
|
||||
<text class="iconfont icon-lock"></text>
|
||||
|
||||
<!-- 自定义大小和颜色 -->
|
||||
<text
|
||||
class="iconfont icon-login"
|
||||
:style="{
|
||||
width: '24px',
|
||||
height: '24px',
|
||||
filter: 'brightness(0) saturate(100%) invert(38%) sepia(87%) saturate(6400%) hue-rotate(225deg) brightness(104%) contrast(104%)',
|
||||
}"
|
||||
></text>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎨 引入 iconfont.cn 图标库 - 2025-01-03 17:30:15
|
||||
|
||||
### 📋 变更概述
|
||||
|
||||
**目的**: 在移动端 uni-app 项目中引入 iconfont.cn 图标字体库,解决 uview-plus 图标显示异常问题,提供稳定的矢量图标解决方案。
|
||||
|
||||
### 🔧 修改内容
|
||||
|
||||
#### 1. **创建 iconfont 字体配置** (`mobile/src/static/fonts/iconfont.css`)
|
||||
|
||||
**新增完整的 iconfont.css 文件**:
|
||||
|
||||
```css
|
||||
@font-face {
|
||||
font-family: 'iconfont'; /* Project id 4793979 */
|
||||
src: url('//at.alicdn.com/t/c/font_4793979_9m3yqql3ybj.woff2?t=1704153600') format('woff2'), url('//at.alicdn.com/t/c/font_4793979_9m3yqql3ybj.woff?t=1704153600')
|
||||
format('woff'),
|
||||
url('//at.alicdn.com/t/c/font_4793979_9m3yqql3ybj.ttf?t=1704153600') format('truetype');
|
||||
}
|
||||
|
||||
.iconfont {
|
||||
font-family: 'iconfont' !important;
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
/* 包含17个常用图标类定义 */
|
||||
.icon-user:before {
|
||||
content: '\e7ae';
|
||||
} /* 用户图标 */
|
||||
.icon-lock:before {
|
||||
content: '\e60e';
|
||||
} /* 密码锁图标 */
|
||||
.icon-eye:before {
|
||||
content: '\e61c';
|
||||
} /* 眼睛图标 */
|
||||
.icon-login:before {
|
||||
content: '\e612';
|
||||
} /* 登录图标 */
|
||||
/* ... 更多图标 */
|
||||
```
|
||||
|
||||
#### 2. **全局样式引入** (`mobile/src/main.js`)
|
||||
|
||||
**添加 iconfont 样式导入**:
|
||||
|
||||
```javascript
|
||||
// 导入 iconfont 样式
|
||||
import '@/static/fonts/iconfont.css';
|
||||
```
|
||||
|
||||
#### 3. **登录页面图标更新** (`mobile/src/pages/login/index.vue`)
|
||||
|
||||
**替换 emoji 为 iconfont 图标**:
|
||||
|
||||
```vue
|
||||
<!-- 之前使用emoji -->
|
||||
<text class="input-icon-emoji">👤</text>
|
||||
<text class="input-icon-emoji">🔒</text>
|
||||
|
||||
<!-- 现在使用iconfont -->
|
||||
<text class="iconfont icon-user input-icon"></text>
|
||||
<text class="iconfont icon-lock input-icon"></text>
|
||||
|
||||
<!-- 登录按钮添加图标 -->
|
||||
<button class="login-btn" @click="onLogin">
|
||||
<text class="iconfont icon-login"></text>
|
||||
<text class="login-btn-text">登录</text>
|
||||
</button>
|
||||
```
|
||||
|
||||
#### 4. **创建图标演示页面** (`mobile/src/pages/iconfont-demo/index.vue`)
|
||||
|
||||
**功能特性**:
|
||||
|
||||
- 展示所有 17 个可用图标
|
||||
- 按分类展示(基础、操作、导航、联系)
|
||||
- 包含使用代码示例
|
||||
- 响应式网格布局
|
||||
- 图标悬停效果
|
||||
|
||||
#### 5. **页面路由配置** (`mobile/src/pages.json`)
|
||||
|
||||
**新增演示页面路由**:
|
||||
|
||||
```json
|
||||
{
|
||||
"path": "pages/iconfont-demo/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "图标演示",
|
||||
"navigationBarBackgroundColor": "#5d87ff",
|
||||
"navigationBarTextStyle": "white"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 6. **创建使用指南文档** (`docs/开发文档/移动端iconfont图标使用指南.md`)
|
||||
|
||||
**包含内容**:
|
||||
|
||||
- 完整的安装配置说明
|
||||
- 17 个图标的详细列表(类名、说明、编码)
|
||||
- 多种使用场景的代码示例
|
||||
- 样式自定义指南
|
||||
- 扩展图标的方法
|
||||
- 兼容性说明和最佳实践
|
||||
|
||||
### 📊 图标库特性
|
||||
|
||||
#### 可用图标列表 (17 个)
|
||||
|
||||
**基础图标**: user, lock, eye, eye-close
|
||||
**操作图标**: login, search, add, check, close
|
||||
**导航图标**: home, message, setting, arrow-right
|
||||
**联系图标**: phone, mail
|
||||
|
||||
#### 技术优势
|
||||
|
||||
1. **跨平台兼容**: 支持 H5、微信小程序、App、HarmonyOS
|
||||
2. **矢量图标**: 无限缩放不失真,适配各种屏幕密度
|
||||
3. **性能优化**: CDN 加载,字体平滑处理
|
||||
4. **灵活定制**: CSS 自定义大小、颜色、样式
|
||||
|
||||
### 🎯 解决的问题
|
||||
|
||||
1. **uview-plus 图标异常**: u-icon 组件显示为横线的问题彻底解决
|
||||
2. **emoji 兼容性**: 不同设备 emoji 显示不一致的问题
|
||||
3. **图标扩展性**: 提供更丰富的图标选择和自定义能力
|
||||
4. **字体文件 404 问题**: 使用 SVG Data URI 避免外部字体文件依赖
|
||||
|
||||
### 📱 使用方式
|
||||
|
||||
```vue
|
||||
<!-- 基础用法 -->
|
||||
<text class="iconfont icon-user"></text>
|
||||
|
||||
<!-- 自定义样式 -->
|
||||
<text class="iconfont icon-home custom-icon"></text>
|
||||
|
||||
<!-- 登录表单应用 -->
|
||||
<view class="input-group">
|
||||
<text class="iconfont icon-user input-icon"></text>
|
||||
<input class="login-input" placeholder="账号" />
|
||||
</view>
|
||||
```
|
||||
|
||||
### 📝 文档覆盖
|
||||
|
||||
- ✅ **技术文档**: 完整的使用指南和 API 参考
|
||||
- ✅ **演示页面**: 可视化的图标展示和代码示例
|
||||
- ✅ **变更记录**: 详细的实现过程和配置说明
|
||||
- ✅ **最佳实践**: 命名规范、性能优化、回退方案
|
||||
|
||||
### 🚀 后续计划
|
||||
|
||||
1. **图标扩展**: 根据业务需求添加更多图标
|
||||
2. **主题适配**: 适配暗色模式和多主题切换
|
||||
3. **组件封装**: 封装常用的图标组件,简化使用
|
||||
|
||||
---
|
||||
|
||||
## 📱 完善移动端文档同步规范 - 2025-01-03 16:45:23
|
||||
|
||||
### 📋 变更概述
|
||||
|
||||
**目的**: 完善.cursorrules 主配置文件,补充移动端工程的强制性文档同步更新规范,确保移动端代码变更时也能及时更新相关文档。
|
||||
|
||||
### 🔧 修改内容
|
||||
|
||||
#### 1. **主配置文件更新** (`.cursorrules`)
|
||||
|
||||
**位置**: "强制性文档同步更新" → "必须更新的文档" 部分
|
||||
|
||||
**新增内容**:
|
||||
|
||||
```markdown
|
||||
- **📱 移动端变更** → 移动端工程相关文档必须同步更新:
|
||||
- **移动端 API 新增/变更** → 相应的 API 文档必须补充移动端实现章节
|
||||
- **移动端页面开发** → `docs/开发文档/` 移动端相关文档
|
||||
- **移动端功能变更** → `docs/业务模块/[模块名称]/` 移动端部分
|
||||
- **移动端技术规范变更** → `.cursor/cursorrules-mobile.md`
|
||||
```
|
||||
|
||||
### 📊 规范意义
|
||||
|
||||
1. **统一标准**: 移动端、前端、后端都遵循相同的文档同步要求
|
||||
2. **防止遗漏**: 明确规定移动端变更时必须更新的文档类型
|
||||
3. **完整覆盖**:
|
||||
- API 变更 → API 文档补充移动端实现
|
||||
- 页面开发 → 开发文档记录
|
||||
- 功能变更 → 业务模块文档更新
|
||||
- 技术规范 → cursorrules 文档维护
|
||||
|
||||
### 📝 强制执行要求
|
||||
|
||||
- **移动端 API 新增/变更**: 必须在对应的 API 文档中补充移动端实现章节
|
||||
- **移动端页面开发**: 必须在`docs/开发文档/`中记录相关文档
|
||||
- **移动端功能变更**: 必须更新`docs/业务模块/[模块名称]/`中的移动端部分
|
||||
- **移动端技术规范变更**: 必须同步更新`.cursor/cursorrules-mobile.md`
|
||||
|
||||
### 🎯 价值目标
|
||||
|
||||
- 📚 **文档完整性**: 确保移动端变更有完整的文档记录
|
||||
- 🔄 **规范一致性**: 所有技术栈都遵循统一的文档管理规范
|
||||
- 📖 **知识传承**: 移动端开发经验能够有效积累和传承
|
||||
- 🚀 **团队效率**: 为后续移动端开发提供准确的文档支持
|
||||
|
||||
---
|
||||
|
||||
## 📱 移动端登录页面完整开发 - 2025-01-03 15:30:12
|
||||
@@ -1,5 +1,239 @@
|
||||
# 代码调整记录 - 2025-07-01 - wuzilong
|
||||
|
||||
## 2025-07-02 19:30:00 - 实现全局路由守卫功能
|
||||
|
||||
### 🎯 变更目标
|
||||
|
||||
- 实现用户登录失效或未登录时,访问任何需要权限的页面都自动跳转到登录页
|
||||
- 建立完整的路由权限控制体系
|
||||
- 提供多层次的权限检查机制
|
||||
- 增强用户体验和系统安全性
|
||||
|
||||
### 🔧 修改内容
|
||||
|
||||
**1. 新增全局路由守卫模块**
|
||||
|
||||
文件:`mobile/src/utils/route-guard.js`
|
||||
|
||||
```javascript
|
||||
// 核心功能
|
||||
- checkPageAccess() // 全局页面权限检查
|
||||
- guardedNavigate() // 带权限检查的页面跳转
|
||||
- guardedSwitchTab() // 带权限检查的Tab切换
|
||||
- routeGuardMixin // 页面混入,自动权限检查
|
||||
- initRouteGuard() // 初始化全局路由守卫
|
||||
|
||||
// 配置管理
|
||||
- PUBLIC_PAGES: ['/pages/login/index', '/pages/index/index'] // 公开页面列表
|
||||
- LOGIN_PAGE: '/pages/login/index' // 登录页路径
|
||||
```
|
||||
|
||||
**功能特性**:
|
||||
- ✅ 自动检测当前页面路径
|
||||
- ✅ 区分公开页面和受保护页面
|
||||
- ✅ 多重登录状态验证(isLoggedIn + Token存在 + Token未过期)
|
||||
- ✅ 优雅的错误提示和跳转
|
||||
- ✅ 支持页面加载和显示时的权限检查
|
||||
- ✅ 提供页面混入和手动调用两种使用方式
|
||||
|
||||
**2. 修改应用初始化**
|
||||
|
||||
文件:`mobile/src/App.vue`
|
||||
|
||||
```javascript
|
||||
import { initRouteGuard } from '@/utils/route-guard';
|
||||
|
||||
export default {
|
||||
onLaunch: function () {
|
||||
console.log('App Launch');
|
||||
// 初始化全局路由守卫
|
||||
initRouteGuard();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**3. 更新页面权限检查**
|
||||
|
||||
文件:`mobile/src/pages/profile/index.vue`
|
||||
|
||||
```javascript
|
||||
import { checkPageAccess } from '@/utils/route-guard';
|
||||
|
||||
// 页面生命周期权限检查
|
||||
onLoad() {
|
||||
if (!checkPageAccess()) return;
|
||||
// 其他初始化逻辑...
|
||||
},
|
||||
onShow() {
|
||||
if (!checkPageAccess()) return;
|
||||
// 刷新用户信息...
|
||||
}
|
||||
```
|
||||
|
||||
文件:`mobile/src/pages/workbench/index.vue`
|
||||
|
||||
```javascript
|
||||
import { checkPageAccess } from '@/utils/route-guard';
|
||||
|
||||
// 同样的权限检查模式
|
||||
onLoad() {
|
||||
if (!checkPageAccess()) return;
|
||||
this.checkLoginStatus();
|
||||
},
|
||||
onShow() {
|
||||
if (!checkPageAccess()) return;
|
||||
}
|
||||
```
|
||||
|
||||
**4. 优化首页跳转逻辑**
|
||||
|
||||
文件:`mobile/src/pages/index/index.vue`
|
||||
|
||||
```javascript
|
||||
import { guardedSwitchTab } from '@/utils/route-guard';
|
||||
|
||||
// 使用带权限检查的Tab切换
|
||||
navigateToService() {
|
||||
guardedSwitchTab('/pages/workbench/index');
|
||||
}
|
||||
```
|
||||
|
||||
**5. 增强HTTP请求拦截器**
|
||||
|
||||
文件:`mobile/src/utils/request.js`
|
||||
|
||||
```javascript
|
||||
// 401响应自动处理
|
||||
function handleUnauthorized(message = '登录已失效,请重新登录') {
|
||||
// 清除登录信息
|
||||
uni.removeStorageSync('access_token');
|
||||
uni.removeStorageSync('userInfo');
|
||||
uni.removeStorageSync('isLoggedIn');
|
||||
|
||||
// 显示提示并跳转
|
||||
uni.showToast({ title: message, icon: 'none' });
|
||||
setTimeout(() => {
|
||||
uni.reLaunch({ url: '/pages/login/index' });
|
||||
}, 500);
|
||||
}
|
||||
|
||||
// HTTP响应拦截
|
||||
success: (res) => {
|
||||
if (res.data.code === 401 || res.statusCode === 401) {
|
||||
handleUnauthorized(res.data.msg || '登录已失效,请重新登录');
|
||||
reject(res.data);
|
||||
}
|
||||
// 其他处理...
|
||||
}
|
||||
```
|
||||
|
||||
**6. 新增测试页面**
|
||||
|
||||
文件:`mobile/src/pages/test-route/index.vue`
|
||||
|
||||
- 🧪 路由守卫功能测试页面
|
||||
- 📊 实时显示登录状态、Token状态、用户信息
|
||||
- 🔧 提供测试按钮:清除Token、带守卫跳转等
|
||||
- 📋 详细的测试日志显示
|
||||
|
||||
### 📝 技术细节
|
||||
|
||||
**权限检查流程**:
|
||||
|
||||
1. **页面路径检查** → 判断是否为公开页面
|
||||
2. **登录状态检查** → userStorage.isLoggedIn()
|
||||
3. **Token存在检查** → userStorage.getToken()
|
||||
4. **Token过期检查** → userState.checkTokenExpiration()
|
||||
|
||||
**多层次拦截机制**:
|
||||
|
||||
1. **页面级拦截** → onLoad/onShow 中调用 checkPageAccess()
|
||||
2. **跳转级拦截** → guardedNavigate/guardedSwitchTab 预检查
|
||||
3. **HTTP级拦截** → 401响应自动处理
|
||||
4. **应用级拦截** → App进入前台时状态检查
|
||||
|
||||
**用户体验优化**:
|
||||
|
||||
- ✅ 友好的错误提示信息
|
||||
- ✅ 延迟跳转确保Toast显示
|
||||
- ✅ 使用reLaunch清除页面栈
|
||||
- ✅ 失败时自动尝试备用跳转方式
|
||||
|
||||
### ✅ 验证结果
|
||||
|
||||
**权限控制测试**:
|
||||
- ✅ 未登录访问受保护页面 → 自动跳转登录页
|
||||
- ✅ Token过期访问受保护页面 → 清除信息并跳转
|
||||
- ✅ 登录后正常访问所有页面
|
||||
- ✅ 公开页面无论何时都可访问
|
||||
|
||||
**跳转功能测试**:
|
||||
- ✅ guardedNavigate 正确拦截未授权跳转
|
||||
- ✅ guardedSwitchTab 正确处理Tab切换
|
||||
- ✅ HTTP 401响应自动清理和跳转
|
||||
|
||||
**兼容性测试**:
|
||||
- ✅ 不影响现有登录流程
|
||||
- ✅ 与现有用户状态管理兼容
|
||||
- ✅ 页面加载性能无影响
|
||||
|
||||
### 🎯 影响评估
|
||||
|
||||
**安全性提升**:
|
||||
- 🔒 完全防止未授权页面访问
|
||||
- 🔒 自动处理Token失效情况
|
||||
- 🔒 多层次权限检查防止绕过
|
||||
|
||||
**用户体验改善**:
|
||||
- 🎨 无感知的权限检查
|
||||
- 🎨 友好的错误提示
|
||||
- 🎨 自动跳转减少用户操作
|
||||
|
||||
**开发效率提升**:
|
||||
- 🚀 统一的权限检查工具
|
||||
- 🚀 简单的页面集成方式
|
||||
- 🚀 完善的测试工具
|
||||
|
||||
### 💡 最佳实践建议
|
||||
|
||||
**页面开发规范**:
|
||||
```javascript
|
||||
// 在需要权限的页面中
|
||||
import { checkPageAccess } from '@/utils/route-guard';
|
||||
|
||||
onLoad() {
|
||||
if (!checkPageAccess()) return;
|
||||
// 页面初始化逻辑
|
||||
},
|
||||
onShow() {
|
||||
if (!checkPageAccess()) return;
|
||||
// 页面显示逻辑
|
||||
}
|
||||
```
|
||||
|
||||
**页面跳转规范**:
|
||||
```javascript
|
||||
// 使用带权限检查的跳转
|
||||
import { guardedNavigate, guardedSwitchTab } from '@/utils/route-guard';
|
||||
|
||||
// 普通页面跳转
|
||||
guardedNavigate('/pages/some-page/index');
|
||||
|
||||
// Tab页面切换
|
||||
guardedSwitchTab('/pages/workbench/index');
|
||||
```
|
||||
|
||||
**公开页面配置**:
|
||||
```javascript
|
||||
// 在 route-guard.js 中配置公开页面
|
||||
const PUBLIC_PAGES = [
|
||||
'/pages/login/index', // 登录页
|
||||
'/pages/index/index' // 首页(功能受限)
|
||||
];
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2025-07-02 18:00:00 - 解决移动端 Buffer 兼容性问题
|
||||
|
||||
### 🐛 问题描述
|
||||
|
||||
+193
-2
@@ -460,6 +460,197 @@ admin 用户具有系统最高权限`*:*:*`,可以访问所有功能模块和
|
||||
|
||||
---
|
||||
|
||||
---
|
||||
|
||||
## 📱 移动端登录实现
|
||||
|
||||
### 移动端特殊配置
|
||||
|
||||
移动端使用uni-app框架,登录实现与web端保持API兼容,但在实现细节上有所差异:
|
||||
|
||||
**网络请求配置**:
|
||||
```javascript
|
||||
// mobile/src/utils/request.js
|
||||
const baseURL = 'http://localhost:8080'
|
||||
|
||||
const request = (options) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const token = getToken()
|
||||
|
||||
uni.request({
|
||||
url: baseURL + options.url,
|
||||
method: options.method || 'GET',
|
||||
data: options.data || {},
|
||||
header: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': token,
|
||||
'clientid': 'e5cd7e4891bf95d1d19206ce24a7b32e',
|
||||
'referer': baseURL,
|
||||
...options.header,
|
||||
},
|
||||
// 响应处理逻辑...
|
||||
})
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
**登录API调用**:
|
||||
```javascript
|
||||
// 移动端登录API
|
||||
export const api = {
|
||||
login: (data) =>
|
||||
request({
|
||||
url: '/auth/login',
|
||||
method: 'POST',
|
||||
data: {
|
||||
tenantId: data.tenantId || '000000',
|
||||
username: data.username,
|
||||
password: data.password,
|
||||
clientId: data.clientId || 'e5cd7e4891bf95d1d19206ce24a7b32e',
|
||||
grantType: data.grantType || 'password'
|
||||
},
|
||||
header: {
|
||||
isToken: false,
|
||||
isEncrypt: true,
|
||||
repeatSubmit: false
|
||||
}
|
||||
}),
|
||||
|
||||
getUserInfo: () =>
|
||||
request({
|
||||
url: '/system/user/getInfo',
|
||||
method: 'GET'
|
||||
}),
|
||||
|
||||
// 其他API...
|
||||
}
|
||||
```
|
||||
|
||||
### 移动端用户信息缓存
|
||||
|
||||
**用户信息存储工具**:
|
||||
```javascript
|
||||
// mobile/src/utils/request.js
|
||||
export const userStorage = {
|
||||
// 保存登录信息
|
||||
saveLoginInfo: (loginResult, userInfo) => {
|
||||
try {
|
||||
uni.setStorageSync('access_token', loginResult.access_token)
|
||||
if (userInfo) {
|
||||
uni.setStorageSync('userInfo', JSON.stringify(userInfo))
|
||||
}
|
||||
uni.setStorageSync('isLoggedIn', true)
|
||||
} catch (e) {
|
||||
console.error('保存登录信息失败:', e)
|
||||
}
|
||||
},
|
||||
|
||||
// 获取用户信息
|
||||
getUserInfo: () => {
|
||||
try {
|
||||
const userInfo = uni.getStorageSync('userInfo')
|
||||
return userInfo ? JSON.parse(userInfo) : null
|
||||
} catch (e) {
|
||||
return null
|
||||
}
|
||||
},
|
||||
|
||||
// 检查登录状态
|
||||
isLoggedIn: () => {
|
||||
try {
|
||||
const isLoggedIn = uni.getStorageSync('isLoggedIn')
|
||||
const token = uni.getStorageSync('access_token')
|
||||
return !!(isLoggedIn && token)
|
||||
} catch (e) {
|
||||
return false
|
||||
}
|
||||
},
|
||||
|
||||
// 清除登录信息
|
||||
clearLoginInfo: () => {
|
||||
try {
|
||||
uni.removeStorageSync('access_token')
|
||||
uni.removeStorageSync('userInfo')
|
||||
uni.removeStorageSync('isLoggedIn')
|
||||
} catch (e) {
|
||||
console.error('清除登录信息失败:', e)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 移动端登录流程
|
||||
|
||||
1. **应用启动** → 检查登录状态 → 未登录自动跳转登录页
|
||||
2. **用户登录** → 调用`/auth/login`API → 获取access_token
|
||||
3. **获取用户信息** → 调用`/system/user/getInfo`API → 获取用户详情
|
||||
4. **缓存信息** → 使用`userStorage.saveLoginInfo()`缓存登录信息
|
||||
5. **页面访问** → 所有页面自动检查登录状态
|
||||
6. **退出登录** → 清除本地缓存 → 跳转登录页
|
||||
|
||||
### 移动端页面入口配置
|
||||
|
||||
**pages.json配置**:
|
||||
```json
|
||||
{
|
||||
"pages": [
|
||||
{
|
||||
"path": "pages/login/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "登录",
|
||||
"navigationStyle": "custom",
|
||||
"navigationBarBackgroundColor": "transparent",
|
||||
"backgroundColor": "transparent"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 移动端登录状态验证
|
||||
|
||||
**全局登录验证**:
|
||||
```javascript
|
||||
// 在每个需要登录的页面中
|
||||
onLoad() {
|
||||
this.checkLoginStatus();
|
||||
},
|
||||
|
||||
methods: {
|
||||
checkLoginStatus() {
|
||||
if (!userStorage.isLoggedIn()) {
|
||||
uni.reLaunch({
|
||||
url: '/pages/login/index'
|
||||
});
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 移动端与web端差异
|
||||
|
||||
| 特性 | Web端 | 移动端 |
|
||||
|------|-------|--------|
|
||||
| 框架 | Vue 3 + TypeScript | Vue 3 + uni-app |
|
||||
| 存储 | localStorage | uni.storage |
|
||||
| 网络请求 | axios | uni.request |
|
||||
| 路由跳转 | router.push | uni.navigateTo/reLaunch |
|
||||
| UI组件 | Element Plus | uview-plus |
|
||||
| 加密方式 | AES+RSA | 支持,但简化实现 |
|
||||
|
||||
### 移动端登录页面特性
|
||||
|
||||
- ✅ **现代化UI**: 渐变背景 + 毛玻璃效果
|
||||
- ✅ **租户选择**: 支持多租户选择(与web端一致)
|
||||
- ✅ **表单验证**: 完整的输入验证和错误提示
|
||||
- ✅ **响应式设计**: 适配不同屏幕尺寸
|
||||
- ✅ **无障碍访问**: 支持键盘导航和屏幕阅读器
|
||||
- ✅ **安全存储**: 使用uni.storage安全存储用户信息
|
||||
|
||||
---
|
||||
|
||||
## 📞 联系方式
|
||||
|
||||
如有认证相关问题,请联系开发团队或查看项目仓库的 Issues。
|
||||
@@ -467,5 +658,5 @@ admin 用户具有系统最高权限`*:*:*`,可以访问所有功能模块和
|
||||
---
|
||||
|
||||
**📝 维护**: 用户认证登录 API 文档
|
||||
**🔄 更新**: 2025-07-01
|
||||
**�� 文件**: 用户认证登录 API.md
|
||||
**🔄 更新**: 2025-01-03
|
||||
**📁 文件**: 用户认证登录 API.md
|
||||
@@ -0,0 +1,351 @@
|
||||
# 移动端 iconfont 图标使用指南
|
||||
|
||||
## 📋 概述
|
||||
|
||||
本项目已集成 iconfont.cn 图标字体库,提供丰富的矢量图标,替代 emoji 和 uview-plus 图标,确保跨平台兼容性。
|
||||
|
||||
## 🎯 解决的问题
|
||||
|
||||
- **uview-plus 图标显示异常**:u-icon 组件在某些环境下显示为横线
|
||||
- **emoji 兼容性问题**:不同设备和系统 emoji 显示效果不一致
|
||||
- **图标扩展性**:iconfont 提供更丰富的图标选择
|
||||
|
||||
## 📦 安装配置
|
||||
|
||||
### 1. 字体文件配置
|
||||
|
||||
**文件位置**:`mobile/src/static/fonts/iconfont.css`
|
||||
|
||||
```css
|
||||
@font-face {
|
||||
font-family: 'iconfont';
|
||||
src: url('//at.alicdn.com/t/c/font_4793979_9m3yqql3ybj.woff2?t=1704153600') format('woff2'), url('//at.alicdn.com/t/c/font_4793979_9m3yqql3ybj.woff?t=1704153600')
|
||||
format('woff'),
|
||||
url('//at.alicdn.com/t/c/font_4793979_9m3yqql3ybj.ttf?t=1704153600') format('truetype');
|
||||
}
|
||||
|
||||
.iconfont {
|
||||
font-family: 'iconfont' !important;
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 全局引入
|
||||
|
||||
**文件位置**:`mobile/src/main.js`
|
||||
|
||||
```javascript
|
||||
// 导入 iconfont 样式
|
||||
import '@/static/fonts/iconfont.css';
|
||||
```
|
||||
|
||||
## 🎨 可用图标列表
|
||||
|
||||
### 基础图标
|
||||
|
||||
| 图标 | 类名 | 说明 | 编码 |
|
||||
| ---- | ---------------- | -------- | ------- |
|
||||
| 👤 | `icon-user` | 用户 | `\e7ae` |
|
||||
| 🔒 | `icon-lock` | 锁定 | `\e60e` |
|
||||
| 👁️ | `icon-eye` | 显示密码 | `\e61c` |
|
||||
| 🙈 | `icon-eye-close` | 隐藏密码 | `\e61d` |
|
||||
|
||||
### 操作图标
|
||||
|
||||
| 图标 | 类名 | 说明 | 编码 |
|
||||
| ---- | ------------- | ---- | ------- |
|
||||
| 🚪 | `icon-login` | 登录 | `\e612` |
|
||||
| 🔍 | `icon-search` | 搜索 | `\e6b4` |
|
||||
| ➕ | `icon-add` | 添加 | `\e6a4` |
|
||||
| ✅ | `icon-check` | 确认 | `\e6a8` |
|
||||
| ❌ | `icon-close` | 关闭 | `\e6a9` |
|
||||
|
||||
### 导航图标
|
||||
|
||||
| 图标 | 类名 | 说明 | 编码 |
|
||||
| ---- | ------------------ | ------ | ------- |
|
||||
| 🏠 | `icon-home` | 首页 | `\e65e` |
|
||||
| 💬 | `icon-message` | 消息 | `\e6a7` |
|
||||
| ⚙️ | `icon-setting` | 设置 | `\e6b8` |
|
||||
| ➡️ | `icon-arrow-right` | 箭头右 | `\e6a3` |
|
||||
|
||||
### 联系图标
|
||||
|
||||
| 图标 | 类名 | 说明 | 编码 |
|
||||
| ---- | ------------ | ---- | ------- |
|
||||
| 📞 | `icon-phone` | 电话 | `\e69a` |
|
||||
| 📧 | `icon-mail` | 邮箱 | `\e692` |
|
||||
|
||||
## 🚀 使用方法
|
||||
|
||||
### 1. 基础用法
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<!-- 基础图标 -->
|
||||
<text class="iconfont icon-user"></text>
|
||||
<text class="iconfont icon-lock"></text>
|
||||
|
||||
<!-- 带样式的图标 -->
|
||||
<text class="iconfont icon-home custom-icon"></text>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.custom-icon {
|
||||
font-size: 24px;
|
||||
color: #5d87ff;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
### 2. 登录表单中的应用
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<view class="input-group">
|
||||
<!-- 用户名输入框 -->
|
||||
<text class="iconfont icon-user input-icon"></text>
|
||||
<input class="login-input" type="text" placeholder="账号" />
|
||||
</view>
|
||||
|
||||
<view class="input-group">
|
||||
<!-- 密码输入框 -->
|
||||
<text class="iconfont icon-lock input-icon"></text>
|
||||
<input class="login-input" type="password" placeholder="密码" />
|
||||
</view>
|
||||
|
||||
<!-- 登录按钮 -->
|
||||
<button class="login-btn">
|
||||
<text class="iconfont icon-login"></text>
|
||||
<text>登录</text>
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
.input-group {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.input-icon {
|
||||
position: absolute;
|
||||
left: 14px;
|
||||
z-index: 2;
|
||||
font-size: 18px;
|
||||
color: #b0b8c9;
|
||||
}
|
||||
|
||||
.login-input {
|
||||
width: 100%;
|
||||
height: 44px;
|
||||
padding: 0 12px 0 44px;
|
||||
border: 1px solid #e3e8f0;
|
||||
border-radius: 8px;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.login-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
background: #5d87ff;
|
||||
color: #fff;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
### 3. 动态图标切换
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<view class="password-toggle">
|
||||
<text
|
||||
:class="['iconfont', showPassword ? 'icon-eye-close' : 'icon-eye']"
|
||||
@click="togglePassword"
|
||||
></text>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
showPassword: false,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
togglePassword() {
|
||||
this.showPassword = !this.showPassword;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
```
|
||||
|
||||
## 🎨 样式自定义
|
||||
|
||||
### 1. 基础样式属性
|
||||
|
||||
```css
|
||||
.iconfont {
|
||||
/* 字体大小 */
|
||||
font-size: 16px;
|
||||
|
||||
/* 颜色 */
|
||||
color: #333;
|
||||
|
||||
/* 其他样式 */
|
||||
opacity: 0.8;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 响应式设计
|
||||
|
||||
```css
|
||||
/* 小屏幕 */
|
||||
@media (max-width: 375px) {
|
||||
.iconfont {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 大屏幕 */
|
||||
@media (min-width: 768px) {
|
||||
.iconfont {
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 主题适配
|
||||
|
||||
```css
|
||||
/* 亮色主题 */
|
||||
.light-theme .iconfont {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
/* 暗色主题 */
|
||||
.dark-theme .iconfont {
|
||||
color: #fff;
|
||||
}
|
||||
```
|
||||
|
||||
## 🔧 扩展图标
|
||||
|
||||
### 1. 从 iconfont.cn 添加新图标
|
||||
|
||||
1. **访问项目**:打开 [iconfont.cn](https://www.iconfont.cn/) 的项目页面
|
||||
2. **选择图标**:浏览并添加需要的图标到项目
|
||||
3. **生成代码**:下载新的字体文件和 CSS 代码
|
||||
4. **更新项目**:替换 `iconfont.css` 中的字体链接和图标类名
|
||||
|
||||
### 2. 图标命名规范
|
||||
|
||||
```css
|
||||
/* 建议的命名格式 */
|
||||
.icon-[功能]-[状态]
|
||||
|
||||
例如:
|
||||
.icon-user /* 用户图标 */
|
||||
.icon-eye-open /* 眼睛打开 */
|
||||
.icon-eye-close /* 眼睛关闭 */
|
||||
.icon-arrow-left /* 左箭头 */
|
||||
.icon-arrow-right; /* 右箭头 */
|
||||
```
|
||||
|
||||
## 📱 演示页面
|
||||
|
||||
**页面位置**:`mobile/src/pages/iconfont-demo/index.vue`
|
||||
|
||||
访问路径:`/pages/iconfont-demo/index`
|
||||
|
||||
演示页面包含:
|
||||
|
||||
- 所有可用图标的预览
|
||||
- 图标分类展示(基础、操作、导航、联系)
|
||||
- 使用代码示例
|
||||
- 自定义样式演示
|
||||
|
||||
## ⚠️ 注意事项
|
||||
|
||||
### 1. 兼容性
|
||||
|
||||
- ✅ **支持平台**:H5、微信小程序、App、支付宝小程序
|
||||
- ✅ **浏览器支持**:Chrome、Safari、Firefox、Edge
|
||||
- ✅ **设备支持**:iOS、Android、HarmonyOS
|
||||
|
||||
### 2. 性能优化
|
||||
|
||||
```css
|
||||
/* 字体预加载 */
|
||||
.iconfont {
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 缓存策略
|
||||
|
||||
- 字体文件使用 CDN 加载,自动缓存
|
||||
- 本地 CSS 文件确保离线可用
|
||||
- 建议定期更新字体链接时间戳
|
||||
|
||||
### 4. 回退方案
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<!-- 图标 + 文字回退 -->
|
||||
<view class="icon-with-fallback">
|
||||
<text class="iconfont icon-user"></text>
|
||||
<text class="icon-fallback">用户</text>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.icon-fallback {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* 当字体加载失败时显示文字 */
|
||||
@font-face {
|
||||
font-family: 'iconfont-fallback';
|
||||
src: local('Arial'), local('sans-serif');
|
||||
}
|
||||
|
||||
.iconfont:before {
|
||||
font-family: 'iconfont', 'iconfont-fallback';
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
## 📚 相关文件
|
||||
|
||||
- **样式文件**:`mobile/src/static/fonts/iconfont.css`
|
||||
- **演示页面**:`mobile/src/pages/iconfont-demo/index.vue`
|
||||
- **登录页面**:`mobile/src/pages/login/index.vue`
|
||||
- **配置文件**:`mobile/src/main.js`
|
||||
|
||||
## 🎯 最佳实践
|
||||
|
||||
1. **统一使用**:项目中统一使用 iconfont,避免混用多种图标方案
|
||||
2. **语义化命名**:使用有意义的类名,便于维护
|
||||
3. **适当大小**:根据使用场景选择合适的字体大小
|
||||
4. **颜色一致**:遵循设计规范,保持图标颜色的一致性
|
||||
5. **性能考虑**:避免一次性加载过多不使用的图标
|
||||
|
||||
## 📝 更新日志
|
||||
|
||||
- **2025-07-01**:初始版本,集成 iconfont 字体库
|
||||
- **预计扩展**:根据业务需求持续添加新图标
|
||||
|
||||
---
|
||||
|
||||
**维护者**:移动端开发团队
|
||||
**更新时间**:2025-07-01
|
||||
@@ -0,0 +1,280 @@
|
||||
# 移动端路由守卫使用指南
|
||||
|
||||
## 📋 概述
|
||||
|
||||
移动端路由守卫是一个全局的权限控制系统,确保用户未登录或登录失效时,访问需要权限的页面会自动跳转到登录页。
|
||||
|
||||
## 🎯 核心功能
|
||||
|
||||
### 1. 自动权限检查
|
||||
|
||||
- **页面加载时检查** → `onLoad()` 生命周期
|
||||
- **页面显示时检查** → `onShow()` 生命周期
|
||||
- **页面跳转前检查** → 使用守卫跳转方法
|
||||
- **HTTP 请求拦截** → 401 响应自动处理
|
||||
|
||||
### 2. 多层次验证
|
||||
|
||||
- ✅ 检查登录状态 `userStorage.isLoggedIn()`
|
||||
- ✅ 检查 Token 存在 `userStorage.getToken()`
|
||||
- ✅ 检查 Token 过期 `userState.checkTokenExpiration()`
|
||||
- ✅ 区分公开页面和受保护页面
|
||||
|
||||
### 3. 优雅的用户体验
|
||||
|
||||
- 🎨 友好的错误提示
|
||||
- 🎨 自动清理失效信息
|
||||
- 🎨 延迟跳转确保提示显示
|
||||
- 🎨 使用 `reLaunch` 清除页面栈
|
||||
|
||||
## 🔧 使用方法
|
||||
|
||||
### 在页面中使用
|
||||
|
||||
```javascript
|
||||
<script>
|
||||
import { checkPageAccess } from '@/utils/route-guard';
|
||||
|
||||
export default {
|
||||
onLoad() {
|
||||
// 页面加载时检查权限
|
||||
if (!checkPageAccess()) {
|
||||
return; // 权限检查失败,自动跳转登录页
|
||||
}
|
||||
|
||||
// 页面初始化逻辑
|
||||
this.initPageData();
|
||||
},
|
||||
|
||||
onShow() {
|
||||
// 页面显示时再次检查权限(防止后台切换回来时失效)
|
||||
if (!checkPageAccess()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 刷新页面数据
|
||||
this.refreshData();
|
||||
},
|
||||
|
||||
methods: {
|
||||
initPageData() {
|
||||
// 需要登录权限的初始化逻辑
|
||||
},
|
||||
|
||||
refreshData() {
|
||||
// 需要登录权限的数据刷新逻辑
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
### 带权限检查的页面跳转
|
||||
|
||||
```javascript
|
||||
<script>
|
||||
import { guardedNavigate, guardedSwitchTab } from '@/utils/route-guard';
|
||||
|
||||
export default {
|
||||
methods: {
|
||||
// 普通页面跳转
|
||||
goToProfile() {
|
||||
guardedNavigate('/pages/profile/index');
|
||||
},
|
||||
|
||||
// Tab页面切换
|
||||
switchToWorkbench() {
|
||||
guardedSwitchTab('/pages/workbench/index');
|
||||
},
|
||||
|
||||
// 带参数跳转
|
||||
goToDetail(id) {
|
||||
guardedNavigate(`/pages/detail/index?id=${id}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
### 使用页面混入(可选)
|
||||
|
||||
```javascript
|
||||
<script>
|
||||
import { routeGuardMixin } from '@/utils/route-guard';
|
||||
|
||||
export default {
|
||||
// 使用混入,自动添加权限检查
|
||||
mixins: [routeGuardMixin],
|
||||
|
||||
onLoad() {
|
||||
// 混入会自动调用权限检查
|
||||
// 这里只需要写业务逻辑
|
||||
this.loadData();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
## ⚙️ 配置说明
|
||||
|
||||
### 公开页面配置
|
||||
|
||||
在 `mobile/src/utils/route-guard.js` 中配置无需登录的页面:
|
||||
|
||||
```javascript
|
||||
// 无需登录验证的页面列表
|
||||
const PUBLIC_PAGES = [
|
||||
'/pages/login/index', // 登录页
|
||||
'/pages/index/index', // 首页(功能受限但可访问)
|
||||
];
|
||||
```
|
||||
|
||||
### 自定义登录页路径
|
||||
|
||||
```javascript
|
||||
// 登录页路径
|
||||
const LOGIN_PAGE = '/pages/login/index';
|
||||
```
|
||||
|
||||
## 🧪 测试功能
|
||||
|
||||
访问测试页面验证路由守卫功能:
|
||||
|
||||
```
|
||||
页面路径: /pages/test-route/index
|
||||
```
|
||||
|
||||
测试页面提供:
|
||||
|
||||
- 📊 实时登录状态显示
|
||||
- 🔧 测试按钮(清除 Token、跳转等)
|
||||
- 📋 详细的操作日志
|
||||
- 🎯 权限检查结果验证
|
||||
|
||||
## 🔄 工作流程
|
||||
|
||||
### 权限检查流程
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
A[页面访问] --> B{是否公开页面?}
|
||||
B -->|是| C[允许访问]
|
||||
B -->|否| D{用户已登录?}
|
||||
D -->|否| E[跳转登录页]
|
||||
D -->|是| F{Token存在?}
|
||||
F -->|否| E
|
||||
F -->|是| G{Token未过期?}
|
||||
G -->|否| E
|
||||
G -->|是| C
|
||||
```
|
||||
|
||||
### HTTP 拦截流程
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
A[API请求] --> B[发送请求]
|
||||
B --> C{响应状态}
|
||||
C -->|200| D[正常处理]
|
||||
C -->|401| E[清除登录信息]
|
||||
E --> F[显示提示]
|
||||
F --> G[跳转登录页]
|
||||
```
|
||||
|
||||
## 📝 最佳实践
|
||||
|
||||
### 1. 页面权限设计原则
|
||||
|
||||
- **最小权限原则**:只有必要的页面才需要登录
|
||||
- **用户友好原则**:首页等入口页面可以部分访问
|
||||
- **安全优先原则**:敏感操作必须验证权限
|
||||
|
||||
### 2. 错误处理建议
|
||||
|
||||
```javascript
|
||||
onLoad() {
|
||||
try {
|
||||
if (!checkPageAccess()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 业务逻辑
|
||||
await this.loadUserData();
|
||||
|
||||
} catch (error) {
|
||||
console.error('页面初始化失败:', error);
|
||||
uni.showToast({
|
||||
title: '页面加载失败',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 性能优化建议
|
||||
|
||||
- 权限检查放在业务逻辑之前
|
||||
- 避免在权限检查失败后继续执行后续逻辑
|
||||
- 合理使用页面缓存减少重复检查
|
||||
|
||||
## 🚨 注意事项
|
||||
|
||||
### 1. 公开页面处理
|
||||
|
||||
```javascript
|
||||
// ❌ 错误:在公开页面中强制检查权限
|
||||
onLoad() {
|
||||
if (!checkPageAccess()) return; // 首页不应该强制登录
|
||||
}
|
||||
|
||||
// ✅ 正确:区分功能的权限要求
|
||||
onLoad() {
|
||||
// 首页可以访问,但部分功能需要登录
|
||||
this.checkLoginForFeatures();
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 避免循环跳转
|
||||
|
||||
```javascript
|
||||
// ❌ 错误:在登录页中调用权限检查
|
||||
// pages/login/index.vue
|
||||
onLoad() {
|
||||
checkPageAccess(); // 会导致循环跳转
|
||||
}
|
||||
|
||||
// ✅ 正确:登录页无需权限检查
|
||||
onLoad() {
|
||||
// 直接执行登录页逻辑
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Token 刷新处理
|
||||
|
||||
```javascript
|
||||
// 建议在HTTP拦截器中处理Token刷新
|
||||
// 避免在页面级别处理复杂的Token逻辑
|
||||
```
|
||||
|
||||
## 🔗 相关文档
|
||||
|
||||
- [移动端用户管理工具](./移动端用户管理工具使用指南.md)
|
||||
- [移动端 API 请求规范](./移动端API请求规范.md)
|
||||
- [移动端开发规范总结](./移动端开发规范总结.md)
|
||||
|
||||
## 💡 常见问题
|
||||
|
||||
### Q: 如何添加新的公开页面?
|
||||
|
||||
A: 在 `route-guard.js` 的 `PUBLIC_PAGES` 数组中添加页面路径。
|
||||
|
||||
### Q: 权限检查失败但没有跳转到登录页?
|
||||
|
||||
A: 检查控制台日志,确认 `reLaunch` 是否执行成功,必要时检查页面路径配置。
|
||||
|
||||
### Q: 如何自定义权限检查逻辑?
|
||||
|
||||
A: 可以扩展 `checkPageAccess` 函数,添加更复杂的权限验证逻辑。
|
||||
|
||||
### Q: 在什么情况下需要使用 `routeGuardMixin`?
|
||||
|
||||
A: 当页面逻辑复杂,希望自动化权限检查时使用,但通常建议手动调用以保持代码清晰。
|
||||
Reference in new issue
Block a user