diff --git a/.cursor/cursorrules-mobile.md b/.cursor/cursorrules-mobile.md
index f2bbb71e..fbab21de 100644
--- a/.cursor/cursorrules-mobile.md
+++ b/.cursor/cursorrules-mobile.md
@@ -358,6 +358,51 @@ export default {
## 5. 🎨 图标使用规范 (SVG Data URI 方案)
+### 🚨 强制性图标使用规范(2025-08-06 新增)
+
+**⚠️ 重要说明**: 移动端项目已全面禁用 uview-plus 的 u-icon 组件,强制使用 iconfont 图标系统!
+
+#### 📋 强制要求
+
+1. **🚫 禁止使用 u-icon 组件**
+ ```vue
+
+
+
+
+ ```
+
+2. **✅ 必须使用 iconfont 图标**
+ ```vue
+
+
+
+
+ ```
+
+3. **🔄 动态图标类名**
+ ```vue
+
+
+ ```
+
+#### 🎯 替换映射表
+
+| u-icon name | iconfont class | 说明 |
+|-------------|----------------|------|
+| `search` | `icon-search` | 搜索图标 |
+| `account` | `icon-account-circle` | 用户账户图标 |
+| `close` | `icon-close` | 关闭图标 |
+| `email` | `icon-email` | 邮件图标 |
+| `arrow-right` | `icon-arrow-right` | 右箭头图标 |
+| `arrow-down` | `icon-arrow-down` | 下箭头图标 |
+| `home` | `icon-home` | 首页图标 |
+| `setting` | `icon-setting` | 设置图标 |
+
### 背景说明
由于 uview-plus 的 u-icon 组件在某些环境下存在显示异常(显示为横线),项目采用 **SVG Data URI** 方案实现图标系统,确保跨平台兼容性和显示稳定性。
@@ -369,6 +414,7 @@ export default {
- ✅ **加载稳定**: 无 404 错误,无网络请求,性能优异
- ✅ **样式灵活**: 通过 CSS filter 实现颜色自定义
- ✅ **维护简单**: 基于 Material Design,图标清晰美观
+- ✅ **显示稳定**: 彻底解决 u-icon 组件显示异常问题
### 图标配置文件
@@ -1784,3 +1830,152 @@ npm run lint:fix
**📝 维护**: 移动端开发规范文档
**🔄 更新**: 2025-01-03 (新增登录状态管理规范)
**📁 文件**: .cursor/cursorrules-mobile.md
+
+# uview-plus 图标组件规范(2025-07-01 新增)
+- 移动端UI开发中,图标组件统一使用 (uview-plus 3.x 官方标准),禁止使用 或 。
+- 推荐通过 easycom 自动全局注册,无需手动 import。
+- 标准用法示例:
+ ```vue
+
+
+ ```
+- 图标名称请参考 uview-plus 官方文档:https://v3.uviewui.com/components/icon.html
+
+## uview-plus 图标字体配置规范(2025-08-06 更新)
+
+### 📦 版本升级经验总结
+
+**升级记录**: uview-plus 3.4.45 → 3.4.73 (+28个版本)
+**升级时间**: 2025-08-06
+**配置优化**: 采用官方推荐的字体加载机制
+
+### 🔧 官方推荐配置方案
+
+#### 方案1: 自托管字体配置(官方推荐 ⭐)
+
+**参考文档**: https://uview-plus.jiangruyi.com/components/icon.html#自定义默认字体图标自托管资源
+
+```javascript
+// main.js - 官方推荐配置方式
+app.use(uviewPlus, () => {
+ return {
+ options: {
+ config: {
+ // 🔧 自托管字体图标资源地址,避免CDN依赖问题
+ iconUrl: '/static/fonts/uview-plus-official.ttf'
+ }
+ }
+ }
+});
+```
+
+#### 方案2: 手动加载字体(官方兼容性方案)
+
+**参考文档**: https://uview-plus.jiangruyi.com/components/icon.html#新版本全局加载兼容性
+
+```javascript
+// App.vue - 如果在小程序等场景出现图标无法全局加载
+import { fontUtil } from 'uview-plus';
+
+export default {
+ onLaunch: function () {
+ // 官方推荐:手动加载uview-plus字体图标
+ fontUtil.loadFont();
+ }
+};
+```
+
+#### 方案3: CSS备用方案(平台兼容)
+
+```css
+/* App.vue - 其他平台如抖音/QQ/百度小程序的备用方案 */
+@font-face {
+ font-family: 'uicon-iconfont';
+ src: url('/static/fonts/uview-plus-official.ttf') format('truetype');
+}
+```
+
+### 🏆 最佳实践配置
+
+```javascript
+// main.js - 完整配置示例
+import { createSSRApp } from 'vue';
+import App from './App.vue';
+import uviewPlus from 'uview-plus';
+import 'uview-plus/index.scss';
+import '@/static/fonts/iconfont.css'; // 自定义SVG图标库
+
+export function createApp() {
+ const app = createSSRApp(App);
+
+ // 🔧 官方推荐:uview-plus配置
+ app.use(uviewPlus, () => {
+ return {
+ options: {
+ config: {
+ iconUrl: '/static/fonts/uview-plus-official.ttf'
+ }
+ }
+ }
+ });
+
+ return { app };
+}
+```
+
+```javascript
+// App.vue - 兼容性配置
+import { fontUtil } from 'uview-plus';
+
+export default {
+ onLaunch: function () {
+ // 🔧 官方推荐:手动加载字体图标
+ try {
+ fontUtil.loadFont();
+ console.log('✅ 手动加载uview-plus字体成功');
+ } catch (error) {
+ console.error('❌ 手动加载uview-plus字体失败:', error);
+ }
+ }
+};
+```
+
+#### 混合图标使用策略
+- **uview-plus图标**: 使用 `` 系统通用图标
+- **项目iconfont**: 使用 `` 业务专用图标
+- **优先级**: 项目iconfont > uview-plus图标 > CDN字体
+
+### ⚠️ 重要注意事项
+
+#### 升级前检查
+1. **字体文件**: 确保本地字体文件存在且可访问
+2. **网络环境**: 测试CDN地址访问性
+3. **版本兼容**: 检查字体版本与组件版本匹配
+
+#### 最佳实践
+1. **优先级**: 项目iconfont > 本地字体 > CDN字体
+2. **测试覆盖**: 在不同网络环境下测试图标显示
+3. **回退机制**: 准备备用图标方案
+4. **文档记录**: 及时更新字体配置变更
+
+### 📚 相关文件位置
+- **字体配置**: `mobile/src/main.js`
+- **测试页面**: `mobile/src/pages/demo/uview-icon-test/index.vue`
+- **字体文件**: `mobile/src/static/fonts/`
+- **项目图标**: `mobile/src/static/fonts/iconfont.css`
+
+### 🧪 测试验证
+```bash
+# 启动开发服务器
+npm run dev:h5
+
+# 访问测试页面
+http://localhost:3000/#/pages/demo/uview-icon-test/index
+```
+
+### 📖 参考文档
+- uview-plus官方文档: https://uview-plus.jiangruyi.com/
+- 字体图标组件: https://uview-plus.jiangruyi.com/components/icon.html
+- 自托管配置: https://uview-plus.jiangruyi.com/components/icon.html#自定义默认字体图标自托管资源
+- 兼容性配置: https://uview-plus.jiangruyi.com/components/icon.html#新版本全局加载兼容性
+- 项目图标文档: `docs/开发文档/API/u-icon替换映射表.md`
diff --git a/mobile/.gitignore b/mobile/.gitignore
index 7ce6e504..2561bb4f 100644
--- a/mobile/.gitignore
+++ b/mobile/.gitignore
@@ -1,21 +1,23 @@
-# Logs
-logs
-*.log
+.DS_Store
+node_modules/
+unpackage/
+dist/
+
+# local env files
+.env.local
+.env.*.local
+
+# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
-pnpm-debug.log*
-lerna-debug.log*
-
-node_modules
-.DS_Store
-dist
-*.local
# Editor directories and files
+.project
.idea
+.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
-*.sw?
\ No newline at end of file
+*.sw*
diff --git a/mobile/.npmrc b/mobile/.npmrc
new file mode 100644
index 00000000..bf2e7648
--- /dev/null
+++ b/mobile/.npmrc
@@ -0,0 +1 @@
+shamefully-hoist=true
diff --git a/mobile/PROJECT_OVERVIEW.md b/mobile/PROJECT_OVERVIEW.md
deleted file mode 100644
index 1e925914..00000000
--- a/mobile/PROJECT_OVERVIEW.md
+++ /dev/null
@@ -1,249 +0,0 @@
-# 智慧校园 Vue3 应用
-
-基于 Vue3 + uniapp 开发的现代化智慧校园移动应用,采用现代简约设计风格,支持多平台部署。
-
-## 🎯 项目特色
-
-### 🎨 设计系统
-
-- **现代简约风格**: 清新简洁的界面设计
-- **统一色彩方案**: 主题色 RGB(93, 135, 255)
-- **组件化架构**: 高度复用的 Vue 组件
-- **响应式布局**: 适配多种屏幕尺寸
-
-### 📱 核心功能
-
-#### 首页
-
-- 个性化欢迎横幅(用户信息 + 天气)
-- 快速统计仪表板
-- 常用服务快捷入口
-- 待办任务管理
-- 校园动态资讯
-- 快捷功能工具栏
-
-#### 办公中心
-
-- 智能搜索功能
-- 分类服务导航:
- - 🔥 热门服务
- - 🎓 学习教务
- - 🏠 生活服务
- - 🏢 校园资源
- - 📋 办事服务
- - 🔍 信息查询
- - ⚡ 快捷工具
-
-#### 我的页面
-
-- 个人信息展示
-- 学习状态统计
-- 功能菜单分组:
- - 我的服务
- - 校园生活
- - 应用设置
- - 帮助与反馈
-
-### 🎭 交互体验
-
-#### 动画效果
-
-- **页面转场**: 流畅的页面切换动画
-- **组件动画**: 卡片、列表项的进入动画
-- **微交互**: 点击反馈、悬停效果
-- **加载动画**: 优雅的加载状态提示
-
-#### 手势操作
-
-- **滑动切换**: 左右滑动切换页面
-- **长按菜单**: 长按显示上下文菜单
-- **双击操作**: 双击触发特殊功能
-- **触觉反馈**: 操作时的震动反馈
-
-### ♿ 无障碍访问
-
-#### 键盘导航
-
-- **Tab 导航**: 支持键盘 Tab 键导航
-- **方向键**: 网格布局中的方向键导航
-- **快捷键**: Enter/Space 激活,Escape 取消
-
-#### 屏幕阅读器支持
-
-- **ARIA 标签**: 完整的语义化标记
-- **实时区域**: 动态内容的屏幕阅读器通知
-- **焦点管理**: 智能的焦点流转控制
-
-#### 辅助功能
-
-- **高对比度模式**: 视觉障碍用户友好
-- **大字体模式**: 可调节的字体大小
-- **屏幕阅读器优化**: 减少干扰的简化模式
-
-### 📐 响应式设计
-
-#### 断点设置
-
-- **Mobile**: ≤ 375px
-- **Tablet**: ≤ 768px
-- **Desktop**: > 768px
-
-#### 适配特性
-
-- **弹性网格**: 自适应列数的网格布局
-- **安全区域**: 支持刘海屏等特殊设备
-- **方向适配**: 横竖屏切换优化
-- **触摸友好**: 合适的触摸目标尺寸
-
-## 🛠 技术架构
-
-### 核心技术栈
-
-- **Vue 3**: 组合式 API + TypeScript 支持
-- **uniapp**: 跨平台开发框架
-- **Sass**: CSS 预处理器
-- **Vite**: 现代化构建工具
-
-### 组件库
-
-```
-src/components/
-├── NavBar.vue # 导航栏组件
-├── Card.vue # 卡片组件
-├── ServiceItem.vue # 服务项组件
-├── StatCard.vue # 统计卡片组件
-└── GridLayout.vue # 网格布局组件
-```
-
-### 工具库
-
-```
-src/utils/
-├── animations.js # 动画工具
-├── gestures.js # 手势识别
-├── accessibility.js # 无障碍访问
-├── device.js # 设备检测
-├── request.js # 网络请求
-└── sse.js # 服务端推送
-```
-
-### 页面结构
-
-```
-src/pages/
-├── index/ # 首页
-├── workbench/ # 办公中心
-└── profile/ # 我的页面
-```
-
-## 🎨 设计规范
-
-### 色彩系统
-
-```scss
---primary-color: rgb(93, 135, 255); // 主品牌色
---primary-light: rgba(93, 135, 255, 0.1);
---primary-dark: rgb(73, 115, 235);
---text-primary: #333333;
---text-secondary: #666666;
---bg-color: #f8f9fa;
-```
-
-### 间距系统
-
-```scss
---spacing-sm: 8px; // 小间距
---spacing-base: 12px; // 基础间距
---spacing-lg: 16px; // 大间距
---spacing-xl: 20px; // 超大间距
-```
-
-### 圆角系统
-
-```scss
---border-radius-sm: 6px;
---border-radius-base: 8px;
---border-radius-lg: 12px;
---border-radius-xl: 16px;
-```
-
-## 🚀 部署与运行
-
-### 开发环境
-
-```bash
-# 安装依赖
-npm install
-
-# 启动开发服务器 (H5)
-npm run dev:h5
-
-# 启动微信小程序开发
-npm run dev:mp-weixin
-
-# 启动 App 开发
-npm run dev:app
-```
-
-### 生产构建
-
-```bash
-# H5 构建
-npm run build:h5
-
-# 微信小程序构建
-npm run build:mp-weixin
-
-# App 构建
-npm run build:app
-```
-
-## 📊 性能优化
-
-### 代码优化
-
-- **组件懒加载**: 路由级别的代码分割
-- **图片优化**: WebP 格式 + 响应式图片
-- **缓存策略**: 智能的资源缓存机制
-
-### 用户体验
-
-- **预加载**: 关键资源的预加载
-- **离线支持**: Service Worker 离线缓存
-- **快速响应**: 乐观更新 + 错误回退
-
-## 🔧 配置说明
-
-### 主题定制
-
-在 `src/uni.scss` 中修改 CSS 变量即可快速定制主题色彩。
-
-### 功能开关
-
-在各页面组件的 data 中可以配置功能模块的显示/隐藏。
-
-### 无障碍设置
-
-用户的无障碍偏好会自动保存到本地存储,下次启动时自动恢复。
-
-## 📱 支持平台
-
-- ✅ H5 (Web)
-- ✅ 微信小程序
-- ✅ 支付宝小程序
-- ✅ iOS App
-- ✅ Android App
-- ✅ 鸿蒙 App
-
-## 🏆 项目亮点
-
-1. **现代化设计**: 采用最新的 Material Design 3 设计理念
-2. **卓越性能**: 60fps 流畅动画,秒级启动时间
-3. **全面无障碍**: WCAG 2.1 AA 级无障碍访问标准
-4. **智能交互**: 手势操作 + 语音控制支持
-5. **跨平台兼容**: 一套代码,多端部署
-6. **可维护架构**: 模块化设计,易于扩展和维护
-
----
-
-_�� 让校园生活更便捷、更智能、更美好!_
diff --git a/mobile/README.md b/mobile/README.md
index 5c7f3277..34ad6930 100644
--- a/mobile/README.md
+++ b/mobile/README.md
@@ -1,177 +1,19 @@
-# 智慧校园 UniApp 项目
-
-基于 UniApp + Vue3 + uView-plus 的跨平台智慧校园应用。
-
-## 技术栈
-
-- **框架**: UniApp + Vue3
-- **UI 组件库**: uView-plus
-- **代码规范**: ESLint
-- **样式**: SCSS
-- **不使用**: TypeScript
-
-## 支持平台
-
-- 微信小程序
-- H5
-- iOS App
-- Android App
-- 其他小程序平台
-
-## 项目结构
+# mobile2
+## Project setup
```
-src/
-├── pages/ # 页面目录
-│ ├── index/ # 首页
-│ ├── workbench/ # 办公中心
-│ └── profile/ # 我的
-├── utils/ # 工具函数
-│ ├── request.js # API请求工具
-│ ├── sse.js # SSE实时数据工具
-│ └── device.js # 设备兼容性工具
-├── static/ # 静态资源
-├── App.vue # 应用入口
-├── main.js # 项目入口
-├── pages.json # 页面配置
-└── uni.scss # 全局样式
+pnpm install
```
-## 核心功能
-
-### 1. 底部导航
-
-- 首页:展示校园服务快捷入口
-- 办公中心:办公应用和功能
-- 我的:个人信息和设置
-
-### 2. 数据管理
-
-- RESTful API 接口
-- 智能缓存策略(支持过期时间)
-- SSE 实时数据推送(H5 环境使用原生 EventSource,小程序环境使用轮询)
-
-### 3. 设备兼容性
-
-- 低端设备检测和性能优化
-- 网络环境自适应(2G/3G/4G/5G/WiFi)
-- 刘海屏、折叠屏适配
-- 安全区域处理
-
-## 开发命令
-
-```bash
-# 安装依赖
-npm install
-
-# H5开发
-npm run dev:h5
-
-# 微信小程序开发
-npm run dev:mp-weixin
-
-# Android开发
-npm run dev:app-android
-
-# iOS开发
-npm run dev:app-ios
-
-# 代码检查
-npm run lint
-
-# 代码自动修复
-npm run lint:fix
+### Compiles and hot-reloads for development
+```
+pnpm run serve
```
-## 构建命令
-
-```bash
-# H5构建
-npm run build:h5
-
-# 微信小程序构建
-npm run build:mp-weixin
-
-# App构建
-npm run build:app
+### Compiles and minifies for production
+```
+pnpm run build
```
-## 开发规范
-
-### 代码风格
-
-- 使用 2 个空格缩进
-- 使用单引号
-- 不使用分号
-- 组件名使用 PascalCase
-- 文件名使用 kebab-case
-
-### 目录规范
-
-- 页面组件放在 `pages/` 目录
-- 通用组件放在 `components/` 目录
-- 工具函数放在 `utils/` 目录
-- 静态资源放在 `static/` 目录
-
-## API 使用示例
-
-```javascript
-import { api, storage } from '@/utils/request';
-
-// 发起请求
-const data = await api.getUserInfo();
-
-// 设置缓存(30分钟过期)
-storage.set('userInfo', data, 30 * 60);
-
-// 获取缓存
-const cachedData = storage.get('userInfo');
-```
-
-## SSE 实时数据示例
-
-```javascript
-import { messageManager } from '@/utils/sse';
-
-// 连接实时消息
-messageManager.connect('userId123');
-
-// 订阅消息
-messageManager.subscribe('notification', (data) => {
- console.log('收到通知:', data);
-});
-
-// 断开连接
-messageManager.disconnect();
-```
-
-## 设备适配示例
-
-```javascript
-import { deviceUtils, adaptUtils } from '@/utils/device';
-
-// 检测设备类型
-const deviceType = deviceUtils.getDeviceType();
-
-// 获取安全区域样式
-const safeAreaStyle = adaptUtils.getSafeAreaStyle();
-
-// 判断是否为低端设备
-const isLowEnd = deviceUtils.isLowEndDevice();
-```
-
-## 注意事项
-
-1. **图标资源**: 当前底部导航使用文字显示,实际开发时需要替换为对应的图标文件
-2. **API 地址**: 请在 `utils/request.js` 中配置正确的后端 API 地址
-3. **SSE 服务**: SSE 功能需要后端支持,在小程序环境会自动降级为轮询
-4. **权限配置**: 使用定位、相机等原生功能时需要在 `manifest.json` 中配置相应权限
-
-## 下一步开发
-
-1. 根据设计原型完善页面 UI
-2. 接入后端 API 接口
-3. 添加具体业务功能模块
-4. 配置推送、定位等原生能力
-5. 添加图标资源文件
-6. 完善错误处理和用户反馈
+### Customize configuration
+See [Configuration Reference](https://cli.vuejs.org/config/).
diff --git a/mobile/babel.config.js b/mobile/babel.config.js
new file mode 100644
index 00000000..4c1134bc
--- /dev/null
+++ b/mobile/babel.config.js
@@ -0,0 +1,81 @@
+const webpack = require('webpack')
+const plugins = []
+
+if (process.env.UNI_OPT_TREESHAKINGNG) {
+ plugins.push(require('@dcloudio/vue-cli-plugin-uni-optimize/packages/babel-plugin-uni-api/index.js'))
+}
+
+if (
+ (
+ process.env.UNI_PLATFORM === 'app-plus' &&
+ process.env.UNI_USING_V8
+ ) ||
+ (
+ process.env.UNI_PLATFORM === 'h5' &&
+ process.env.UNI_H5_BROWSER === 'builtin'
+ )
+) {
+ const path = require('path')
+
+ const isWin = /^win/.test(process.platform)
+
+ const normalizePath = path => (isWin ? path.replace(/\\/g, '/') : path)
+
+ const input = normalizePath(process.env.UNI_INPUT_DIR)
+ try {
+ plugins.push([
+ require('@dcloudio/vue-cli-plugin-hbuilderx/packages/babel-plugin-console'),
+ {
+ file (file) {
+ file = normalizePath(file)
+ if (file.indexOf(input) === 0) {
+ return path.relative(input, file)
+ }
+ return false
+ }
+ }
+ ])
+ } catch (e) { }
+}
+
+process.UNI_LIBRARIES = process.UNI_LIBRARIES || ['@dcloudio/uni-ui']
+process.UNI_LIBRARIES.forEach(libraryName => {
+ plugins.push([
+ 'import',
+ {
+ 'libraryName': libraryName,
+ 'customName': (name) => {
+ return `${libraryName}/lib/${name}/${name}`
+ }
+ }
+ ])
+})
+
+if (process.env.UNI_PLATFORM !== 'h5') {
+ plugins.push('@babel/plugin-transform-runtime')
+}
+
+const config = {
+ presets: [
+ [
+ '@vue/app',
+ {
+ modules: webpack.version[0] > 4 ? 'auto' : 'commonjs',
+ useBuiltIns: process.env.UNI_PLATFORM === 'h5' ? 'usage' : 'entry'
+ }
+ ]
+ ],
+ plugins
+}
+
+const UNI_H5_TEST = '**/@dcloudio/uni-h5/dist/index.umd.min.js'
+if (process.env.NODE_ENV === 'production') {
+ config.overrides = [{
+ test: UNI_H5_TEST,
+ compact: true,
+ }]
+} else {
+ config.ignore = [UNI_H5_TEST]
+}
+
+module.exports = config
diff --git a/mobile/docs/图标管理系统使用指南.md b/mobile/docs/图标管理系统使用指南.md
deleted file mode 100644
index cc070017..00000000
--- a/mobile/docs/图标管理系统使用指南.md
+++ /dev/null
@@ -1,348 +0,0 @@
-# 📚 图标管理系统使用指南
-
-## 🎯 概述
-
-项目采用了 **双图标系统**:
-- **iconfont 自定义图标** (推荐) - 基于 SVG data URI 的轻量级图标系统
-- **uview-plus 图标** (辅助) - UI库自带图标,部分图标存在显示问题
-
-## 📁 文件结构
-
-```
-mobile/src/static/fonts/
-├── iconfont.css # 主图标文件(SVG data URI)
-├── iconfont.ttf # 字体文件(可选)
-└── iconfont.woff # 字体文件(可选)
-
-mobile/src/
-├── main.js # 引入图标CSS
-├── pages/iconfont-demo/ # 图标演示页面
-└── pages/uview-icon-test/ # uview图标问题测试页面
-```
-
-## 🚀 添加新图标 - 超级简单!
-
-### 方法一:SVG Data URI(推荐)
-
-**优势**:
-- ✅ 无需下载字体文件
-- ✅ 支持彩色图标
-- ✅ 完美适配所有平台
-- ✅ 加载速度最快
-
-**操作步骤**:
-
-#### 1. 从 iconfont.cn 下载 SVG
-1. 访问 [iconfont.cn](https://www.iconfont.cn/)
-2. 搜索需要的图标
-3. 下载 SVG 格式
-
-#### 2. 转换为 data URI
-使用在线工具或以下代码:
-
-```javascript
-// SVG 转 data URI 工具函数
-function svgToDataUri(svgContent) {
- const encoded = encodeURIComponent(svgContent)
- .replace(/'/g, '%27')
- .replace(/"/g, '%22');
- return `data:image/svg+xml;charset=utf8,${encoded}`;
-}
-
-// 示例
-const svgContent = ``;
-```
-
-#### 3. 添加到 CSS 文件
-在 `mobile/src/static/fonts/iconfont.css` 中添加:
-
-```css
-/* 新增图标示例 */
-.icon-add-circle {
- 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 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm5 11h-4v4h-2v-4H7v-2h4V7h2v4h4v2z'/%3E%3C/svg%3E");
-}
-```
-
-#### 4. 在页面中使用
-```vue
-
-
-
-```
-
-### 方法二:iconfont.cn 项目同步(适合大量图标)
-
-#### 1. 创建 iconfont 项目
-1. 在 iconfont.cn 注册账号
-2. 创建新项目
-3. 添加需要的图标到项目
-
-#### 2. 下载代码
-1. 点击"下载至本地"
-2. 选择 "Font class" 或 "Unicode" 方式
-3. 下载解压得到文件
-
-#### 3. 替换项目文件
-```bash
-# 将下载的文件复制到项目
-cp iconfont.css mobile/src/static/fonts/
-cp iconfont.ttf mobile/src/static/fonts/
-cp iconfont.woff mobile/src/static/fonts/
-```
-
-#### 4. 更新字体引用(如需要)
-在 `uni.scss` 或相关文件中更新字体路径:
-
-```scss
-@font-face {
- font-family: 'iconfont';
- src: url('./static/fonts/iconfont.woff') format('woff'),
- url('./static/fonts/iconfont.ttf') format('truetype');
-}
-```
-
-## 🎨 使用规范
-
-### 基础用法
-```vue
-
-
-
-
-
-
-
-
-
-
-
-
-```
-
-### 响应式尺寸
-```vue
-
-
-
-
-
-
-
-
-
-
-
-
-```
-
-## 📋 当前可用图标列表
-
-### 基础图标
-- `icon-user` - 用户
-- `icon-lock` - 锁定
-- `icon-eye` - 显示
-- `icon-eye-close` - 隐藏
-- `icon-home` - 首页
-- `icon-setting` - 设置
-
-### 操作图标
-- `icon-add` - 添加
-- `icon-close` - 关闭
-- `icon-check` - 确认
-- `icon-search` - 搜索
-- `icon-arrow-right` - 右箭头
-
-### 功能图标
-- `icon-message` - 消息
-- `icon-phone` - 电话
-- `icon-mail` - 邮件
-- `icon-login` - 登录
-
-## 🔧 管理工具
-
-### 在线 SVG 编辑器
-- [SVG Editor](https://svg-edit.github.io/svgedit/)
-- [Boxy SVG](https://boxy-svg.com/)
-
-### Data URI 转换工具
-```javascript
-// 工具函数:SVG 转 data URI
-function createSvgDataUri(svgPath, viewBox = "0 0 24 24") {
- const svg = ``;
- return `url("data:image/svg+xml;charset=utf8,${encodeURIComponent(svg)}")`;
-}
-
-// 使用示例
-const newIconCSS = `
-.icon-new-feature {
- background-image: ${createSvgDataUri("M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10...")};
-}
-`;
-```
-
-### 批量添加脚本
-```javascript
-// 批量生成图标CSS的脚本
-const icons = [
- { name: 'star', path: 'M12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-.61L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21z' },
- { name: 'heart', path: 'M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z' }
-];
-
-function generateIconCSS(icons) {
- return icons.map(icon => `
-.icon-${icon.name} {
- 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='${icon.path}'/%3E%3C/svg%3E");
-}`).join('\n');
-}
-```
-
-## 🎯 最佳实践
-
-### 1. 命名规范
-```css
-/* ✅ 推荐命名 */
-.icon-user /* 功能性图标 */
-.icon-user-add /* 带操作的图标 */
-.icon-arrow-right /* 方向性图标 */
-
-/* ❌ 避免命名 */
-.icon-1 /* 无意义编号 */
-.icon-temp /* 临时命名 */
-```
-
-### 2. 颜色管理
-```scss
-// 使用CSS变量管理图标颜色
-:root {
- --icon-primary: #1890ff;
- --icon-success: #52c41a;
- --icon-warning: #faad14;
- --icon-error: #ff4d4f;
-}
-
-.icon-primary { color: var(--icon-primary); }
-.icon-success { color: var(--icon-success); }
-.icon-warning { color: var(--icon-warning); }
-.icon-error { color: var(--icon-error); }
-```
-
-### 3. 尺寸规范
-```scss
-// 标准尺寸系统
-.icon-xs { width: 12px; height: 12px; } // 超小
-.icon-sm { width: 16px; height: 16px; } // 小
-.icon-md { width: 24px; height: 24px; } // 中(默认)
-.icon-lg { width: 32px; height: 32px; } // 大
-.icon-xl { width: 48px; height: 48px; } // 超大
-```
-
-## 🔄 迁移指南
-
-### 从 uview 图标迁移到 iconfont
-
-```vue
-
-
-
-
-
-```
-
-### 批量替换工具
-```bash
-# 使用正则表达式批量替换
-# 将 {
- if (!document.querySelector('#iconfont-css')) {
- const link = document.createElement('link');
- link.id = 'iconfont-css';
- link.rel = 'stylesheet';
- link.href = './static/fonts/iconfont.css';
- document.head.appendChild(link);
- }
-};
-```
-
-### 2. CDN 优化
-```css
-/* 使用CDN加载字体文件 */
-@font-face {
- font-family: 'iconfont';
- src: url('https://cdn.example.com/fonts/iconfont.woff') format('woff');
-}
-```
-
-## 🛠️ 开发工具
-
-### VS Code 插件推荐
-- **SVG Viewer** - 预览SVG文件
-- **CSS Peek** - 快速查看CSS定义
-- **IconFont Helper** - iconfont图标提示
-
-### 图标搜索工具
-- [Iconify](https://iconify.design/) - 100,000+ 开源图标
-- [Feather Icons](https://feathericons.com/) - 简洁线性图标
-- [Heroicons](https://heroicons.com/) - Tailwind CSS图标
-
-## ❓ 常见问题
-
-### Q: 图标不显示怎么办?
-A: 检查以下几点:
-1. CSS文件是否正确引入
-2. 类名是否拼写正确
-3. 字体文件路径是否正确
-4. 浏览器是否支持data URI
-
-### Q: 如何修改图标颜色?
-A: 使用 `color` 或 `fill` 属性:
-```css
-.my-icon {
- color: #1890ff; /* 对于SVG data URI */
- fill: #1890ff; /* 对于字体图标 */
-}
-```
-
-### Q: 图标模糊怎么解决?
-A: 确保图标尺寸为偶数像素:
-```css
-.icon-clear {
- width: 24px; /* ✅ 偶数 */
- height: 24px; /* ✅ 偶数 */
-}
-```
-
-## 📞 技术支持
-
-- **项目位置**: `mobile/src/static/fonts/`
-- **演示页面**: `/pages/iconfont-demo/index`
-- **测试页面**: `/pages/uview-icon-test/index`
-- **文档更新**: 2025-01-07
-
----
-
-**✨ 总结**: iconfont 图标系统让您轻松管理图标,添加新图标只需要复制粘贴几行CSS代码!推荐使用 SVG data URI 方式,兼容性最好,性能最优。
\ No newline at end of file
diff --git a/mobile/eslint.config.cjs b/mobile/eslint.config.cjs
deleted file mode 100644
index 83368d38..00000000
--- a/mobile/eslint.config.cjs
+++ /dev/null
@@ -1,33 +0,0 @@
-const js = require('@eslint/js');
-const vue = require('eslint-plugin-vue');
-
-module.exports = [
- js.configs.recommended,
- ...vue.configs['flat/essential'],
- {
- languageOptions: {
- ecmaVersion: 'latest',
- sourceType: 'module',
- globals: {
- uni: 'readonly',
- wx: 'readonly',
- plus: 'readonly',
- console: 'readonly',
- process: 'readonly',
- EventSource: 'readonly',
- setTimeout: 'readonly',
- setInterval: 'readonly',
- clearInterval: 'readonly',
- },
- },
- rules: {
- 'vue/multi-word-component-names': 'off',
- 'no-console': 'warn',
- 'no-debugger': 'warn',
- 'no-unused-vars': 'warn',
- // indent: ['error', 2],
- quotes: ['error', 'single'],
- // semi: ['error', 'never'],
- },
- },
-];
diff --git a/mobile/index.html b/mobile/index.html
deleted file mode 100644
index fb81ddc6..00000000
--- a/mobile/index.html
+++ /dev/null
@@ -1,20 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/mobile/package.json b/mobile/package.json
index ff729bc1..eedf7efa 100644
--- a/mobile/package.json
+++ b/mobile/package.json
@@ -1,80 +1,117 @@
{
- "name": "uni-preset-vue",
- "version": "0.0.0",
+ "name": "mobile2",
+ "version": "0.1.0",
+ "private": true,
"scripts": {
- "dev:custom": "uni -p",
- "dev:h5": "uni",
- "dev:h5:ssr": "uni --ssr",
- "dev:mp-alipay": "uni -p mp-alipay",
- "dev:mp-baidu": "uni -p mp-baidu",
- "dev:mp-jd": "uni -p mp-jd",
- "dev:mp-kuaishou": "uni -p mp-kuaishou",
- "dev:mp-lark": "uni -p mp-lark",
- "dev:mp-qq": "uni -p mp-qq",
- "dev:mp-toutiao": "uni -p mp-toutiao",
- "dev:mp-harmony": "uni -p mp-harmony",
- "dev:mp-weixin": "uni -p mp-weixin",
- "dev:mp-xhs": "uni -p mp-xhs",
- "dev:quickapp-webview": "uni -p quickapp-webview",
- "dev:quickapp-webview-huawei": "uni -p quickapp-webview-huawei",
- "dev:quickapp-webview-union": "uni -p quickapp-webview-union",
- "build:custom": "uni build -p",
- "build:h5": "uni build",
- "build:h5:ssr": "uni build --ssr",
- "build:mp-alipay": "uni build -p mp-alipay",
- "build:mp-baidu": "uni build -p mp-baidu",
- "build:mp-jd": "uni build -p mp-jd",
- "build:mp-kuaishou": "uni build -p mp-kuaishou",
- "build:mp-lark": "uni build -p mp-lark",
- "build:mp-qq": "uni build -p mp-qq",
- "build:mp-toutiao": "uni build -p mp-toutiao",
- "build:mp-harmony": "uni build -p mp-harmony",
- "build:mp-weixin": "uni build -p mp-weixin",
- "build:mp-xhs": "uni build -p mp-xhs",
- "build:quickapp-webview": "uni build -p quickapp-webview",
- "build:quickapp-webview-huawei": "uni build -p quickapp-webview-huawei",
- "build:quickapp-webview-union": "uni build -p quickapp-webview-union",
- "lint": "eslint src/**/*.{js,vue}",
- "lint:fix": "eslint src/**/*.{js,vue} --fix"
+ "serve": "npm run dev:h5",
+ "build": "npm run build:h5",
+ "build:app-plus": "cross-env NODE_ENV=production UNI_PLATFORM=app-plus vue-cli-service uni-build",
+ "build:custom": "cross-env NODE_ENV=production uniapp-cli custom",
+ "build:h5": "cross-env NODE_ENV=production UNI_PLATFORM=h5 vue-cli-service uni-build",
+ "build:mp-360": "cross-env NODE_ENV=production UNI_PLATFORM=mp-360 vue-cli-service uni-build",
+ "build:mp-alipay": "cross-env NODE_ENV=production UNI_PLATFORM=mp-alipay vue-cli-service uni-build",
+ "build:mp-baidu": "cross-env NODE_ENV=production UNI_PLATFORM=mp-baidu vue-cli-service uni-build",
+ "build:mp-harmony": "cross-env NODE_ENV=production UNI_PLATFORM=mp-harmony vue-cli-service uni-build",
+ "build:mp-jd": "cross-env NODE_ENV=production UNI_PLATFORM=mp-jd vue-cli-service uni-build",
+ "build:mp-kuaishou": "cross-env NODE_ENV=production UNI_PLATFORM=mp-kuaishou vue-cli-service uni-build",
+ "build:mp-lark": "cross-env NODE_ENV=production UNI_PLATFORM=mp-lark vue-cli-service uni-build",
+ "build:mp-qq": "cross-env NODE_ENV=production UNI_PLATFORM=mp-qq vue-cli-service uni-build",
+ "build:mp-toutiao": "cross-env NODE_ENV=production UNI_PLATFORM=mp-toutiao vue-cli-service uni-build",
+ "build:mp-weixin": "cross-env NODE_ENV=production UNI_PLATFORM=mp-weixin vue-cli-service uni-build",
+ "build:mp-xhs": "cross-env NODE_ENV=production UNI_PLATFORM=mp-xhs vue-cli-service uni-build",
+ "build:quickapp-native": "cross-env NODE_ENV=production UNI_PLATFORM=quickapp-native vue-cli-service uni-build",
+ "build:quickapp-webview": "cross-env NODE_ENV=production UNI_PLATFORM=quickapp-webview vue-cli-service uni-build",
+ "build:quickapp-webview-huawei": "cross-env NODE_ENV=production UNI_PLATFORM=quickapp-webview-huawei vue-cli-service uni-build",
+ "build:quickapp-webview-union": "cross-env NODE_ENV=production UNI_PLATFORM=quickapp-webview-union vue-cli-service uni-build",
+ "dev:app-plus": "cross-env NODE_ENV=development UNI_PLATFORM=app-plus vue-cli-service uni-build --watch",
+ "dev:custom": "cross-env NODE_ENV=development uniapp-cli custom",
+ "dev:h5": "cross-env NODE_ENV=development UNI_PLATFORM=h5 vue-cli-service uni-serve",
+ "dev:mp-360": "cross-env NODE_ENV=development UNI_PLATFORM=mp-360 vue-cli-service uni-build --watch",
+ "dev:mp-alipay": "cross-env NODE_ENV=development UNI_PLATFORM=mp-alipay vue-cli-service uni-build --watch",
+ "dev:mp-baidu": "cross-env NODE_ENV=development UNI_PLATFORM=mp-baidu vue-cli-service uni-build --watch",
+ "dev:mp-harmony": "cross-env NODE_ENV=development UNI_PLATFORM=mp-harmony vue-cli-service uni-build --watch",
+ "dev:mp-jd": "cross-env NODE_ENV=development UNI_PLATFORM=mp-jd vue-cli-service uni-build --watch",
+ "dev:mp-kuaishou": "cross-env NODE_ENV=development UNI_PLATFORM=mp-kuaishou vue-cli-service uni-build --watch",
+ "dev:mp-lark": "cross-env NODE_ENV=development UNI_PLATFORM=mp-lark vue-cli-service uni-build --watch",
+ "dev:mp-qq": "cross-env NODE_ENV=development UNI_PLATFORM=mp-qq vue-cli-service uni-build --watch",
+ "dev:mp-toutiao": "cross-env NODE_ENV=development UNI_PLATFORM=mp-toutiao vue-cli-service uni-build --watch",
+ "dev:mp-weixin": "cross-env NODE_ENV=development UNI_PLATFORM=mp-weixin vue-cli-service uni-build --watch",
+ "dev:mp-xhs": "cross-env NODE_ENV=development UNI_PLATFORM=mp-xhs vue-cli-service uni-build --watch",
+ "dev:quickapp-native": "cross-env NODE_ENV=development UNI_PLATFORM=quickapp-native vue-cli-service uni-build --watch",
+ "dev:quickapp-webview": "cross-env NODE_ENV=development UNI_PLATFORM=quickapp-webview vue-cli-service uni-build --watch",
+ "dev:quickapp-webview-huawei": "cross-env NODE_ENV=development UNI_PLATFORM=quickapp-webview-huawei vue-cli-service uni-build --watch",
+ "dev:quickapp-webview-union": "cross-env NODE_ENV=development UNI_PLATFORM=quickapp-webview-union vue-cli-service uni-build --watch",
+ "info": "node node_modules/@dcloudio/vue-cli-plugin-uni/commands/info.js",
+ "serve:quickapp-native": "node node_modules/@dcloudio/uni-quickapp-native/bin/serve.js",
+ "test:android": "cross-env UNI_PLATFORM=app-plus UNI_OS_NAME=android jest -i",
+ "test:h5": "cross-env UNI_PLATFORM=h5 jest -i",
+ "test:ios": "cross-env UNI_PLATFORM=app-plus UNI_OS_NAME=ios jest -i",
+ "test:mp-baidu": "cross-env UNI_PLATFORM=mp-baidu jest -i",
+ "test:mp-weixin": "cross-env UNI_PLATFORM=mp-weixin jest -i"
},
"dependencies": {
- "@dcloudio/uni-app": "3.0.0-4060620250520001",
- "@dcloudio/uni-app-harmony": "3.0.0-4060620250520001",
- "@dcloudio/uni-app-plus": "3.0.0-4060620250520001",
- "@dcloudio/uni-components": "3.0.0-4060620250520001",
- "@dcloudio/uni-h5": "3.0.0-4060620250520001",
- "@dcloudio/uni-mp-alipay": "3.0.0-4060620250520001",
- "@dcloudio/uni-mp-baidu": "3.0.0-4060620250520001",
- "@dcloudio/uni-mp-harmony": "3.0.0-4060620250520001",
- "@dcloudio/uni-mp-jd": "3.0.0-4060620250520001",
- "@dcloudio/uni-mp-kuaishou": "3.0.0-4060620250520001",
- "@dcloudio/uni-mp-lark": "3.0.0-4060620250520001",
- "@dcloudio/uni-mp-qq": "3.0.0-4060620250520001",
- "@dcloudio/uni-mp-toutiao": "3.0.0-4060620250520001",
- "@dcloudio/uni-mp-weixin": "3.0.0-4060620250520001",
- "@dcloudio/uni-mp-xhs": "3.0.0-4060620250520001",
- "@dcloudio/uni-quickapp-webview": "3.0.0-4060620250520001",
- "big-integer": "^1.6.52",
- "buffer": "^6.0.3",
+ "@dcloudio/uni-app": "^2.0.2-4070520250711001",
+ "@dcloudio/uni-app-plus": "^2.0.2-4070520250711001",
+ "@dcloudio/uni-h5": "^2.0.2-4070520250711001",
+ "@dcloudio/uni-i18n": "^2.0.2-4070520250711001",
+ "@dcloudio/uni-mp-360": "^2.0.2-4070520250711001",
+ "@dcloudio/uni-mp-alipay": "^2.0.2-4070520250711001",
+ "@dcloudio/uni-mp-baidu": "^2.0.2-4070520250711001",
+ "@dcloudio/uni-mp-harmony": "^2.0.2-4070520250711001",
+ "@dcloudio/uni-mp-jd": "^2.0.2-4070520250711001",
+ "@dcloudio/uni-mp-kuaishou": "^2.0.2-4070520250711001",
+ "@dcloudio/uni-mp-lark": "^2.0.2-4070520250711001",
+ "@dcloudio/uni-mp-qq": "^2.0.2-4070520250711001",
+ "@dcloudio/uni-mp-toutiao": "^2.0.2-4070520250711001",
+ "@dcloudio/uni-mp-vue": "^2.0.2-4070520250711001",
+ "@dcloudio/uni-mp-weixin": "^2.0.2-4070520250711001",
+ "@dcloudio/uni-mp-xhs": "^2.0.2-4070520250711001",
+ "@dcloudio/uni-quickapp-native": "^2.0.2-4070520250711001",
+ "@dcloudio/uni-quickapp-webview": "^2.0.2-4070520250711001",
+ "@dcloudio/uni-stacktracey": "^2.0.2-4070520250711001",
+ "@dcloudio/uni-stat": "^2.0.2-4070520250711001",
+ "@vue/shared": "^3.0.0",
+ "core-js": "^3.8.3",
"crypto-js": "^4.2.0",
+ "flyio": "^0.6.2",
"jsencrypt": "^3.3.2",
- "node-rsa": "^1.1.1",
- "uview-plus": "^3.4.45",
- "vite-plugin-node-polyfills": "^0.23.0",
- "vue": "^3.4.21",
- "vue-i18n": "^9.1.9"
+ "uview-ui": "2.0.38",
+ "vue": ">= 2.6.14 < 2.7",
+ "vue-class-component": "^6.3.2",
+ "vue-property-decorator": "^8.0.0",
+ "vuex": "^3.2.0"
},
"devDependencies": {
- "@dcloudio/types": "^3.4.8",
- "@dcloudio/uni-automator": "3.0.0-4060620250520001",
- "@dcloudio/uni-cli-shared": "3.0.0-4060620250520001",
- "@dcloudio/uni-stacktracey": "3.0.0-4060620250520001",
- "@dcloudio/vite-plugin-uni": "3.0.0-4060620250520001",
- "@eslint/js": "^9.30.1",
- "@vue/runtime-core": "^3.4.21",
- "eslint": "^9.30.1",
- "eslint-plugin-vue": "^10.3.0",
- "sass": "^1.89.2",
- "vite": "5.2.8"
+ "@babel/plugin-syntax-typescript": "^7.2.0",
+ "@dcloudio/types": "^3.3.2",
+ "@dcloudio/uni-automator": "^2.0.2-4070520250711001",
+ "@dcloudio/uni-cli-i18n": "^2.0.2-4070520250711001",
+ "@dcloudio/uni-cli-shared": "^2.0.2-4070520250711001",
+ "@dcloudio/uni-helper-json": "*",
+ "@dcloudio/uni-migration": "^2.0.2-4070520250711001",
+ "@dcloudio/uni-template-compiler": "^2.0.2-4070520250711001",
+ "@dcloudio/vue-cli-plugin-hbuilderx": "^2.0.2-4070520250711001",
+ "@dcloudio/vue-cli-plugin-uni": "^2.0.2-4070520250711001",
+ "@dcloudio/vue-cli-plugin-uni-optimize": "^2.0.2-4070520250711001",
+ "@dcloudio/webpack-uni-mp-loader": "^2.0.2-4070520250711001",
+ "@dcloudio/webpack-uni-pages-loader": "^2.0.2-4070520250711001",
+ "@vue/cli-plugin-babel": "~5.0.0",
+ "@vue/cli-plugin-typescript": "~5.0.8",
+ "@vue/cli-service": "~5.0.0",
+ "babel-plugin-import": "^1.11.0",
+ "cross-env": "^7.0.2",
+ "jest": "^25.4.0",
+ "postcss-comment": "^2.0.0",
+ "sass": "^1.90.0",
+ "sass-loader": "^10.5.2",
+ "typescript": "~4.5.5",
+ "vue-template-compiler": ">= 2.6.14 < 2.7"
+ },
+ "browserslist": [
+ "Android >= 4.4",
+ "ios >= 9"
+ ],
+ "uni-app": {
+ "scripts": {}
}
}
diff --git a/mobile/postcss.config.js b/mobile/postcss.config.js
new file mode 100644
index 00000000..c4a965e9
--- /dev/null
+++ b/mobile/postcss.config.js
@@ -0,0 +1,27 @@
+const path = require('path')
+const webpack = require('webpack')
+const config = {
+ parser: require('postcss-comment'),
+ plugins: [
+ require('postcss-import')({
+ resolve (id, basedir, importOptions) {
+ if (id.startsWith('~@/')) {
+ return path.resolve(process.env.UNI_INPUT_DIR, id.substr(3))
+ } else if (id.startsWith('@/')) {
+ return path.resolve(process.env.UNI_INPUT_DIR, id.substr(2))
+ } else if (id.startsWith('/') && !id.startsWith('//')) {
+ return path.resolve(process.env.UNI_INPUT_DIR, id.substr(1))
+ }
+ return id
+ }
+ }),
+ require('autoprefixer')({
+ remove: process.env.UNI_PLATFORM !== 'h5'
+ }),
+ require('@dcloudio/vue-cli-plugin-uni/packages/postcss')
+ ]
+}
+if (webpack.version[0] > 4) {
+ delete config.parser
+}
+module.exports = config
diff --git a/mobile/public/index.html b/mobile/public/index.html
new file mode 100644
index 00000000..a528055d
--- /dev/null
+++ b/mobile/public/index.html
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+ <%= htmlWebpackPlugin.options.title %>
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/mobile/shims-uni.d.ts b/mobile/shims-uni.d.ts
index ed4adcfc..b24295a4 100644
--- a/mobile/shims-uni.d.ts
+++ b/mobile/shims-uni.d.ts
@@ -1,10 +1,11 @@
///
-import 'vue'
-
-declare module '@vue/runtime-core' {
+import Vue from 'vue'
+declare module "vue/types/options" {
type Hooks = App.AppInstance & Page.PageInstance;
-
- interface ComponentCustomOptions extends Hooks {
-
+ interface ComponentOptions extends Hooks {
+ /**
+ * 组件类型
+ */
+ mpType?: string;
}
}
diff --git a/mobile/shims-vue.d.ts b/mobile/shims-vue.d.ts
new file mode 100644
index 00000000..eb40980e
--- /dev/null
+++ b/mobile/shims-vue.d.ts
@@ -0,0 +1,4 @@
+declare module "*.vue" {
+ import Vue from 'vue'
+ export default Vue
+}
diff --git a/mobile/src/App.vue b/mobile/src/App.vue
index e0a7f47d..150c63c3 100644
--- a/mobile/src/App.vue
+++ b/mobile/src/App.vue
@@ -1,34 +1,23 @@
-
-
diff --git a/mobile/src/common/config.js b/mobile/src/common/config.js
new file mode 100644
index 00000000..3ebedeca
--- /dev/null
+++ b/mobile/src/common/config.js
@@ -0,0 +1,72 @@
+// 移动端配置管理 - 统一管理密钥和请求配置
+export const ENCRYPT_CONFIG = {
+ // RSA公钥 - 与后端保持一致
+ RSA_PUBLIC_KEY:
+ 'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdHnzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ==',
+ // RSA私钥 - 与web端配置文档保持一致
+ RSA_PRIVATE_KEY:
+ 'MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAmc3CuPiGL/LcIIm7zryCEIbl1SPzBkr75E2VMtxegyZ1lYRD+7TZGAPkvIsBcaMs6Nsy0L78n2qh+lIZMpLH8wIDAQABAkEAk82Mhz0tlv6IVCyIcw/s3f0E+WLmtPFyR9/WtV3Y5aaejUkU60JpX4m5xNR2VaqOLTZAYjW8Wy0aXr3zYIhhQQIhAMfqR9oFdYw1J9SsNc+CrhugAvKTi0+BF6VoL6psWhvbAiEAxPPNTmrkmrXwdm/pQQu3UOQmc2vCZ5tiKpW10CgJi8kCIFGkL6utxw93Ncj4exE/gPLvKcT+1Emnoox+O9kRXss5AiAMtYLJDaLEzPrAWcZeeSgSIzbL+ecokmFKSDDcRske6QIgSMkHedwND1olF8vlKsJUGK3BcdtM8w4Xq7BpSBwsloE=',
+ // 客户端ID
+ CLIENT_ID: 'e5cd7e4891bf95d1d19206ce24a7b32e',
+ // 租户ID
+ TENANT_ID: '000000',
+ // 加密请求头
+ ENCRYPT_HEADER: 'encrypt-key',
+ // 是否启用加密
+ ENCRYPT_ENABLED: true,
+};
+
+// API请求配置
+export const REQUEST_CONFIG = {
+ // 基础URL配置
+ baseURL: process.env.NODE_ENV === 'development' ? '/api' : 'https://your-api-domain.com',
+ // 请求超时时间
+ timeout: 300000, // 5分钟,适合大数据量导出
+ // 客户端ID
+ clientId: 'e5cd7e4891bf95d1d19206ce24a7b32e',
+ // 默认referer
+ referer: 'http://localhost/login?redirect=/index',
+ // 是否启用加密
+ encryptEnabled: true,
+ // 防重复提交间隔时间(ms)
+ repeatSubmitInterval: 500,
+};
+
+/**
+ * 更新加密配置
+ * @param {Object} config 新的配置对象
+ */
+export const updateEncryptConfig = (config) => {
+ Object.assign(ENCRYPT_CONFIG, config);
+ console.log('🔧 加密配置已更新:', ENCRYPT_CONFIG);
+};
+
+/**
+ * 更新请求配置
+ * @param {Object} config 新的配置对象
+ */
+export const updateRequestConfig = (config) => {
+ Object.assign(REQUEST_CONFIG, config);
+ console.log('🔧 请求配置已更新:', REQUEST_CONFIG);
+};
+
+/**
+ * 获取当前配置摘要
+ * @returns {Object} 配置摘要
+ */
+export const getConfigSummary = () => {
+ return {
+ hasPublicKey: !!ENCRYPT_CONFIG.RSA_PUBLIC_KEY,
+ hasPrivateKey: !!ENCRYPT_CONFIG.RSA_PRIVATE_KEY,
+ publicKeyLength: ENCRYPT_CONFIG.RSA_PUBLIC_KEY ? ENCRYPT_CONFIG.RSA_PUBLIC_KEY.length : 0,
+ privateKeyLength: ENCRYPT_CONFIG.RSA_PRIVATE_KEY
+ ? ENCRYPT_CONFIG.RSA_PRIVATE_KEY.length
+ : 0,
+ clientId: ENCRYPT_CONFIG.CLIENT_ID,
+ tenantId: ENCRYPT_CONFIG.TENANT_ID,
+ encryptHeader: ENCRYPT_CONFIG.ENCRYPT_HEADER,
+ encryptEnabled: ENCRYPT_CONFIG.ENCRYPT_ENABLED,
+ baseURL: REQUEST_CONFIG.baseURL,
+ timeout: REQUEST_CONFIG.timeout,
+ };
+};
diff --git a/mobile/src/utils/crypto.js b/mobile/src/common/crypto.js
similarity index 84%
rename from mobile/src/utils/crypto.js
rename to mobile/src/common/crypto.js
index 4d2782f9..f727d22c 100644
--- a/mobile/src/utils/crypto.js
+++ b/mobile/src/common/crypto.js
@@ -1,24 +1,7 @@
-// 移动端加密解密工具 - 与web端保持一致
+// 移动端加密解密工具 - 与mobile工程保持一致
import CryptoJS from 'crypto-js';
import JSEncrypt from 'jsencrypt';
-
-// 加密配置 - 可配置
-export const ENCRYPT_CONFIG = {
- // RSA公钥 - 与后端保持一致
- RSA_PUBLIC_KEY:
- 'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdHnzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ==',
- // RSA私钥 - 与web端配置文档保持一致
- RSA_PRIVATE_KEY:
- 'MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAmc3CuPiGL/LcIIm7zryCEIbl1SPzBkr75E2VMtxegyZ1lYRD+7TZGAPkvIsBcaMs6Nsy0L78n2qh+lIZMpLH8wIDAQABAkEAk82Mhz0tlv6IVCyIcw/s3f0E+WLmtPFyR9/WtV3Y5aaejUkU60JpX4m5xNR2VaqOLTZAYjW8Wy0aXr3zYIhhQQIhAMfqR9oFdYw1J9SsNc+CrhugAvKTi0+BF6VoL6psWhvbAiEAxPPNTmrkmrXwdm/pQQu3UOQmc2vCZ5tiKpW10CgJi8kCIFGkL6utxw93Ncj4exE/gPLvKcT+1Emnoox+O9kRXss5AiAMtYLJDaLEzPrAWcZeeSgSIzbL+ecokmFKSDDcRske6QIgSMkHedwND1olF8vlKsJUGK3BcdtM8w4Xq7BpSBwsloE=',
- // 客户端ID
- CLIENT_ID: 'e5cd7e4891bf95d1d19206ce24a7b32e',
- // 租户ID
- TENANT_ID: '000000',
- // 加密请求头
- ENCRYPT_HEADER: 'encrypt-key',
- // 是否启用加密
- ENCRYPT_ENABLED: true,
-};
+import { ENCRYPT_CONFIG, updateEncryptConfig } from './config.js';
/**
* 随机生成32位的字符串
@@ -210,15 +193,6 @@ export const decryptWithAES = (data, key) => {
return decrypted.toString(CryptoJS.enc.Utf8);
};
-/**
- * 更新加密配置
- * @param {Object} config 新的配置对象
- */
-export const updateEncryptConfig = (config) => {
- Object.assign(ENCRYPT_CONFIG, config);
- console.log('🔧 加密配置已更新:', ENCRYPT_CONFIG);
-};
-
/**
* 验证RSA密钥对配置
* @returns {boolean} 验证结果
@@ -287,5 +261,5 @@ export const getConfigSummary = () => {
};
};
-// 导出配置以便外部修改
-export { ENCRYPT_CONFIG as config };
+// 导出配置更新方法
+export { updateEncryptConfig };
diff --git a/mobile/src/utils/request.js b/mobile/src/common/request.js
similarity index 66%
rename from mobile/src/utils/request.js
rename to mobile/src/common/request.js
index f3692053..c3a16600 100644
--- a/mobile/src/utils/request.js
+++ b/mobile/src/common/request.js
@@ -1,3 +1,4 @@
+// 移动端统一请求工具 - 与mobile工程保持一致
import {
generateAesKey,
encryptBase64,
@@ -6,63 +7,77 @@ import {
decryptWithAes,
encrypt,
decrypt,
- ENCRYPT_CONFIG,
- updateEncryptConfig,
- validateKeyPair,
- getConfigSummary,
} from './crypto.js';
+import { ENCRYPT_CONFIG, REQUEST_CONFIG } from './config.js';
+import { sessionCache, userStorage } from './storage.js';
-// API请求配置 - 可配置
-const REQUEST_CONFIG = {
- // 基础URL配置
- baseURL: process.env.NODE_ENV === 'development' ? '/api' : 'https://your-api-domain.com',
+/**
+ * 获取token
+ */
+function getToken() {
+ try {
+ const token = uni.getStorageSync('access_token');
+ console.log(
+ '🔍 获取Token:',
+ token ? `Bearer ${token.substring(0, 20)}...` : '❌ Token为空',
+ );
+ return token ? `Bearer ${token}` : '';
+ } catch (e) {
+ console.error('❌ 获取Token失败:', e);
+ return '';
+ }
+}
- // 请求超时时间
- timeout: 300000, // 5分钟,适合大数据量导出
+/**
+ * 处理401未授权响应
+ */
+function handleUnauthorized(message = '登录已失效,请重新登录') {
+ console.log('🔒 处理401未授权响应:', message);
- // 客户端ID
- clientId: 'e5cd7e4891bf95d1d19206ce24a7b32e',
+ // 清除登录信息
+ try {
+ uni.removeStorageSync('access_token');
+ uni.removeStorageSync('userInfo');
+ uni.removeStorageSync('isLoggedIn');
+ console.log('📤 已清除本地登录信息');
+ } catch (e) {
+ console.error('❌ 清除登录信息失败:', e);
+ }
- // 默认referer
- referer: 'http://localhost/login?redirect=/index',
+ // 显示提示信息
+ uni.showToast({
+ title: message,
+ icon: 'none',
+ duration: 2000,
+ });
- // 是否启用加密
- encryptEnabled: true,
+ // 延迟跳转,确保Toast显示
+ setTimeout(() => {
+ // 获取当前页面路径
+ const pages = getCurrentPages();
+ const currentPage = pages[pages.length - 1];
+ const currentPath = '/' + currentPage.route;
- // 防重复提交间隔时间(ms)
- repeatSubmitInterval: 500,
-};
-
-// 会话缓存工具
-const sessionCache = {
- set: (key, value) => {
- try {
- uni.setStorageSync(`session_${key}`, JSON.stringify(value));
- } catch (e) {
- console.error('设置会话缓存失败:', e);
+ // 如果当前不是登录页,则跳转到登录页
+ if (currentPath !== '/pages/login/index') {
+ console.log('🔄 从', currentPath, '跳转到登录页');
+ uni.reLaunch({
+ url: '/pages/login/index',
+ fail: (error) => {
+ console.error('❌ 跳转登录页失败:', error);
+ // 如果reLaunch失败,尝试其他方式
+ uni.redirectTo({
+ url: '/pages/login/index',
+ });
+ },
+ });
}
- },
+ }, 500);
+}
- get: (key) => {
- try {
- const data = uni.getStorageSync(`session_${key}`);
- return data ? JSON.parse(data) : null;
- } catch (e) {
- console.error('获取会话缓存失败:', e);
- return null;
- }
- },
-
- remove: (key) => {
- try {
- uni.removeStorageSync(`session_${key}`);
- } catch (e) {
- console.error('删除会话缓存失败:', e);
- }
- },
-};
-
-// 统一请求方法
+/**
+ * 统一请求方法
+ */
export const request = (options) => {
return new Promise((resolve, reject) => {
// 获取token
@@ -286,66 +301,6 @@ export const request = (options) => {
});
};
-// 获取token
-function getToken() {
- try {
- const token = uni.getStorageSync('access_token');
- console.log(
- '🔍 获取Token:',
- token ? `Bearer ${token.substring(0, 20)}...` : '❌ Token为空',
- );
- return token ? `Bearer ${token}` : '';
- } catch (e) {
- console.error('❌ 获取Token失败:', e);
- return '';
- }
-}
-
-// 处理401未授权响应
-function handleUnauthorized(message = '登录已失效,请重新登录') {
- console.log('🔒 处理401未授权响应:', message);
-
- // 清除登录信息
- try {
- uni.removeStorageSync('access_token');
- uni.removeStorageSync('userInfo');
- uni.removeStorageSync('isLoggedIn');
- console.log('📤 已清除本地登录信息');
- } catch (e) {
- console.error('❌ 清除登录信息失败:', e);
- }
-
- // 显示提示信息
- uni.showToast({
- title: message,
- icon: 'none',
- duration: 2000,
- });
-
- // 延迟跳转,确保Toast显示
- setTimeout(() => {
- // 获取当前页面路径
- const pages = getCurrentPages();
- const currentPage = pages[pages.length - 1];
- const currentPath = '/' + currentPage.route;
-
- // 如果当前不是登录页,则跳转到登录页
- if (currentPath !== '/pages/login/index') {
- console.log('🔄 从', currentPath, '跳转到登录页');
- uni.reLaunch({
- url: '/pages/login/index',
- fail: (error) => {
- console.error('❌ 跳转登录页失败:', error);
- // 如果reLaunch失败,尝试其他方式
- uni.redirectTo({
- url: '/pages/login/index',
- });
- },
- });
- }
- }, 500);
-}
-
// API方法封装
export const api = {
// 用户登录 - 使用统一的request方法和加密拦截器
@@ -401,164 +356,6 @@ export const api = {
}),
};
-// 用户信息缓存工具
-export const userStorage = {
- // 保存登录信息
- saveLoginInfo: (loginResult, userInfo) => {
- try {
- // 保存token
- uni.setStorageSync('access_token', loginResult.access_token);
-
- // 保存用户信息(增加登录时间)
- if (userInfo) {
- const enhancedUserInfo = {
- ...userInfo,
- loginTime: new Date().toISOString(),
- tokenExpireTime: loginResult.expires_in
- ? new Date(Date.now() + loginResult.expires_in * 1000).toISOString()
- : null,
- };
- uni.setStorageSync('userInfo', JSON.stringify(enhancedUserInfo));
- }
-
- // 保存登录状态
- uni.setStorageSync('isLoggedIn', true);
-
- // 保存其他token信息
- if (loginResult.refresh_token) {
- uni.setStorageSync('refresh_token', loginResult.refresh_token);
- }
-
- console.log('用户登录信息已保存', {
- hasToken: !!loginResult.access_token,
- hasUserInfo: !!userInfo,
- loginTime: new Date().toLocaleString(),
- });
- } catch (e) {
- console.error('保存登录信息失败:', e);
- }
- },
-
- // 获取用户信息
- getUserInfo: () => {
- try {
- const userInfo = uni.getStorageSync('userInfo');
- return userInfo ? JSON.parse(userInfo) : null;
- } catch (e) {
- console.error('获取用户信息失败:', e);
- return null;
- }
- },
-
- // 获取token
- getToken: () => {
- try {
- return uni.getStorageSync('access_token') || '';
- } catch (e) {
- return '';
- }
- },
-
- // 检查登录状态
- 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');
- console.log('用户登录信息已清除');
- } catch (e) {
- console.error('清除登录信息失败:', e);
- }
- },
-
- // 清除所有用户相关数据(包括refresh_token等)
- clearAll: () => {
- try {
- // 清除所有登录相关的存储
- uni.removeStorageSync('access_token');
- uni.removeStorageSync('userInfo');
- uni.removeStorageSync('isLoggedIn');
- uni.removeStorageSync('refresh_token');
-
- // 清除可能存在的其他用户相关数据
- uni.removeStorageSync('userPermissions');
- uni.removeStorageSync('userRoles');
- uni.removeStorageSync('tenantInfo');
-
- console.log('所有用户相关数据已清除');
- } catch (e) {
- console.error('清除所有用户数据失败:', e);
- }
- },
-};
-
-// 数据缓存工具
-export const storage = {
- // 设置缓存
- set: (key, value, expire) => {
- try {
- const data = {
- value,
- timestamp: Date.now(),
- expire: expire ? Date.now() + expire * 1000 : null,
- };
- uni.setStorageSync(key, JSON.stringify(data));
- } catch (e) {
- console.error('缓存设置失败:', e);
- }
- },
-
- // 获取缓存
- get: (key) => {
- try {
- const data = uni.getStorageSync(key);
- if (!data) return null;
-
- const parsed = JSON.parse(data);
-
- // 检查是否过期
- if (parsed.expire && Date.now() > parsed.expire) {
- uni.removeStorageSync(key);
- return null;
- }
-
- return parsed.value;
- } catch (e) {
- console.error('缓存获取失败:', e);
- return null;
- }
- },
-
- // 删除缓存
- remove: (key) => {
- try {
- uni.removeStorageSync(key);
- } catch (e) {
- console.error('缓存删除失败:', e);
- }
- },
-
- // 清空缓存
- clear: () => {
- try {
- uni.clearStorageSync();
- } catch (e) {
- console.error('缓存清空失败:', e);
- }
- },
-};
-
// 通用下载方法
export const download = (url, params, fileName) => {
// 显示加载提示
@@ -595,21 +392,3 @@ export const download = (url, params, fileName) => {
});
});
};
-
-// 配置更新方法
-export const updateRequestConfig = (config) => {
- Object.assign(REQUEST_CONFIG, config);
- console.log('🔧 请求配置已更新:', REQUEST_CONFIG);
-};
-
-// 加密配置更新方法
-export const updateCryptoConfig = updateEncryptConfig;
-
-// 密钥对验证方法
-export const validateCryptoKeys = validateKeyPair;
-
-// 获取加密配置摘要
-export const getCryptoConfigSummary = getConfigSummary;
-
-// 导出配置以便外部修改
-export { REQUEST_CONFIG as config, ENCRYPT_CONFIG as cryptoConfig };
diff --git a/mobile/src/common/storage.js b/mobile/src/common/storage.js
new file mode 100644
index 00000000..8529a80c
--- /dev/null
+++ b/mobile/src/common/storage.js
@@ -0,0 +1,267 @@
+// 用户存储管理工具 - 与mobile工程保持一致
+
+// 会话缓存工具
+export const sessionCache = {
+ set: (key, value) => {
+ try {
+ uni.setStorageSync(`session_${key}`, JSON.stringify(value));
+ } catch (e) {
+ console.error('设置会话缓存失败:', e);
+ }
+ },
+
+ get: (key) => {
+ try {
+ const data = uni.getStorageSync(`session_${key}`);
+ return data ? JSON.parse(data) : null;
+ } catch (e) {
+ console.error('获取会话缓存失败:', e);
+ return null;
+ }
+ },
+
+ remove: (key) => {
+ try {
+ uni.removeStorageSync(`session_${key}`);
+ } catch (e) {
+ console.error('删除会话缓存失败:', e);
+ }
+ },
+};
+
+// 用户信息缓存工具
+export const userStorage = {
+ // 保存登录信息
+ saveLoginInfo: (loginResult, userInfo) => {
+ try {
+ // 保存token
+ uni.setStorageSync('access_token', loginResult.access_token);
+
+ // 保存用户信息(增加登录时间)
+ if (userInfo) {
+ const enhancedUserInfo = {
+ ...userInfo,
+ loginTime: new Date().toISOString(),
+ tokenExpireTime: loginResult.expires_in
+ ? new Date(Date.now() + loginResult.expires_in * 1000).toISOString()
+ : null,
+ };
+ uni.setStorageSync('userInfo', JSON.stringify(enhancedUserInfo));
+ }
+
+ // 保存登录状态
+ uni.setStorageSync('isLoggedIn', true);
+
+ // 保存其他token信息
+ if (loginResult.refresh_token) {
+ uni.setStorageSync('refresh_token', loginResult.refresh_token);
+ }
+
+ console.log('用户登录信息已保存', {
+ hasToken: !!loginResult.access_token,
+ hasUserInfo: !!userInfo,
+ loginTime: new Date().toLocaleString(),
+ });
+ } catch (e) {
+ console.error('保存登录信息失败:', e);
+ }
+ },
+
+ // 获取用户信息
+ getUserInfo: () => {
+ try {
+ const userInfo = uni.getStorageSync('userInfo');
+ return userInfo ? JSON.parse(userInfo) : null;
+ } catch (e) {
+ console.error('获取用户信息失败:', e);
+ return null;
+ }
+ },
+
+ // 获取token
+ getToken: () => {
+ try {
+ return uni.getStorageSync('access_token') || '';
+ } catch (e) {
+ return '';
+ }
+ },
+
+ // 检查登录状态
+ 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');
+ console.log('用户登录信息已清除');
+ } catch (e) {
+ console.error('清除登录信息失败:', e);
+ }
+ },
+
+ // 清除所有用户相关数据(包括refresh_token等)
+ clearAll: () => {
+ try {
+ // 清除所有登录相关的存储
+ uni.removeStorageSync('access_token');
+ uni.removeStorageSync('userInfo');
+ uni.removeStorageSync('isLoggedIn');
+ uni.removeStorageSync('refresh_token');
+
+ // 清除可能存在的其他用户相关数据
+ uni.removeStorageSync('userPermissions');
+ uni.removeStorageSync('userRoles');
+ uni.removeStorageSync('tenantInfo');
+
+ console.log('所有用户相关数据已清除');
+ } catch (e) {
+ console.error('清除所有用户数据失败:', e);
+ }
+ },
+};
+
+// 数据缓存工具
+export const storage = {
+ // 设置缓存
+ set: (key, value, expire) => {
+ try {
+ const data = {
+ value,
+ timestamp: Date.now(),
+ expire: expire ? Date.now() + expire * 1000 : null,
+ };
+ uni.setStorageSync(key, JSON.stringify(data));
+ } catch (e) {
+ console.error('缓存设置失败:', e);
+ }
+ },
+
+ // 获取缓存
+ get: (key) => {
+ try {
+ const data = uni.getStorageSync(key);
+ if (!data) return null;
+
+ const parsed = JSON.parse(data);
+
+ // 检查是否过期
+ if (parsed.expire && Date.now() > parsed.expire) {
+ uni.removeStorageSync(key);
+ return null;
+ }
+
+ return parsed.value;
+ } catch (e) {
+ console.error('缓存获取失败:', e);
+ return null;
+ }
+ },
+
+ // 删除缓存
+ remove: (key) => {
+ try {
+ uni.removeStorageSync(key);
+ } catch (e) {
+ console.error('缓存删除失败:', e);
+ }
+ },
+
+ // 清空缓存
+ clear: () => {
+ try {
+ uni.clearStorageSync();
+ } catch (e) {
+ console.error('缓存清空失败:', e);
+ }
+ },
+};
+
+// Token测试工具
+export const tokenTest = {
+ // 测试Token存储
+ testTokenStorage: () => {
+ console.log('🧪 开始Token存储测试...');
+
+ try {
+ // 1. 清除现有Token
+ uni.removeStorageSync('access_token');
+ console.log('🗑️ 已清除现有Token');
+
+ // 2. 测试存储Token
+ const testToken =
+ 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJsb2dpblR5cGUiOiJsb2dpbiIsImxvZ2luSWQiOiJzeXNfdXNlcjoxIn0.test';
+ uni.setStorageSync('access_token', testToken);
+ console.log('💾 已存储测试Token');
+
+ // 3. 测试获取Token
+ const retrievedToken = uni.getStorageSync('access_token');
+ console.log(
+ '📖 获取到的Token:',
+ retrievedToken ? retrievedToken.substring(0, 30) + '...' : '❌ 为空',
+ );
+
+ // 4. 验证Token是否一致
+ if (retrievedToken === testToken) {
+ console.log('✅ Token存储和获取测试通过');
+ return true;
+ } else {
+ console.log('❌ Token存储和获取测试失败');
+ return false;
+ }
+ } catch (error) {
+ console.error('❌ Token测试失败:', error);
+ return false;
+ }
+ },
+
+ // 获取当前存储的Token信息
+ getCurrentTokenInfo: () => {
+ try {
+ const token = uni.getStorageSync('access_token');
+ const userInfo = uni.getStorageSync('userInfo');
+ const isLoggedIn = uni.getStorageSync('isLoggedIn');
+
+ console.log('📊 当前存储状态:');
+ console.log(' 🎫 Token:', token ? `存在 (${token.length}字符)` : '❌ 不存在');
+ console.log(' 👤 用户信息:', userInfo ? '存在' : '❌ 不存在');
+ console.log(' 🔐 登录状态:', isLoggedIn ? '已登录' : '❌ 未登录');
+
+ return {
+ hasToken: !!token,
+ tokenLength: token ? token.length : 0,
+ hasUserInfo: !!userInfo,
+ isLoggedIn: !!isLoggedIn,
+ };
+ } catch (error) {
+ console.error('❌ 获取Token信息失败:', error);
+ return null;
+ }
+ },
+
+ // 清除所有登录信息
+ clearAllLoginInfo: () => {
+ try {
+ uni.removeStorageSync('access_token');
+ uni.removeStorageSync('userInfo');
+ uni.removeStorageSync('isLoggedIn');
+ uni.removeStorageSync('refresh_token');
+
+ console.log('🗑️ 已清除所有登录信息');
+ return true;
+ } catch (error) {
+ console.error('❌ 清除登录信息失败:', error);
+ return false;
+ }
+ },
+};
diff --git a/mobile/src/utils/workflow-api.js b/mobile/src/common/workflow-api.js
similarity index 79%
rename from mobile/src/utils/workflow-api.js
rename to mobile/src/common/workflow-api.js
index 7b8607da..d682b5dd 100644
--- a/mobile/src/utils/workflow-api.js
+++ b/mobile/src/common/workflow-api.js
@@ -50,6 +50,41 @@ export const workflowApi = {
method: 'GET',
data: params,
}),
+ // 获取全部待办任务(对应web端allTaskWaiting.vue)
+ pageByAllTaskWait: (params) =>
+ request({
+ url: '/workflow/task/pageByAllTaskWait',
+ method: 'GET',
+ data: params,
+ }),
+ // 获取全部已办任务(对应web端allTaskWaiting.vue)
+ pageByAllTaskFinish: (params) =>
+ request({
+ url: '/workflow/task/pageByAllTaskFinish',
+ method: 'GET',
+ data: params,
+ }),
+ // 获取我的已办任务(对应web端taskFinish.vue)
+ pageByTaskFinish: (params) =>
+ request({
+ url: '/workflow/task/pageByTaskFinish',
+ method: 'GET',
+ data: params,
+ }),
+ // 获取我的抄送任务(对应web端taskCopyList.vue)
+ pageByTaskCopy: (params) =>
+ request({
+ url: '/workflow/task/pageByTaskCopy',
+ method: 'GET',
+ data: params,
+ }),
+ // 修改办理人
+ updateAssignee: (taskIds, assigneeId) =>
+ request({
+ url: '/workflow/task/updateAssignee',
+ method: 'POST',
+ data: { taskIds, assigneeId },
+ }),
// 启动流程实例
startProcessInstance: (data) =>
request({
diff --git a/mobile/src/components/Card.vue b/mobile/src/components/Card.vue
index 8eaefbb6..48a57174 100644
--- a/mobile/src/components/Card.vue
+++ b/mobile/src/components/Card.vue
@@ -45,36 +45,36 @@ export default {
diff --git a/mobile/src/components/GridLayout.vue b/mobile/src/components/GridLayout.vue
index 79765a48..9327ddd0 100644
--- a/mobile/src/components/GridLayout.vue
+++ b/mobile/src/components/GridLayout.vue
@@ -1,5 +1,5 @@
-
+
@@ -15,7 +15,7 @@ export default {
},
gap: {
type: String,
- default: '12px',
+ default: '16px',
},
},
computed: {
@@ -29,7 +29,7 @@ export default {
diff --git a/mobile/src/components/NavBar.vue b/mobile/src/components/NavBar.vue
deleted file mode 100644
index 60c5948a..00000000
--- a/mobile/src/components/NavBar.vue
+++ /dev/null
@@ -1,176 +0,0 @@
-
-
-
- {{ title }}
-
-
-
- 🔄
-
-
- 🔍
-
-
- ⚙️
-
-
-
-
-
-
-
-
-
-
diff --git a/mobile/src/components/ServiceItem.vue b/mobile/src/components/ServiceItem.vue
index 21332076..9f731d95 100644
--- a/mobile/src/components/ServiceItem.vue
+++ b/mobile/src/components/ServiceItem.vue
@@ -46,24 +46,24 @@ export default {
diff --git a/mobile/src/components/StatCard.vue b/mobile/src/components/StatCard.vue
deleted file mode 100644
index af1f4aa9..00000000
--- a/mobile/src/components/StatCard.vue
+++ /dev/null
@@ -1,174 +0,0 @@
-
-
- {{ number }}
- {{ label }}
-
- {{ trendIcon }}
- {{ trend }}
-
-
-
-
-
-
-
diff --git a/mobile/src/components/TreeNode.vue b/mobile/src/components/TreeNode.vue
new file mode 100644
index 00000000..2c2621a0
--- /dev/null
+++ b/mobile/src/components/TreeNode.vue
@@ -0,0 +1,108 @@
+
+
+
+
+
+
+ {{ node.label }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/mobile/src/main.js b/mobile/src/main.js
deleted file mode 100644
index 3304d0bf..00000000
--- a/mobile/src/main.js
+++ /dev/null
@@ -1,32 +0,0 @@
-import { createSSRApp } from 'vue';
-import App from './App.vue';
-import uviewPlus from 'uview-plus';
-
-// 导入 uview-plus 样式
-import 'uview-plus/index.scss';
-
-// 导入 iconfont 样式
-import '@/static/fonts/iconfont.css';
-
-// 预加载字体文件 - 使用阿里CDN
-uni.loadFontFace({
- family: 'uicon-iconfont',
- source: 'url("https://at.alicdn.com/t/font_2225171_8kdcwk4po24.ttf")',
- success() {
- console.log('uview-plus图标字体加载成功');
- },
- fail(error) {
- console.error('uview-plus图标字体加载失败', error);
- },
-});
-
-export function createApp() {
- const app = createSSRApp(App);
-
- // 注册 uview-plus
- app.use(uviewPlus);
-
- return {
- app,
- };
-}
diff --git a/mobile/src/main.ts b/mobile/src/main.ts
new file mode 100644
index 00000000..216b9270
--- /dev/null
+++ b/mobile/src/main.ts
@@ -0,0 +1,9 @@
+import Vue from 'vue'
+import App from './App.vue'
+import './uni.promisify.adaptor'
+import uView from "uview-ui";
+Vue.use(uView);
+Vue.config.productionTip = false
+
+const app = new (typeof App === 'function' ? App : Vue.extend(Object.assign({ mpType: 'app' }, App)))
+app.$mount();
diff --git a/mobile/src/manifest.json b/mobile/src/manifest.json
index 309b9ec3..5adde367 100644
--- a/mobile/src/manifest.json
+++ b/mobile/src/manifest.json
@@ -1,60 +1,58 @@
-{
- "name" : "",
- "appid" : "",
- "description" : "",
- "versionName" : "1.0.0",
- "versionCode" : "100",
- "transformPx" : false,
- /* 5+App特有相关 */
- "app-plus" : {
- "usingComponents" : true,
- "nvueStyleCompiler" : "uni-app",
- "compilerVersion" : 3,
- "splashscreen" : {
- "alwaysShowBeforeRender" : true,
- "waiting" : true,
- "autoclose" : true,
- "delay" : 0
+{
+ "name": "mobile2",
+ "appid": "",
+ "description": "",
+ "versionName": "1.0.0",
+ "versionCode": "100",
+ "transformPx":false,
+ "app-plus": { /* 5+App特有相关 */
+ "modules": { /* 模块配置 */
+
+ },
+ "distribute": { /* 应用发布信息 */
+ "android": { /* android打包配置 */
+ "permissions": ["",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ ""
+ ]
+ },
+ "ios": { /* ios打包配置 */
+
+ },
+ "sdkConfigs": { /* SDK配置 */
+
+ }
},
- /* 模块配置 */
- "modules" : {},
- /* 应用发布信息 */
- "distribute" : {
- /* android打包配置 */
- "android" : {
- "permissions" : [
- "",
- "",
- "",
- "",
- "",
- "",
- "",
- "",
- "",
- "",
- "",
- "",
- "",
- "",
- ""
- ]
- },
- /* ios打包配置 */
- "ios" : {},
- /* SDK配置 */
- "sdkConfigs" : {}
- }
- },
- /* 快应用特有相关 */
- "quickapp" : {},
- /* 小程序特有相关 */
- "mp-weixin" : {
- "appid" : "",
- "setting" : {
- "urlCheck" : false
- },
- "usingComponents" : true
+ "usingComponents":true
+ },
+ "quickapp": { /* 快应用特有相关 */
+
+ },
+ "mp-weixin": { /* 小程序特有相关 */
+ "usingComponents":true,
+ "appid": "",
+ "setting" : {
+ "urlCheck" : true
+ }
},
"mp-alipay" : {
"usingComponents" : true
@@ -65,8 +63,7 @@
"mp-toutiao" : {
"usingComponents" : true
},
- "uniStatistics": {
- "enable": false
- },
- "vueVersion" : "3"
+ "mp-qq" : {
+ "usingComponents" : true
+ }
}
diff --git a/mobile/src/pages.json b/mobile/src/pages.json
index f7d50f72..75e1c6d0 100644
--- a/mobile/src/pages.json
+++ b/mobile/src/pages.json
@@ -1,71 +1,59 @@
{
"easycom": {
- "autoscan": true,
- "custom": {
- "^u-(.*)": "uview-plus/components/u-$1/u-$1.vue",
- "^up-(.*)": "uview-plus/components/u-$1/u-$1.vue",
- "^uv-(.*)": "uview-plus/components/u-$1/u-$1.vue"
- }
+ "^u-(.*)": "uview-ui/components/u-$1/u-$1.vue"
},
"pages": [
{
"path": "pages/login/index",
"style": {
"navigationBarTitleText": "登录",
- "navigationStyle": "custom",
- "navigationBarBackgroundColor": "transparent",
- "backgroundColor": "transparent"
+ "navigationStyle": "custom"
}
},
{
"path": "pages/index/index",
"style": {
- "navigationBarTitleText": "首页",
+ "navigationBarTitleText": "杭州第一技师学院智慧校园",
"navigationStyle": "custom"
}
},
{
- "path": "pages/iconfont-demo/index",
+ "path": "pages/task/myDocument",
"style": {
- "navigationBarTitleText": "图标演示",
+ "navigationBarTitleText": "我发起的",
"navigationBarBackgroundColor": "#5d87ff",
"navigationBarTextStyle": "white"
}
},
{
- "path": "pages/test-route/index",
+ "path": "pages/task/allTaskWaiting",
"style": {
- "navigationBarTitleText": "路由守卫测试",
- "navigationStyle": "custom"
+ "navigationBarTitleText": "我的待办",
+ "navigationBarBackgroundColor": "#5d87ff",
+ "navigationBarTextStyle": "white"
}
},
{
- "path": "pages/workflow/process",
+ "path": "pages/task/taskFinish",
"style": {
- "navigationBarTitleText": "流程详情",
- "navigationStyle": "custom"
+ "navigationBarTitleText": "我的已办",
+ "navigationBarBackgroundColor": "#5d87ff",
+ "navigationBarTextStyle": "white"
}
},
{
- "path": "pages/workflow/initiated",
+ "path": "pages/task/taskCopyList",
"style": {
- "navigationBarTitleText": "我发起的流程",
- "navigationStyle": "custom"
- }
- },
- {
- "path": "pages/uview-icon-test/index",
- "style": {
- "navigationBarTitleText": "uview图标测试",
+ "navigationBarTitleText": "抄送我的",
"navigationBarBackgroundColor": "#5d87ff",
"navigationBarTextStyle": "white"
}
}
],
"globalStyle": {
- "navigationBarTextStyle": "white",
+ "navigationBarTextStyle": "black",
"navigationBarTitleText": "智慧校园",
- "navigationBarBackgroundColor": "rgb(93, 135, 255)",
- "backgroundColor": "#f8f9fa"
+ "navigationBarBackgroundColor": "#5d87ff",
+ "backgroundColor": "#F8F8F8"
}
}
\ No newline at end of file
diff --git a/mobile/src/pages/demo/iconfont-demo/index.vue b/mobile/src/pages/demo/iconfont-demo/index.vue
deleted file mode 100644
index 1e5f80ae..00000000
--- a/mobile/src/pages/demo/iconfont-demo/index.vue
+++ /dev/null
@@ -1,348 +0,0 @@
-
-
-
-
-
-
-
- 基础图标
-
-
-
- 用户
- icon-user
-
-
-
- 锁定
- icon-lock
-
-
-
- 眼睛
- icon-eye
-
-
-
- 关闭眼睛
- icon-eye-close
-
-
-
-
-
-
- 操作图标
-
-
-
- 添加
- icon-add
-
-
-
- 编辑
- icon-edit
-
-
-
- 删除
- icon-delete
-
-
-
- 搜索
- icon-search
-
-
-
- 刷新
- icon-refresh
-
-
-
- 关闭
- icon-close
-
-
-
-
-
-
- 文件图标
-
-
-
- 下载
- icon-download
-
-
-
- 上传
- icon-upload
-
-
-
-
-
-
- 状态图标
-
-
-
- 成功
- icon-success
-
-
-
- 警告
- icon-warning
-
-
-
- 信息
- icon-info
-
-
-
- 确认
- icon-check
-
-
-
-
-
-
- 社交图标
-
-
-
- 星星
- icon-star
-
-
-
- 喜欢
- icon-heart
-
-
-
- 消息
- icon-message
-
-
-
- 电话
- icon-phone
-
-
-
- 邮件
- icon-mail
-
-
-
-
-
-
- 导航图标
-
-
-
- 首页
- icon-home
-
-
-
- 消息
- icon-message
-
-
-
- 设置
- icon-setting
-
-
-
- 箭头右
- icon-arrow-right
-
-
-
-
-
-
- 联系图标
-
-
-
- 电话
- icon-phone
-
-
-
- 邮箱
- icon-mail
-
-
-
- 确认
- icon-check
-
-
-
-
-
-
- 使用方法
-
-
- <text class="iconfont icon-user"></text>
-
-
- <text class="iconfont icon-lock"></text>
-
- /* 自定义样式 */
- .custom-icon {
- font-size: 24px;
- color: #5d87ff;
- }
-
-
-
-
-
-
-
-
-
diff --git a/mobile/src/pages/demo/test-route/index.vue b/mobile/src/pages/demo/test-route/index.vue
deleted file mode 100644
index 185bfb5a..00000000
--- a/mobile/src/pages/demo/test-route/index.vue
+++ /dev/null
@@ -1,346 +0,0 @@
-
-
-
-
-
-
- 🛡️ 路由守卫测试页面
- 如果你能看到这个页面,说明已经通过了登录验证
-
-
-
-
- 当前时间:
- {{ currentTime }}
-
-
- 登录状态:
-
- {{ isLoggedIn ? '✅ 已登录' : '❌ 未登录' }}
-
-
-
- Token状态:
-
- {{ hasToken ? '✅ 有效' : '❌ 无效' }}
-
-
-
- 用户信息:
- {{ userName || '未获取' }}
-
-
-
-
-
-
-
-
-
-
-
- 📋 测试日志
-
-
- {{ log.time }}
- {{ log.message }}
-
-
-
-
-
-
-
-
-
-
diff --git a/mobile/src/pages/demo/uview-icon-test/index.vue b/mobile/src/pages/demo/uview-icon-test/index.vue
deleted file mode 100644
index 5b356f2d..00000000
--- a/mobile/src/pages/demo/uview-icon-test/index.vue
+++ /dev/null
@@ -1,209 +0,0 @@
-
-
-
-
-
-
- ❌ 问题演示 - uview-plus图标异常
-
-
-
- logout图标 (可能显示文字)
-
-
-
- power图标 (可能显示文字)
-
-
-
- exit图标 (可能显示文字)
-
-
-
-
-
-
- ✅ 正常工作 - uview-plus图标
-
-
-
- account图标 (正常)
-
-
-
- home图标 (正常)
-
-
-
- setting图标 (正常)
-
-
-
-
-
-
- 🎯 解决方案 - 使用iconfont自定义图标
-
-
-
- 关闭图标 (退出登录)
-
-
-
- 用户图标
-
-
-
- 设置图标
-
-
-
-
-
-
- 🔤 备选方案 - Unicode字符
-
-
- ⏻
- 电源符号 (退出登录)
-
-
- ❌
- 关闭符号
-
-
- ⚙️
- 设置符号
-
-
-
-
-
-
- 💡 解决方案说明
-
-
- 1. **根本原因**: uview-plus某些图标名称在当前版本字体文件中不存在
-
-
- 2. **推荐方案**: 使用项目中的iconfont自定义图标系统
-
-
- 3. **备选方案**: 使用Unicode字符(⏻ ❌ ⚙️),兼容性最好
-
-
- 4. **长期方案**: 更新uview-plus字体文件或使用其他图标库
-
-
-
-
-
-
-
-
-
diff --git a/mobile/src/pages/index/index.vue b/mobile/src/pages/index/index.vue
index 2b496d9a..fb089852 100644
--- a/mobile/src/pages/index/index.vue
+++ b/mobile/src/pages/index/index.vue
@@ -1,821 +1,956 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ item.title }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/mobile/src/pages/login/README.md b/mobile/src/pages/login/README.md
deleted file mode 100644
index 0519ecba..00000000
--- a/mobile/src/pages/login/README.md
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/mobile/src/pages/login/index.vue b/mobile/src/pages/login/index.vue
index 7580eb5f..bf32c5ba 100644
--- a/mobile/src/pages/login/index.vue
+++ b/mobile/src/pages/login/index.vue
@@ -17,14 +17,19 @@
-
+
@@ -40,7 +45,12 @@
@confirm="onPasswordConfirm"
>
-
+
@@ -60,7 +70,7 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 搜索
+ 重置
+
+
+
+
+
+
+ 已选择 {{ userSelectCount }} 位申请人
+ 清除
+
+
+
+
+
+
+
+
+ 加载中...
+
+
+
+
+
+
+ 暂无数据
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 查看
+
+
+ 流程干预
+
+
+
+
+
+
+ 查看
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/mobile/src/pages/task/myDocument.vue b/mobile/src/pages/task/myDocument.vue
new file mode 100644
index 00000000..23c31b0e
--- /dev/null
+++ b/mobile/src/pages/task/myDocument.vue
@@ -0,0 +1,764 @@
+
+
+
+
+
+
+
+
+
+
+
+ 搜索
+ 重置
+
+
+
+
+
+ 当前分类:{{ getCategoryPath(selectedCategory) }}
+ 清除
+
+
+
+
+
+
+
+ 加载中...
+
+
+
+
+
+
+ 暂无数据
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/mobile/src/pages/task/taskCopyList.vue b/mobile/src/pages/task/taskCopyList.vue
new file mode 100644
index 00000000..21b81fbf
--- /dev/null
+++ b/mobile/src/pages/task/taskCopyList.vue
@@ -0,0 +1,434 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 搜索
+ 重置
+
+
+
+
+
+
+
+
+ 加载中...
+
+
+
+
+
+
+ 暂无数据
+
+
+
+
+
+
+
+
+
+
+
+ 查看
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/mobile/src/pages/task/taskFinish.vue b/mobile/src/pages/task/taskFinish.vue
new file mode 100644
index 00000000..6acf2954
--- /dev/null
+++ b/mobile/src/pages/task/taskFinish.vue
@@ -0,0 +1,706 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 搜索
+ 重置
+
+
+
+
+
+
+ 已选择 {{ userSelectCount }} 位申请人
+ 清除
+
+
+
+
+
+
+
+
+ 加载中...
+
+
+
+
+
+
+ 暂无数据
+
+
+
+
+
+
+
+
+
+
+
+ 查看
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/mobile/src/pages/workflow/initiated.vue b/mobile/src/pages/workflow/initiated.vue
deleted file mode 100644
index d0169f7a..00000000
--- a/mobile/src/pages/workflow/initiated.vue
+++ /dev/null
@@ -1,167 +0,0 @@
-
-
-
- 加载中...
-
- 暂无数据
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/mobile/src/pages/workflow/process.vue b/mobile/src/pages/workflow/process.vue
deleted file mode 100644
index 8fe3a3ed..00000000
--- a/mobile/src/pages/workflow/process.vue
+++ /dev/null
@@ -1,117 +0,0 @@
-
-
-
- 加载中...
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 提交
-
-
-
-
-
-
-
-
diff --git a/mobile/src/shime-uni.d.ts b/mobile/src/shime-uni.d.ts
deleted file mode 100644
index b3d3cc9c..00000000
--- a/mobile/src/shime-uni.d.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-export {};
-
-declare module "vue" {
- type Hooks = App.AppInstance & Page.PageInstance;
- interface ComponentCustomOptions extends Hooks {}
-}
\ No newline at end of file
diff --git a/mobile/src/static/fonts/font_2225171_8kdcwk4po24.ttf b/mobile/src/static/fonts/font_2225171_8kdcwk4po24.ttf
deleted file mode 100644
index befa24aa..00000000
Binary files a/mobile/src/static/fonts/font_2225171_8kdcwk4po24.ttf and /dev/null differ
diff --git a/mobile/src/static/fonts/iconfont.css b/mobile/src/static/fonts/iconfont.css
index 66021018..ed0cf9e7 100644
--- a/mobile/src/static/fonts/iconfont.css
+++ b/mobile/src/static/fonts/iconfont.css
@@ -5,248 +5,20 @@
background-size: contain;
background-repeat: no-repeat;
background-position: center;
+ color: #666; /* 设置默认图标颜色为灰色,避免黑色显示 */
}
-/* 用户图标 */
-.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");
-}
-
-/* 密码锁图标 */
-.icon-lock {
- 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='M18 8h-1V6c0-2.76-2.24-5-5-5S7 3.24 7 6v2H6c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V10c0-1.1-.9-2-2-2zM9 6c0-1.66 1.34-3 3-3s3 1.34 3 3v2H9V6z'/%3E%3C/svg%3E");
-}
-
-/* 眼睛图标(显示密码) */
-.icon-eye {
- 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 4.5C7 4.5 2.73 7.61 1 12c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7.5-11-7.5zM12 17c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5zm0-8c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3z'/%3E%3C/svg%3E");
-}
-
-/* 眼睛关闭图标(隐藏密码) */
-.icon-eye-close {
- 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 7c2.76 0 5 2.24 5 5 0 .65-.13 1.26-.36 1.83l2.92 2.92c1.51-1.26 2.7-2.89 3.43-4.75-1.73-4.39-6-7.5-11-7.5-1.4 0-2.74.25-3.98.7l2.16 2.16C10.74 7.13 11.35 7 12 7zM2 4.27l2.28 2.28.46.46C3.08 8.3 1.78 10.02 1 12c1.73 4.39 6 7.5 11 7.5 1.55 0 3.03-.3 4.38-.84l.42.42L19.73 22 21 20.73 3.27 3 2 4.27zM7.53 9.8l1.55 1.55c-.05.21-.08.43-.08.65 0 1.66 1.34 3 3 3 .22 0 .44-.03.65-.08l1.55 1.55c-.67.33-1.41.53-2.2.53-2.76 0-5-2.24-5-5 0-.79.2-1.53.53-2.2zm4.31-.78l3.15 3.15.02-.16c0-1.66-1.34-3-3-3l-.17.01z'/%3E%3C/svg%3E");
-}
-
-/* 登录图标 */
-.icon-login {
- 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='M11 7L9.6 8.4l2.6 2.6H2v2h10.2l-2.6 2.6L11 17l5-5-5-5zm9 12h-8v2h8c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2h-8v2h8v14z'/%3E%3C/svg%3E");
-}
-
-/* 箭头右图标 */
-.icon-arrow-right {
- 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='M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z'/%3E%3C/svg%3E");
-}
-
-/* 更多常用图标 */
-.icon-home {
- 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='M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z'/%3E%3C/svg%3E");
-}
-
-.icon-message {
- 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='M20 2H4c-1.1 0-1.99.9-1.99 2L2 22l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-2 12H6v-2h12v2zm0-3H6V9h12v2zm0-3H6V6h12v2z'/%3E%3C/svg%3E");
-}
-
-.icon-setting {
- 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='M19.14,12.94c0.04-0.3,0.06-0.61,0.06-0.94c0-0.32-0.02-0.64-0.07-0.94l2.03-1.58c0.18-0.14,0.23-0.41,0.12-0.61 l-1.92-3.32c-0.12-0.22-0.37-0.29-0.59-0.22l-2.39,0.96c-0.5-0.38-1.03-0.7-1.62-0.94L14.4,2.81c-0.04-0.24-0.24-0.41-0.48-0.41 h-3.84c-0.24,0-0.43,0.17-0.47,0.41L9.25,5.35C8.66,5.59,8.12,5.92,7.63,6.29L5.24,5.33c-0.22-0.08-0.47,0-0.59,0.22L2.74,8.87 C2.62,9.08,2.66,9.34,2.86,9.48l2.03,1.58C4.84,11.36,4.8,11.69,4.8,12s0.02,0.64,0.07,0.94l-2.03,1.58 c-0.18,0.14-0.23,0.41-0.12,0.61l1.92,3.32c0.12,0.22,0.37,0.29,0.59,0.22l2.39-0.96c0.5,0.38,1.03,0.7,1.62,0.94l0.36,2.54 c0.05,0.24,0.24,0.41,0.48,0.41h3.84c0.24,0,0.44-0.17,0.47-0.41l0.36-2.54c0.59-0.24,1.13-0.56,1.62-0.94l2.39,0.96 c0.22,0.08,0.47,0,0.59-0.22l1.92-3.32c0.12-0.22,0.07-0.47-0.12-0.61L19.14,12.94z M12,15.6c-1.98,0-3.6-1.62-3.6-3.6 s1.62-3.6,3.6-3.6s3.6,1.62,3.6,3.6S13.98,15.6,12,15.6z'/%3E%3C/svg%3E");
-}
-
-.icon-search {
- 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='M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z'/%3E%3C/svg%3E");
-}
-
-.icon-add {
- 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='M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z'/%3E%3C/svg%3E");
-}
-
-.icon-check {
- 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='M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z'/%3E%3C/svg%3E");
-}
-
-.icon-close {
- 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='M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z'/%3E%3C/svg%3E");
-}
-
-.icon-phone {
- 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='M6.62 10.79c1.44 2.83 3.76 5.14 6.59 6.59l2.2-2.2c.27-.27.67-.36 1.02-.24 1.12.37 2.33.57 3.57.57.55 0 1 .45 1 1V20c0 .55-.45 1-1 1-9.39 0-17-7.61-17-17 0-.55.45-1 1-1h3.5c.55 0 1 .45 1 1 0 1.25.2 2.45.57 3.57.11.35.03.74-.25 1.02l-2.2 2.2z'/%3E%3C/svg%3E");
-}
-
-.icon-mail {
- 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='M20 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 4l-8 5-8-5V6l8 5 8-5v2z'/%3E%3C/svg%3E");
-}
-
-/* 新增常用图标 - 2025-01-07 */
-
-/* 星星图标 */
-.icon-star {
- 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 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-.61L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21z'/%3E%3C/svg%3E");
-}
-
-/* 心形图标 */
-.icon-heart {
- 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 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z'/%3E%3C/svg%3E");
-}
-
-/* 日历图标 */
-.icon-calendar {
- 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='M19 3h-1V1h-2v2H8V1H6v2H5c-1.11 0-1.99.9-1.99 2L3 19c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H5V8h14v11zM7 10h5v5H7z'/%3E%3C/svg%3E");
-}
-
-/* 聊天图标 */
-.icon-chat {
- 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='M20 2H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h4l4 4 4-4h4c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-2 12H6v-2h12v2zm0-3H6V9h12v2zm0-3H6V6h12v2z'/%3E%3C/svg%3E");
-}
-
-/* 工具图标 */
-.icon-tools {
- 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='M22.7 19l-9.1-9.1c.9-2.3.4-5-1.5-6.9-2-2-5-2.4-7.4-1.3L9 6 6 9 1.6 4.7C.4 7.1.9 10.1 2.9 12.1c1.9 1.9 4.6 2.4 6.9 1.5l9.1 9.1c.4.4 1 .4 1.4 0l2.3-2.3c.5-.4.5-1.1.1-1.4z'/%3E%3C/svg%3E");
-}
-
-/* 地图图标 */
-.icon-map {
- 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='M20.5 3l-.16.03L15 5.1 9 3 3.36 4.9c-.21.07-.36.25-.36.48V20.5c0 .28.22.5.5.5l.16-.03L9 18.9l6 2.1 5.64-1.9c.21-.07.36-.25.36-.48V3.5c0-.28-.22-.5-.5-.5zM15 19l-6-2.11V5l6 2.11V19z'/%3E%3C/svg%3E");
-}
-
-/* 播放圆圈图标 */
-.icon-play-circle {
- 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 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 14.5v-9l6 4.5-6 4.5z'/%3E%3C/svg%3E");
-}
-
-/* 列表图标 */
-.icon-list {
- 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='M3 13h2v-2H3v2zm0 4h2v-2H3v2zm0-8h2V7H3v2zm4 4h14v-2H7v2zm0 4h14v-2H7v2zM7 7v2h14V7H7z'/%3E%3C/svg%3E");
-}
-
-/* 人民币圆圈图标 */
-.icon-rmb-circle {
- 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 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1.5 6L12 10.5 10.5 8H9.5l1.75 2.5H9v1h3v1H9v1h3V15h1v-1.5h3v-1h-3v-1h3v-1h-2.25L14.5 8h-1z'/%3E%3C/svg%3E");
-}
-
-/* 商店图标 */
-.icon-shop {
- 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 2l3.09 6.26L22 9.27 16.18 14l1.64 7L12 17.77 6.18 21l1.64-7L2 9.27l6.91-1.01L12 2zm0 0L4 8v12h16V8l-8-6z'/%3E%3C/svg%3E");
-}
-
-/* 奖杯图标 */
-.icon-trophy {
- 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='M7 4V2c0-1.1.9-2 2-2h6c1.1 0 2 .9 2 2v2h4c1.1 0 2 .9 2 2v4c0 2.21-1.79 4-4 4h-1.28c-.44 2.83-2.82 5-5.72 5s-5.28-2.17-5.72-5H5c-2.21 0-4-1.79-4-4V6c0-1.1.9-2 2-2h4zM9 4h6V2H9v2zM5 6v4c0 1.1.9 2 2 2h1.28c.42-1.82 1.89-3.26 3.72-3.26s3.3 1.44 3.72 3.26H17c1.1 0 2-.9 2-2V6H5zm7 14c1.66 0 3-1.34 3-3s-1.34-3-3-3-3 1.34-3 3 1.34 3 3 3z'/%3E%3C/svg%3E");
-}
-
-/* 朋友图标 */
-.icon-friends {
- 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='M16 4c0-1.11.89-2 2-2s2 .89 2 2-.89 2-2 2-2-.89-2-2zm4 18v-6h2.5l-2.54-7.63A1.495 1.495 0 0 0 18.5 7.5h-3c-.57 0-1.08.32-1.34.82L11.5 15h2.5v7h6zM12.5 11.5c.83 0 1.5-.67 1.5-1.5s-.67-1.5-1.5-1.5S11 9.17 11 10s.67 1.5 1.5 1.5zM5.5 6c1.11 0 2-.89 2-2s-.89-2-2-2-2 .89-2 2 .89 2 2 2zm2 16v-7H9l-1.46-3.12A1.495 1.495 0 0 0 6.12 11h-1.5c-.57 0-1.08.32-1.34.82L1 17h2.5v7h4z'/%3E%3C/svg%3E");
-}
-
-/* 飞机图标 */
-.icon-plane {
- 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='M21 16v-2l-8-5V3.5c0-.83-.67-1.5-1.5-1.5S10 2.67 10 3.5V9l-8 5v2l8-2.5V19l-2 1.5V22l3.5-1 3.5 1v-1.5L13 19v-5.5l8 2.5z'/%3E%3C/svg%3E");
-}
-
-/* 书签图标 */
-.icon-bookmark {
- 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='M17 3H7c-1.1 0-1.99.9-1.99 2L5 21l7-3 7 3V5c0-1.1-.9-2-2-2z'/%3E%3C/svg%3E");
-}
-
-/* 人民币圆圈填充图标 */
-.icon-rmb-circle-fill {
- 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 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1.5 6L12 10.5 10.5 8H9.5l1.75 2.5H9v1h3v1H9v1h3V15h1v-1.5h3v-1h-3v-1h3v-1h-2.25L14.5 8h-1z'/%3E%3C/svg%3E");
-}
-
-/* 金钱圆圈图标 */
-.icon-money-circle {
- 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='M11.8 10.9c-2.27-.59-3-1.2-3-2.15 0-1.09 1.01-1.85 2.7-1.85 1.78 0 2.44.85 2.5 2.1h2.21c-.07-1.72-1.12-3.3-3.21-3.81V3h-3v2.16c-1.94.42-3.5 1.68-3.5 3.61 0 2.31 1.91 3.46 4.7 4.13 2.5.6 3 1.48 3 2.41 0 .69-.49 1.79-2.7 1.79-2.06 0-2.87-.92-2.98-2.1h-2.2c.12 2.19 1.76 3.42 3.68 3.83V21h3v-2.15c1.95-.37 3.5-1.5 3.5-3.55 0-2.84-2.43-3.81-4.7-4.4z'/%3E%3C/svg%3E");
-}
-
-/* 文件文本图标 */
-.icon-file-text {
- 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='M14 2H6c-1.1 0-1.99.9-1.99 2L4 20c0 1.1.89 2 2 2h12c1.1 0 2-.9 2-2V8l-6-6zm2 16H8v-2h8v2zm0-4H8v-2h8v2zm-3-5V3.5L18.5 9H13z'/%3E%3C/svg%3E");
-}
-
-/* 订单图标 */
-.icon-order {
- 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='M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-5 14H7v-2h7v2zm3-4H7v-2h10v2zm0-4H7V7h10v2z'/%3E%3C/svg%3E");
-}
-
-/* 设置填充图标 */
-.icon-setting-fill {
- 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 8c-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4-1.79-4-4-4zm8.94 3A8.994 8.994 0 0 0 13 3.06V1h-2v2.06A8.994 8.994 0 0 0 3.06 11H1v2h2.06A8.994 8.994 0 0 0 11 20.94V23h2v-2.06A8.994 8.994 0 0 0 20.94 13H23v-2h-2.06zM12 19c-3.87 0-7-3.13-7-7s3.13-7 7-7 7 3.13 7 7-3.13 7-7 7z'/%3E%3C/svg%3E");
-}
-
-/* 用户圆圈填充图标 */
-.icon-account-circle-fill {
- 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 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 3c1.66 0 3 1.34 3 3s-1.34 3-3 3-3-1.34-3-3 1.34-3 3-3zm0 14.2c-2.5 0-4.71-1.28-6-3.22.03-1.99 4-3.08 6-3.08 1.99 0 5.97 1.09 6 3.08-1.29 1.94-3.5 3.22-6 3.22z'/%3E%3C/svg%3E");
-}
-
-/* 列表点图标 */
-.icon-list-dot {
- 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='M4 10.5c-.83 0-1.5.67-1.5 1.5s.67 1.5 1.5 1.5 1.5-.67 1.5-1.5-.67-1.5-1.5-1.5zm0-6c-.83 0-1.5.67-1.5 1.5S3.17 7.5 4 7.5 5.5 6.83 5.5 6 4.83 4.5 4 4.5zm0 12c-.83 0-1.5.67-1.5 1.5s.67 1.5 1.5 1.5 1.5-.67 1.5-1.5-.67-1.5-1.5-1.5zM7 19h14v-2H7v2zm0-6h14v-2H7v2zm0-8v2h14V5H7z'/%3E%3C/svg%3E");
-}
-
-/* 首页填充图标 */
-.icon-home-fill {
- 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='M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z'/%3E%3C/svg%3E");
-}
-
-/* WiFi图标 */
-.icon-wifi {
- 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='M1 9l2 2c4.97-4.97 13.03-4.97 18 0l2-2C16.93 2.93 7.07 2.93 1 9zm8 8l3 3 3-3c-1.65-1.66-4.34-1.66-6 0zm-4-4l2 2c2.76-2.76 7.24-2.76 10 0l2-2C15.14 9.14 8.87 9.14 5 13z'/%3E%3C/svg%3E");
-}
-
-/* 分享图标 */
-.icon-share {
- 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='M18 16.08c-.76 0-1.44.3-1.96.77L8.91 12.7c.05-.23.09-.46.09-.7s-.04-.47-.09-.7l7.05-4.11c.54.5 1.25.81 2.04.81 1.66 0 3-1.34 3-3s-1.34-3-3-3-3 1.34-3 3c0 .24.04.47.09.7L8.04 9.81C7.5 9.31 6.79 9 6 9c-1.66 0-3 1.34-3 3s1.34 3 3 3c.79 0 1.5-.31 2.04-.81l7.12 4.16c-.05.21-.08.43-.08.65 0 1.61 1.31 2.92 2.92 2.92 1.61 0 2.92-1.31 2.92-2.92s-1.31-2.92-2.92-2.92z'/%3E%3C/svg%3E");
-}
-
-/* 分享方形图标 */
-.icon-share-square {
- 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='M18 2h-3c-1.1 0-2 .9-2 2v3H7c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h10c1.1 0 2-.9 2-2v-6h3c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-8 15H7v-3h3v3zm0-5H7v-2h3v2zm0-4H7V6h3v2zm5 9h-3v-3h3v3zm0-5h-3v-2h3v2zm0-4h-3V6h3v2zm5-3h-3V4h3v1z'/%3E%3C/svg%3E");
-}
-
-/* 编辑笔图标 */
-.icon-edit-pen {
- 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='M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04c.39-.39.39-1.02 0-1.41l-2.34-2.34c-.39-.39-1.02-.39-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z'/%3E%3C/svg%3E");
-}
-
-/* 时钟图标 */
-.icon-clock {
- 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='M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8z'/%3E%3C/path%3E%3Cpath d='m12.5 7-1 1v4.25l3.5 2.08.75-1.23-2.75-1.63V8z'/%3E%3C/svg%3E");
-}
-
-/* 确认圆圈图标 */
-.icon-checkmark-circle {
- 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 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z'/%3E%3C/svg%3E");
-}
-
-/* 复制图标 */
-.icon-copy {
- 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='M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z'/%3E%3C/svg%3E");
-}
-
-/* 用户圆圈图标 */
-.icon-account-circle {
- 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 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 3c1.66 0 3 1.34 3 3s-1.34 3-3 3-3-1.34-3-3 1.34-3 3-3zm0 14.2c-2.5 0-4.71-1.28-6-3.22.03-1.99 4-3.08 6-3.08 1.99 0 5.97 1.09 6 3.08-1.29 1.94-3.5 3.22-6 3.22z'/%3E%3C/svg%3E");
-}
-
-/* 用户填充图标 */
-.icon-account-fill {
- 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");
-}
-
-/* 星星填充图标 */
-.icon-star-fill {
- 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 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z'/%3E%3C/svg%3E");
-}
-
-/* 包图标 */
-.icon-bag {
- 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='M17 6h-2V3c0-.55-.45-1-1-1h-4c-.55 0-1 .45-1 1v3H7c-1.1 0-2 .9-2 2v11c0 1.1.9 2 2 2h10c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2zM9.5 3h5v3h-5V3zM9 12v-2h2v2h2v-2h2v2c0 1.1-.9 2-2 2h-2c-1.1 0-2-.9-2-2z'/%3E%3C/svg%3E");
-}
-
-/* 学工管理填充图标 */
-.icon-account-fill {
- 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");
-}
-
-/* 登出图标 */
+/* 退出登录图标 */
.icon-logout {
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='M17 7l-1.41 1.41L18.17 11H8v2h10.17l-2.58 2.59L17 17l5-5-5-5zM4 5h8V3H4c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h8v-2H4V5z'/%3E%3C/svg%3E");
}
+
+/* 电源开关图标 */
+.icon-power {
+ 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='M13 3h-2v10h2V3zm4.83 2.17l-1.42 1.42C17.99 7.86 19 9.81 19 12c0 3.87-3.13 7-7 7s-7-3.13-7-7c0-2.19 1.01-4.14 2.59-5.41L6.17 5.17C4.23 6.82 3 9.26 3 12c0 4.97 4.03 9 9 9s9-4.03 9-9c0-2.74-1.23-5.18-3.17-6.83z'/%3E%3C/svg%3E");
+}
+
+/* 关闭圆圈图标 */
+.icon-close-circle {
+ 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 2C6.47 2 2 6.47 2 12s4.47 10 10 10 10-4.47 10-10S17.53 2 12 2zm5 13.59L15.59 17 12 13.41 8.41 17 7 15.59 10.59 12 7 8.41 8.41 7 12 10.59 15.59 7 17 8.41 13.41 12 17 15.59z'/%3E%3C/svg%3E");
+}
\ No newline at end of file
diff --git a/mobile/src/static/logo-1.png b/mobile/src/static/logo-1.png
deleted file mode 100644
index ce1caf95..00000000
Binary files a/mobile/src/static/logo-1.png and /dev/null differ
diff --git a/mobile/src/static/tabbar/home-active.svg b/mobile/src/static/tabbar/home-active.svg
deleted file mode 100644
index 60652641..00000000
--- a/mobile/src/static/tabbar/home-active.svg
+++ /dev/null
@@ -1,4 +0,0 @@
-
\ No newline at end of file
diff --git a/mobile/src/static/tabbar/home.svg b/mobile/src/static/tabbar/home.svg
deleted file mode 100644
index 768819fe..00000000
--- a/mobile/src/static/tabbar/home.svg
+++ /dev/null
@@ -1,4 +0,0 @@
-
\ No newline at end of file
diff --git a/mobile/src/static/xx01.jpg b/mobile/src/static/xx01.jpg
new file mode 100644
index 00000000..0a736165
Binary files /dev/null and b/mobile/src/static/xx01.jpg differ
diff --git a/mobile/src/static/xx02.jpg b/mobile/src/static/xx02.jpg
new file mode 100644
index 00000000..aad102d9
Binary files /dev/null and b/mobile/src/static/xx02.jpg differ
diff --git a/mobile/src/static/xx03.jpg b/mobile/src/static/xx03.jpg
new file mode 100644
index 00000000..9c91ca16
Binary files /dev/null and b/mobile/src/static/xx03.jpg differ
diff --git a/mobile/src/types/uview-ui.d.ts b/mobile/src/types/uview-ui.d.ts
new file mode 100644
index 00000000..9dd501e0
--- /dev/null
+++ b/mobile/src/types/uview-ui.d.ts
@@ -0,0 +1,10 @@
+declare module 'uview-ui' {
+ import { VueConstructor } from 'vue'
+
+ interface UViewUIInstall {
+ install: (vue: VueConstructor) => void
+ }
+
+ const uView: UViewUIInstall
+ export default uView
+}
\ No newline at end of file
diff --git a/mobile/src/uni.promisify.adaptor.ts b/mobile/src/uni.promisify.adaptor.ts
new file mode 100644
index 00000000..168d9df0
--- /dev/null
+++ b/mobile/src/uni.promisify.adaptor.ts
@@ -0,0 +1,10 @@
+uni.addInterceptor({
+ returnValue (res) {
+ if (!(!!res && (typeof res === "object" || typeof res === "function") && typeof res.then === "function")) {
+ return res;
+ }
+ return new Promise((resolve, reject) => {
+ res.then((res: [unknown, unknown]) => res[0] ? reject(res[0]) : resolve(res[1]));
+ });
+ },
+});
\ No newline at end of file
diff --git a/mobile/src/uni.scss b/mobile/src/uni.scss
index 69cd1c25..2138bdc7 100644
--- a/mobile/src/uni.scss
+++ b/mobile/src/uni.scss
@@ -12,53 +12,38 @@
* 如果你的项目同样使用了scss预处理,你也可以直接在你的 scss 代码中使用如下变量,同时无需 import 这个文件
*/
-// 引入 uview-plus 样式
-@import 'uview-plus/theme.scss';
+/* 颜色变量 */
+/* uni.scss */
+@import 'uview-ui/theme.scss';
-// 引入 uview-plus 图标字体 - 使用阿里CDN
-@font-face {
- font-family: 'uicon-iconfont';
- src: url('https://at.alicdn.com/t/font_2225171_8kdcwk4po24.ttf') format('truetype');
-}
-
-/* 原有的Sass变量定义 - 保持向后兼容 */
-$uni-color-primary: rgb(93, 135, 255);
-$uni-color-primary-dark: rgb(73, 115, 235);
-$uni-color-primary-light: rgba(93, 135, 255, 0.1);
-
-/* 状态颜色 */
-$uni-color-success: #52c41a;
-$uni-color-warning: #faad14;
-$uni-color-error: #ff4d4f;
-$uni-color-info: #1890ff;
+/* 行为相关颜色 */
+$uni-color-primary: #007aff;
+$uni-color-success: #4cd964;
+$uni-color-warning: #f0ad4e;
+$uni-color-error: #dd524d;
/* 文字基本颜色 */
-$uni-text-color: #333333;
-$uni-text-color-inverse: #fff;
-$uni-text-color-secondary: #666666;
-$uni-text-color-grey: #999999;
-$uni-text-color-placeholder: #c0c0c0;
-$uni-text-color-disable: #d9d9d9;
+$uni-text-color: #333; // 基本色
+$uni-text-color-inverse: #fff; // 反色
+$uni-text-color-grey: #999; // 辅助灰色,如加载更多的提示信息
+$uni-text-color-placeholder: #808080;
+$uni-text-color-disable: #c0c0c0;
/* 背景颜色 */
-$uni-bg-color: #ffffff;
-$uni-bg-color-grey: #f8f9fa;
-$uni-bg-color-hover: #f1f1f1;
-$uni-bg-color-mask: rgba(0, 0, 0, 0.4);
+$uni-bg-color: #fff;
+$uni-bg-color-grey: #f8f8f8;
+$uni-bg-color-hover: #f1f1f1; // 点击状态颜色
+$uni-bg-color-mask: rgba(0, 0, 0, 0.4); // 遮罩颜色
/* 边框颜色 */
-$uni-border-color: #e0e0e0;
+$uni-border-color: #c8c7cc;
-/* 阴影 */
-$uni-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
-$uni-shadow-hover: 0 4px 20px rgba(0, 0, 0, 0.15);
+/* 尺寸变量 */
/* 文字尺寸 */
$uni-font-size-sm: 12px;
$uni-font-size-base: 14px;
$uni-font-size-lg: 16px;
-$uni-font-size-xl: 18px;
-$uni-font-size-xxl: 20px;
/* 图片尺寸 */
$uni-img-size-sm: 20px;
@@ -66,244 +51,28 @@ $uni-img-size-base: 26px;
$uni-img-size-lg: 40px;
/* Border Radius */
-$uni-border-radius-sm: 6px;
-$uni-border-radius-base: 8px;
-$uni-border-radius-lg: 12px;
-$uni-border-radius-xl: 16px;
+$uni-border-radius-sm: 2px;
+$uni-border-radius-base: 3px;
+$uni-border-radius-lg: 6px;
$uni-border-radius-circle: 50%;
-/* 间距 */
-$uni-spacing-row-sm: 8px;
-$uni-spacing-row-base: 12px;
-$uni-spacing-row-lg: 16px;
-$uni-spacing-row-xl: 20px;
+/* 水平间距 */
+$uni-spacing-row-sm: 5px;
+$uni-spacing-row-base: 10px;
+$uni-spacing-row-lg: 15px;
-$uni-spacing-col-sm: 8px;
-$uni-spacing-col-base: 12px;
-$uni-spacing-col-lg: 16px;
-$uni-spacing-col-xl: 20px;
+/* 垂直间距 */
+$uni-spacing-col-sm: 4px;
+$uni-spacing-col-base: 8px;
+$uni-spacing-col-lg: 12px;
/* 透明度 */
-$uni-opacity-disabled: 0.3;
+$uni-opacity-disabled: 0.3; // 组件禁用态的透明度
/* 文章场景相关 */
-$uni-color-title: #2c405a;
+$uni-color-title: #2c405a; // 文章标题颜色
$uni-font-size-title: 20px;
-$uni-color-subtitle: #555;
+$uni-color-subtitle: #555; // 二级标题颜色
$uni-font-size-subtitle: 18px;
-$uni-color-paragraph: #3f536e;
+$uni-color-paragraph: #3f536e; // 文章段落颜色
$uni-font-size-paragraph: 15px;
-
-/* 动画 */
-$uni-transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
-$uni-transition-fast: all 0.15s ease-out;
-
-/* 主题色彩变量 - CSS变量版本 */
-:root {
- --primary-color: rgb(93, 135, 255);
- --primary-light: rgba(93, 135, 255, 0.1);
- --primary-dark: rgb(73, 115, 235);
- --success-color: #52c41a;
- --warning-color: #faad14;
- --error-color: #ff4d4f;
- --text-primary: #262626;
- --text-secondary: #595959;
- --text-disabled: #bfbfbf;
- --border-color: #f0f0f0;
- --background-color: #f8f9fa;
- --white: #ffffff;
- --shadow-light: 0 2px 8px rgba(0, 0, 0, 0.06);
- --shadow-medium: 0 4px 16px rgba(0, 0, 0, 0.12);
- --radius-small: 4px;
- --radius-medium: 8px;
- --radius-large: 12px;
- --spacing-xs: 4px;
- --spacing-sm: 8px;
- --spacing-md: 16px;
- --spacing-lg: 24px;
- --spacing-xl: 32px;
- --transition-fast: 0.2s ease;
- --transition-medium: 0.3s ease;
- --font-size-xs: 12px;
- --font-size-sm: 14px;
- --font-size-md: 16px;
- --font-size-lg: 18px;
- --font-size-xl: 20px;
-}
-
-/* 全局样式重置 */
-* {
- box-sizing: border-box;
- margin: 0;
- padding: 0;
-}
-
-body {
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', 'Helvetica Neue', Helvetica, Arial, sans-serif;
- color: var(--text-primary);
- background-color: var(--background-color);
- line-height: 1.6;
-}
-
-page {
- background-color: var(--background-color);
- color: var(--text-primary);
- font-size: var(--font-size-md);
- line-height: 1.6;
-}
-
-/* 通用工具类 */
-.flex {
- display: flex;
-}
-
-.flex-center {
- display: flex;
- align-items: center;
- justify-content: center;
-}
-
-.flex-between {
- display: flex;
- align-items: center;
- justify-content: space-between;
-}
-
-.flex-column {
- display: flex;
- flex-direction: column;
-}
-
-.text-center {
- text-align: center;
-}
-
-.text-primary {
- color: var(--primary-color);
-}
-
-.text-secondary {
- color: var(--text-secondary);
-}
-
-.bg-primary {
- background-color: var(--primary-color);
-}
-
-.bg-white {
- background-color: var(--white);
-}
-
-.rounded {
- border-radius: var(--radius-medium);
-}
-
-.shadow {
- box-shadow: var(--shadow-light);
-}
-
-.mt-sm { margin-top: var(--spacing-sm); }
-.mt-md { margin-top: var(--spacing-md); }
-.mt-lg { margin-top: var(--spacing-lg); }
-.mb-sm { margin-bottom: var(--spacing-sm); }
-.mb-md { margin-bottom: var(--spacing-md); }
-.mb-lg { margin-bottom: var(--spacing-lg); }
-.ml-sm { margin-left: var(--spacing-sm); }
-.ml-md { margin-left: var(--spacing-md); }
-.mr-sm { margin-right: var(--spacing-sm); }
-.mr-md { margin-right: var(--spacing-md); }
-
-.p-sm { padding: var(--spacing-sm); }
-.p-md { padding: var(--spacing-md); }
-.p-lg { padding: var(--spacing-lg); }
-.px-sm { padding-left: var(--spacing-sm); padding-right: var(--spacing-sm); }
-.px-md { padding-left: var(--spacing-md); padding-right: var(--spacing-md); }
-.py-sm { padding-top: var(--spacing-sm); padding-bottom: var(--spacing-sm); }
-.py-md { padding-top: var(--spacing-md); padding-bottom: var(--spacing-md); }
-
-/* 按钮基础样式 */
-.btn {
- padding: var(--spacing-sm) var(--spacing-md);
- border-radius: var(--radius-medium);
- border: none;
- font-size: var(--font-size-md);
- cursor: pointer;
- transition: var(--transition-fast);
- display: inline-flex;
- align-items: center;
- justify-content: center;
- text-decoration: none;
-}
-
-.btn-primary {
- background-color: var(--primary-color);
- color: var(--white);
-}
-
-.btn-primary:hover {
- background-color: var(--primary-dark);
-}
-
-/* 输入框样式 */
-.input {
- padding: var(--spacing-sm) var(--spacing-md);
- border: 1px solid var(--border-color);
- border-radius: var(--radius-medium);
- font-size: var(--font-size-md);
- transition: var(--transition-fast);
-}
-
-.input:focus {
- border-color: var(--primary-color);
- outline: none;
- box-shadow: 0 0 0 2px rgba(93, 135, 255, 0.2);
-}
-
-/* 卡片样式 */
-.card {
- background: var(--white);
- border-radius: var(--radius-large);
- box-shadow: var(--shadow-light);
- padding: var(--spacing-md);
- margin-bottom: var(--spacing-md);
-}
-
-.card-header {
- font-size: var(--font-size-lg);
- font-weight: 600;
- margin-bottom: var(--spacing-md);
- color: var(--text-primary);
-}
-
-/* 动画类 */
-.fade-in {
- animation: fadeIn var(--transition-medium);
-}
-
-.slide-up {
- animation: slideUp var(--transition-medium);
-}
-
-@keyframes fadeIn {
- from { opacity: 0; }
- to { opacity: 1; }
-}
-
-@keyframes slideUp {
- from {
- opacity: 0;
- transform: translateY(20px);
- }
- to {
- opacity: 1;
- transform: translateY(0);
- }
-}
-
-/* 响应式断点 */
-@media (max-width: 768px) {
- .card {
- margin: var(--spacing-sm);
- padding: var(--spacing-sm);
- }
-}
\ No newline at end of file
diff --git a/mobile/src/utils/README.md b/mobile/src/utils/README.md
deleted file mode 100644
index 04885da7..00000000
--- a/mobile/src/utils/README.md
+++ /dev/null
@@ -1,378 +0,0 @@
-# 移动端工具库使用说明
-
-本文档说明移动端 `utils` 目录下各个工具的使用方法,特别是升级后的请求工具。
-
-## 📁 文件结构
-
-```
-mobile/src/utils/
-├── request.js # 统一请求工具(已升级)
-├── crypto.js # 加密解密工具(新增)
-├── config.js # 配置管理工具(新增)
-├── encrypt-pure.js # 登录专用加密工具(保留)
-├── encrypt-*.js # 其他加密实现(保留)
-└── README.md # 本文档
-```
-
-## 🚀 核心升级功能
-
-### 1. 统一的加密解密支持
-
-移动端现在与 Web 端保持一致,支持:
-- ✅ **入参加密**:通过 `isEncrypt: 'true'` 头部控制
-- ✅ **出参解密**:自动处理后端返回的加密数据
-- ✅ **AES + RSA 双重加密**:与后端完全兼容
-- ✅ **防重复提交**:避免用户误操作
-- ✅ **错误处理优化**:404静默处理、401自动跳转
-
-### 2. 可配置的参数管理
-
-所有关键参数都可以通过配置文件统一管理,方便调试和部署。
-
-## 📖 使用方法
-
-### 基础请求
-
-```javascript
-import { request } from '@/utils/request.js';
-
-// 普通GET请求
-const response = await request({
- url: '/api/user/list',
- method: 'GET'
-});
-
-// 普通POST请求
-const response = await request({
- url: '/api/user/create',
- method: 'POST',
- data: {
- name: '张三',
- age: 25
- }
-});
-```
-
-### 加密请求
-
-```javascript
-import { request } from '@/utils/request.js';
-
-// 启用加密的POST请求
-const response = await request({
- url: '/api/sensitive/data',
- method: 'POST',
- data: {
- sensitiveInfo: '敏感数据'
- },
- header: {
- isEncrypt: 'true' // 关键:启用加密
- }
-});
-```
-
-### 免Token请求
-
-```javascript
-import { request } from '@/utils/request.js';
-
-// 不需要Token的请求(如获取租户列表)
-const response = await request({
- url: '/auth/tenant/list',
- method: 'GET',
- header: {
- isToken: false // 关键:不携带Token
- }
-});
-```
-
-### 允许重复提交
-
-```javascript
-import { request } from '@/utils/request.js';
-
-// 允许重复提交的请求
-const response = await request({
- url: '/api/data/sync',
- method: 'POST',
- data: { syncData: 'xxx' },
- header: {
- repeatSubmit: false // 关键:允许重复提交
- }
-});
-```
-
-## ⚙️ 配置管理
-
-### 基础配置更新
-
-```javascript
-import { updateConfig } from '@/utils/config.js';
-
-// 更新API基础URL
-updateConfig({
- request: {
- baseURL: {
- production: 'https://new-api-domain.com'
- }
- }
-});
-
-// 更新客户端ID
-updateConfig({
- app: {
- clientId: 'new-client-id-here'
- }
-});
-```
-
-### 加密配置更新
-
-```javascript
-import { updateConfig } from '@/utils/config.js';
-
-// 更新RSA公钥
-updateConfig({
- encryption: {
- rsaPublicKey: 'new-rsa-public-key-here'
- }
-});
-
-// 临时禁用加密(调试用)
-updateConfig({
- encryption: {
- enabled: false
- }
-});
-```
-
-### 预设配置方案
-
-```javascript
-import { applyPreset } from '@/utils/config.js';
-
-// 应用生产环境配置
-applyPreset('production');
-
-// 应用测试环境配置
-applyPreset('test');
-
-// 应用开发环境配置
-applyPreset('development');
-```
-
-### 单个配置项更新
-
-```javascript
-import { setConfigItem, getConfigItem } from '@/utils/config.js';
-
-// 设置超时时间
-setConfigItem('request.timeout', 600000); // 10分钟
-
-// 获取当前客户端ID
-const clientId = getConfigItem('app.clientId');
-
-// 检查是否启用加密
-const isEncrypted = getConfigItem('encryption.enabled');
-```
-
-## 🔐 加密工具直接使用
-
-```javascript
-import {
- encryptWithAES,
- decryptWithAES,
- generateRandomAESKey,
- encrypt,
- decrypt
-} from '@/utils/crypto.js';
-
-// AES加密解密
-const aesKey = generateRandomAESKey();
-const encryptedData = encryptWithAES('敏感数据', aesKey);
-const decryptedData = decryptWithAES(encryptedData, aesKey);
-
-// RSA加密解密
-const encryptedKey = encrypt('AES密钥');
-const decryptedKey = decrypt(encryptedKey);
-```
-
-## 📦 API方法封装
-
-### 登录
-
-```javascript
-import { api } from '@/utils/request.js';
-
-// 用户登录(自动加密)
-const loginResult = await api.login({
- username: 'admin',
- password: 'password123',
- tenantId: '000000'
-});
-```
-
-### 获取用户信息
-
-```javascript
-import { api } from '@/utils/request.js';
-
-// 获取当前用户信息
-const userInfo = await api.getUserInfo();
-```
-
-### 注销登录
-
-```javascript
-import { api } from '@/utils/request.js';
-
-// 用户注销
-await api.logout();
-```
-
-## 💾 缓存工具
-
-### 用户信息缓存
-
-```javascript
-import { userStorage } from '@/utils/request.js';
-
-// 保存登录信息
-userStorage.saveLoginInfo(loginResult, userInfo);
-
-// 获取用户信息
-const user = userStorage.getUserInfo();
-
-// 检查登录状态
-const isLoggedIn = userStorage.isLoggedIn();
-
-// 清除登录信息
-userStorage.clearLoginInfo();
-```
-
-### 通用缓存
-
-```javascript
-import { storage } from '@/utils/request.js';
-
-// 设置缓存(30分钟过期)
-storage.set('cacheKey', { data: 'value' }, 1800);
-
-// 获取缓存
-const data = storage.get('cacheKey');
-
-// 删除缓存
-storage.remove('cacheKey');
-
-// 清空所有缓存
-storage.clear();
-```
-
-## 📥 文件下载
-
-```javascript
-import { download } from '@/utils/request.js';
-
-// 下载文件
-await download('/api/export/users', {
- filters: { status: 'active' }
-}, 'users.xlsx');
-```
-
-## 🔧 调试和日志
-
-### 开启详细日志
-
-```javascript
-import { updateConfig } from '@/utils/config.js';
-
-// 开启所有调试日志
-updateConfig({
- debug: {
- enableVerboseLog: true,
- showRequestLog: true,
- showResponseLog: true,
- showCryptoLog: true
- }
-});
-```
-
-### 错误处理配置
-
-```javascript
-import { updateConfig } from '@/utils/config.js';
-
-// 自定义错误处理
-updateConfig({
- error: {
- silentCodes: [404, 403], // 静默处理的状态码
- toastDuration: 3000, // 错误提示显示时间
- loginRedirectDelay: 1000 // 跳转登录延迟
- }
-});
-```
-
-## 🚨 注意事项
-
-### 1. 加密兼容性
-- 移动端加密算法与 Web 端完全一致
-- RSA 公钥必须与后端保持同步
-- 加密只对 POST/PUT 请求生效
-
-### 2. 配置优先级
-- 运行时配置 > 环境预设 > 默认配置
-- 配置更新会立即生效,无需重启应用
-
-### 3. Token 管理
-- Token 自动从本地存储获取
-- 401 错误会自动清除 Token 并跳转登录
-- 支持 Token 过期时间管理
-
-### 4. 错误处理
-- 404 错误默认静默处理
-- 403 错误会显示提示但不跳转
-- 网络错误会显示友好提示
-
-## 📊 性能优化
-
-### 1. 防重复提交
-- 默认 500ms 间隔防重复提交
-- 可通过配置调整间隔时间
-- 支持单个请求禁用防重复
-
-### 2. 请求超时
-- 普通请求:5分钟超时
-- 下载请求:20分钟超时
-- 可通过配置调整超时时间
-
-### 3. 缓存策略
-- 会话级缓存用于防重复提交
-- 持久化缓存支持过期时间
-- 登录信息缓存支持时间戳
-
-## 🔗 与 Web 端对比
-
-| 功能 | Web 端 | 移动端 | 说明 |
-|------|--------|--------|------|
-| 入参加密 | ✅ | ✅ | 完全一致 |
-| 出参解密 | ✅ | ✅ | 完全一致 |
-| 防重复提交 | ✅ | ✅ | 完全一致 |
-| 401处理 | ✅ | ✅ | 跳转方式略有不同 |
-| 404静默 | ✅ | ✅ | 完全一致 |
-| 下载支持 | ✅ | ✅ | 平台实现不同 |
-| 配置管理 | 环境变量 | 配置文件 | 更灵活的配置方式 |
-
-## 🎯 最佳实践
-
-1. **生产环境**:关闭调试日志,启用加密
-2. **开发环境**:开启详细日志,保持加密测试
-3. **配置管理**:使用预设配置,避免硬编码
-4. **错误处理**:合理设置静默错误码
-5. **性能优化**:适当设置超时和缓存策略
-
----
-
-**📝 更新日志**
-- 2025-01-07:完成移动端请求工具升级,与 Web 端功能对齐
-- 支持入参加密、出参解密、防重复提交等完整功能
-- 新增配置管理系统,提升可维护性
\ No newline at end of file
diff --git a/mobile/src/utils/accessibility.js b/mobile/src/utils/accessibility.js
deleted file mode 100644
index ae80ddfd..00000000
--- a/mobile/src/utils/accessibility.js
+++ /dev/null
@@ -1,774 +0,0 @@
-/**
- * 无障碍访问工具类
- * 提供屏幕阅读器支持、键盘导航、高对比度模式等功能
- */
-
-class AccessibilityUtils {
- constructor() {
- this.isHighContrast = false;
- this.isLargeText = false;
- this.isScreenReaderMode = false;
- this.focusVisible = true;
- this.currentFocusIndex = 0;
- this.focusableElements = [];
-
- this.init();
- }
-
- /**
- * 初始化无障碍功能
- */
- init() {
- this.setupKeyboardNavigation();
- this.setupFocusManagement();
- this.setupScreenReaderSupport();
- this.loadAccessibilitySettings();
- }
-
- /**
- * 设置键盘导航
- */
- setupKeyboardNavigation() {
- document.addEventListener('keydown', (e) => {
- switch (e.key) {
- case 'Tab':
- this.handleTabNavigation(e);
- break;
- case 'Enter':
- case ' ':
- this.handleActivation(e);
- break;
- case 'Escape':
- this.handleEscape(e);
- break;
- case 'ArrowUp':
- case 'ArrowDown':
- case 'ArrowLeft':
- case 'ArrowRight':
- this.handleArrowNavigation(e);
- break;
- }
- });
-
- // 检测键盘使用,显示焦点指示器
- document.addEventListener('keydown', () => {
- this.focusVisible = true;
- document.body.classList.add('keyboard-navigation');
- });
-
- // 检测鼠标使用,隐藏焦点指示器
- document.addEventListener('mousedown', () => {
- this.focusVisible = false;
- document.body.classList.remove('keyboard-navigation');
- });
- }
-
- /**
- * 处理Tab导航
- * @param {KeyboardEvent} e - 键盘事件
- */
- handleTabNavigation(e) {
- this.updateFocusableElements();
-
- if (this.focusableElements.length === 0) return;
-
- if (e.shiftKey) {
- // Shift + Tab - 反向导航
- this.currentFocusIndex =
- this.currentFocusIndex <= 0
- ? this.focusableElements.length - 1
- : this.currentFocusIndex - 1;
- } else {
- // Tab - 正向导航
- this.currentFocusIndex =
- this.currentFocusIndex >= this.focusableElements.length - 1
- ? 0
- : this.currentFocusIndex + 1;
- }
-
- this.focusableElements[this.currentFocusIndex]?.focus();
- e.preventDefault();
- }
-
- /**
- * 处理激活操作
- * @param {KeyboardEvent} e - 键盘事件
- */
- handleActivation(e) {
- const activeElement = document.activeElement;
- if (activeElement && activeElement.click) {
- activeElement.click();
- e.preventDefault();
- }
- }
-
- /**
- * 处理Escape键
- * @param {KeyboardEvent} e - 键盘事件
- */
- handleEscape(e) {
- // 关闭模态框、菜单等
- const modal = document.querySelector('.modal:not([style*="display: none"])');
- const menu = document.querySelector('.dropdown-menu.show, .longpress-menu');
-
- if (modal) {
- this.closeModal(modal);
- } else if (menu) {
- menu.remove();
- }
- }
-
- /**
- * 处理方向键导航
- * @param {KeyboardEvent} e - 键盘事件
- */
- handleArrowNavigation(e) {
- const currentElement = document.activeElement;
- const container = currentElement.closest('.grid-layout, .menu-section, .stats-section');
-
- if (!container) return;
-
- const focusableInContainer = this.getFocusableElements(container);
- const currentIndex = focusableInContainer.indexOf(currentElement);
-
- if (currentIndex === -1) return;
-
- let nextIndex = currentIndex;
- const containerRect = container.getBoundingClientRect();
- const currentRect = currentElement.getBoundingClientRect();
-
- switch (e.key) {
- case 'ArrowUp':
- nextIndex = this.findElementAbove(focusableInContainer, currentIndex, currentRect);
- break;
- case 'ArrowDown':
- nextIndex = this.findElementBelow(focusableInContainer, currentIndex, currentRect);
- break;
- case 'ArrowLeft':
- nextIndex = Math.max(0, currentIndex - 1);
- break;
- case 'ArrowRight':
- nextIndex = Math.min(focusableInContainer.length - 1, currentIndex + 1);
- break;
- }
-
- if (nextIndex !== currentIndex) {
- focusableInContainer[nextIndex]?.focus();
- e.preventDefault();
- }
- }
-
- /**
- * 查找上方元素
- */
- findElementAbove(elements, currentIndex, currentRect) {
- let bestIndex = currentIndex;
- let minDistance = Infinity;
-
- for (let i = 0; i < elements.length; i++) {
- if (i === currentIndex) continue;
-
- const rect = elements[i].getBoundingClientRect();
- if (rect.bottom <= currentRect.top) {
- const distance =
- Math.abs(rect.left - currentRect.left) + (currentRect.top - rect.bottom);
- if (distance < minDistance) {
- minDistance = distance;
- bestIndex = i;
- }
- }
- }
-
- return bestIndex;
- }
-
- /**
- * 查找下方元素
- */
- findElementBelow(elements, currentIndex, currentRect) {
- let bestIndex = currentIndex;
- let minDistance = Infinity;
-
- for (let i = 0; i < elements.length; i++) {
- if (i === currentIndex) continue;
-
- const rect = elements[i].getBoundingClientRect();
- if (rect.top >= currentRect.bottom) {
- const distance =
- Math.abs(rect.left - currentRect.left) + (rect.top - currentRect.bottom);
- if (distance < minDistance) {
- minDistance = distance;
- bestIndex = i;
- }
- }
- }
-
- return bestIndex;
- }
-
- /**
- * 设置焦点管理
- */
- setupFocusManagement() {
- // 焦点指示器样式
- const focusStyles = `
- .keyboard-navigation *:focus {
- outline: 2px solid rgb(93, 135, 255) !important;
- outline-offset: 2px !important;
- }
-
- .keyboard-navigation .touchable:focus {
- outline: 2px solid rgb(93, 135, 255) !important;
- outline-offset: 2px !important;
- box-shadow: 0 0 0 4px rgba(93, 135, 255, 0.2) !important;
- }
- `;
-
- const styleSheet = document.createElement('style');
- styleSheet.textContent = focusStyles;
- document.head.appendChild(styleSheet);
-
- // 焦点陷阱(用于模态框)
- this.setupFocusTrap();
- }
-
- /**
- * 设置焦点陷阱
- */
- setupFocusTrap() {
- document.addEventListener('focusin', (e) => {
- const modal = document.querySelector('.modal:not([style*="display: none"])');
- if (modal && !modal.contains(e.target)) {
- const firstFocusable = this.getFocusableElements(modal)[0];
- firstFocusable?.focus();
- }
- });
- }
-
- /**
- * 更新可聚焦元素列表
- */
- updateFocusableElements() {
- this.focusableElements = this.getFocusableElements(document);
- }
-
- /**
- * 获取可聚焦元素
- * @param {Element} container - 容器元素
- * @returns {Element[]} 可聚焦元素数组
- */
- getFocusableElements(container = document) {
- const focusableSelectors = [
- 'button:not([disabled])',
- '[href]',
- 'input:not([disabled])',
- 'select:not([disabled])',
- 'textarea:not([disabled])',
- '[tabindex]:not([tabindex="-1"])',
- '.touchable',
- '.service-item',
- '.menu-item',
- '.stat-card',
- ].join(', ');
-
- return Array.from(container.querySelectorAll(focusableSelectors)).filter((el) => {
- return (
- el.offsetParent !== null &&
- !el.hasAttribute('disabled') &&
- !el.classList.contains('sr-only')
- );
- });
- }
-
- /**
- * 设置屏幕阅读器支持
- */
- setupScreenReaderSupport() {
- // 动态添加ARIA标签
- this.addAriaLabels();
-
- // 实时区域更新
- this.setupLiveRegions();
-
- // 页面标题更新
- this.setupPageTitleUpdates();
- }
-
- /**
- * 添加ARIA标签
- */
- addAriaLabels() {
- // 为服务项添加描述
- document.querySelectorAll('.service-item').forEach((item) => {
- const title = item.querySelector('.service-title')?.textContent;
- if (title && !item.hasAttribute('aria-label')) {
- item.setAttribute('aria-label', `${title}服务`);
- item.setAttribute('role', 'button');
- item.setAttribute('tabindex', '0');
- }
- });
-
- // 为统计卡片添加描述
- document.querySelectorAll('.stat-card').forEach((card) => {
- const number = card.querySelector('.stat-number')?.textContent;
- const label = card.querySelector('.stat-label')?.textContent;
- if (number && label && !card.hasAttribute('aria-label')) {
- card.setAttribute('aria-label', `${label}:${number}`);
- card.setAttribute('role', 'button');
- card.setAttribute('tabindex', '0');
- }
- });
-
- // 为导航按钮添加描述
- document.querySelectorAll('.nav-tab').forEach((tab) => {
- const label = tab.querySelector('.nav-label')?.textContent;
- if (label && !tab.hasAttribute('aria-label')) {
- tab.setAttribute('aria-label', `导航到${label}页面`);
- }
- });
- }
-
- /**
- * 设置实时区域
- */
- setupLiveRegions() {
- // 创建实时区域用于通知
- const liveRegion = document.createElement('div');
- liveRegion.setAttribute('aria-live', 'polite');
- liveRegion.setAttribute('aria-atomic', 'true');
- liveRegion.className = 'sr-only';
- liveRegion.id = 'live-region';
- document.body.appendChild(liveRegion);
-
- // 监听toast消息
- const originalShowToast = uni.showToast;
- uni.showToast = (options) => {
- this.announceToScreenReader(options.title || options.content);
- return originalShowToast(options);
- };
- }
-
- /**
- * 向屏幕阅读器宣布消息
- * @param {string} message - 消息内容
- */
- announceToScreenReader(message) {
- const liveRegion = document.getElementById('live-region');
- if (liveRegion) {
- liveRegion.textContent = message;
-
- // 清空内容以便下次宣布
- setTimeout(() => {
- liveRegion.textContent = '';
- }, 1000);
- }
- }
-
- /**
- * 设置页面标题更新
- */
- setupPageTitleUpdates() {
- // 监听路由变化,更新页面标题
- const originalSwitchTab = uni.switchTab;
- uni.switchTab = (options) => {
- const pageTitles = {
- '/pages/index/index': '智慧校园 - 首页',
- };
-
- const title = pageTitles[options.url];
- if (title) {
- document.title = title;
- this.announceToScreenReader(`已切换到${title.split(' - ')[1]}页面`);
- }
-
- return originalSwitchTab(options);
- };
- }
-
- /**
- * 启用高对比度模式
- */
- enableHighContrast() {
- this.isHighContrast = true;
- document.body.classList.add('high-contrast');
- this.saveAccessibilitySettings();
- this.announceToScreenReader('已启用高对比度模式');
- }
-
- /**
- * 禁用高对比度模式
- */
- disableHighContrast() {
- this.isHighContrast = false;
- document.body.classList.remove('high-contrast');
- this.saveAccessibilitySettings();
- this.announceToScreenReader('已禁用高对比度模式');
- }
-
- /**
- * 启用大字体模式
- */
- enableLargeText() {
- this.isLargeText = true;
- document.body.classList.add('large-text');
- this.saveAccessibilitySettings();
- this.announceToScreenReader('已启用大字体模式');
- }
-
- /**
- * 禁用大字体模式
- */
- disableLargeText() {
- this.isLargeText = false;
- document.body.classList.remove('large-text');
- this.saveAccessibilitySettings();
- this.announceToScreenReader('已禁用大字体模式');
- }
-
- /**
- * 启用屏幕阅读器模式
- */
- enableScreenReaderMode() {
- this.isScreenReaderMode = true;
- document.body.classList.add('screen-reader-mode');
-
- // 隐藏装饰性动画
- const style = document.createElement('style');
- style.id = 'screen-reader-styles';
- style.textContent = `
- .screen-reader-mode * {
- animation: none !important;
- transition: none !important;
- }
- .screen-reader-mode .decorative {
- display: none !important;
- }
- `;
- document.head.appendChild(style);
-
- this.saveAccessibilitySettings();
- this.announceToScreenReader('已启用屏幕阅读器优化模式');
- }
-
- /**
- * 禁用屏幕阅读器模式
- */
- disableScreenReaderMode() {
- this.isScreenReaderMode = false;
- document.body.classList.remove('screen-reader-mode');
-
- const style = document.getElementById('screen-reader-styles');
- if (style) style.remove();
-
- this.saveAccessibilitySettings();
- this.announceToScreenReader('已禁用屏幕阅读器优化模式');
- }
-
- /**
- * 保存无障碍设置
- */
- saveAccessibilitySettings() {
- const settings = {
- highContrast: this.isHighContrast,
- largeText: this.isLargeText,
- screenReaderMode: this.isScreenReaderMode,
- };
-
- uni.setStorageSync('accessibility-settings', settings);
- }
-
- /**
- * 加载无障碍设置
- */
- loadAccessibilitySettings() {
- try {
- const settings = uni.getStorageSync('accessibility-settings');
- if (settings) {
- if (settings.highContrast) this.enableHighContrast();
- if (settings.largeText) this.enableLargeText();
- if (settings.screenReaderMode) this.enableScreenReaderMode();
- }
- } catch (e) {
- console.warn('Failed to load accessibility settings:', e);
- }
- }
-
- /**
- * 添加语义化结构
- */
- addSemanticStructure() {
- // 添加主要地标
- const main = document.querySelector('.main-content');
- if (main && !main.hasAttribute('role')) {
- main.setAttribute('role', 'main');
- main.setAttribute('aria-label', '主要内容');
- }
-
- // 添加导航地标
- const nav = document.querySelector('.bottom-nav');
- if (nav && !nav.hasAttribute('role')) {
- nav.setAttribute('role', 'navigation');
- nav.setAttribute('aria-label', '主导航');
- }
-
- // 添加区域标识
- document.querySelectorAll('.card').forEach((card, index) => {
- if (!card.hasAttribute('role')) {
- card.setAttribute('role', 'region');
- const title = card.querySelector('.card-title')?.textContent;
- if (title) {
- card.setAttribute('aria-labelledby', `card-title-${index}`);
- const titleElement = card.querySelector('.card-title');
- if (titleElement) {
- titleElement.id = `card-title-${index}`;
- }
- }
- }
- });
- }
-
- /**
- * 关闭模态框
- * @param {Element} modal - 模态框元素
- */
- closeModal(modal) {
- modal.style.display = 'none';
-
- // 恢复焦点到触发元素
- const trigger = document.querySelector('[data-modal-trigger]');
- if (trigger) {
- trigger.focus();
- }
- }
-}
-
-/**
- * 响应式设计工具类
- */
-class ResponsiveUtils {
- constructor() {
- this.breakpoints = {
- mobile: 375,
- tablet: 768,
- desktop: 1024,
- };
-
- this.currentBreakpoint = this.getCurrentBreakpoint();
- this.init();
- }
-
- /**
- * 初始化响应式功能
- */
- init() {
- this.setupViewportMeta();
- this.setupMediaQueries();
- this.setupOrientationChange();
- this.addResponsiveClasses();
- }
-
- /**
- * 设置viewport元标签
- */
- setupViewportMeta() {
- let viewport = document.querySelector('meta[name="viewport"]');
- if (!viewport) {
- viewport = document.createElement('meta');
- viewport.name = 'viewport';
- document.head.appendChild(viewport);
- }
- viewport.content =
- 'width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no';
- }
-
- /**
- * 设置媒体查询监听
- */
- setupMediaQueries() {
- Object.entries(this.breakpoints).forEach(([name, width]) => {
- const mediaQuery = window.matchMedia(`(max-width: ${width}px)`);
-
- mediaQuery.addListener((e) => {
- this.handleBreakpointChange();
- });
- });
-
- window.addEventListener('resize', () => {
- this.handleBreakpointChange();
- });
- }
-
- /**
- * 处理断点变化
- */
- handleBreakpointChange() {
- const newBreakpoint = this.getCurrentBreakpoint();
-
- if (newBreakpoint !== this.currentBreakpoint) {
- this.currentBreakpoint = newBreakpoint;
- this.addResponsiveClasses();
-
- // 触发自定义事件
- const event = new CustomEvent('breakpointChange', {
- detail: { breakpoint: newBreakpoint },
- });
- window.dispatchEvent(event);
- }
- }
-
- /**
- * 获取当前断点
- */
- getCurrentBreakpoint() {
- const width = window.innerWidth;
-
- if (width <= this.breakpoints.mobile) return 'mobile';
- if (width <= this.breakpoints.tablet) return 'tablet';
- return 'desktop';
- }
-
- /**
- * 添加响应式类名
- */
- addResponsiveClasses() {
- document.body.className = document.body.className
- .replace(/\b(mobile|tablet|desktop)\b/g, '')
- .trim();
-
- document.body.classList.add(this.currentBreakpoint);
- }
-
- /**
- * 设置屏幕方向变化监听
- */
- setupOrientationChange() {
- window.addEventListener('orientationchange', () => {
- setTimeout(() => {
- this.handleBreakpointChange();
-
- // 修复iOS Safari的viewport问题
- if (/iPhone|iPad/.test(navigator.userAgent)) {
- document.body.style.height = window.innerHeight + 'px';
- }
- }, 100);
- });
- }
-
- /**
- * 获取安全区域内边距
- */
- getSafeAreaInsets() {
- const computedStyle = getComputedStyle(document.documentElement);
-
- return {
- top: computedStyle.getPropertyValue('env(safe-area-inset-top)') || '0px',
- right: computedStyle.getPropertyValue('env(safe-area-inset-right)') || '0px',
- bottom: computedStyle.getPropertyValue('env(safe-area-inset-bottom)') || '0px',
- left: computedStyle.getPropertyValue('env(safe-area-inset-left)') || '0px',
- };
- }
-
- /**
- * 是否为移动设备
- */
- isMobile() {
- return this.currentBreakpoint === 'mobile';
- }
-
- /**
- * 是否为平板设备
- */
- isTablet() {
- return this.currentBreakpoint === 'tablet';
- }
-
- /**
- * 是否为桌面设备
- */
- isDesktop() {
- return this.currentBreakpoint === 'desktop';
- }
-}
-
-// 创建全局实例
-const accessibilityUtils = new AccessibilityUtils();
-const responsiveUtils = new ResponsiveUtils();
-
-// 添加全局样式
-const globalStyles = `
-/* 高对比度模式 */
-.high-contrast {
- --uni-color-primary: #000000;
- --uni-bg-color: #ffffff;
- --uni-text-color: #000000;
- --uni-text-color-secondary: #333333;
- --uni-border-color: #000000;
-}
-
-.high-contrast .card {
- border: 2px solid #000000;
- background: #ffffff;
-}
-
-/* 大字体模式 */
-.large-text {
- font-size: 120%;
-}
-
-.large-text .card-title {
- font-size: 120%;
-}
-
-.large-text .service-title {
- font-size: 120%;
-}
-
-/* 屏幕阅读器模式 */
-.screen-reader-mode .decorative {
- display: none;
-}
-
-/* 键盘导航焦点样式 */
-.keyboard-navigation *:focus {
- outline: 2px solid rgb(93, 135, 255);
- outline-offset: 2px;
-}
-
-/* 响应式调整 */
-@media (max-width: 375px) {
- .mobile .grid-4 {
- grid-template-columns: repeat(3, 1fr);
- }
-
- .mobile .service-item {
- padding: 12px;
- }
-
- .mobile .card {
- margin: 8px;
- }
-}
-
-@media (max-width: 320px) {
- .mobile .grid-4,
- .mobile .grid-3 {
- grid-template-columns: 1fr 1fr;
- }
-}
-
-/* 安全区域适配 */
-.page-container {
- padding-top: env(safe-area-inset-top);
- padding-bottom: env(safe-area-inset-bottom);
- padding-left: env(safe-area-inset-left);
- padding-right: env(safe-area-inset-right);
-}
-`;
-
-// 插入全局样式
-const globalStyleSheet = document.createElement('style');
-globalStyleSheet.textContent = globalStyles;
-document.head.appendChild(globalStyleSheet);
-
-export { AccessibilityUtils, ResponsiveUtils };
-export default { accessibilityUtils, responsiveUtils };
diff --git a/mobile/src/utils/animations.js b/mobile/src/utils/animations.js
deleted file mode 100644
index 6ad242bf..00000000
--- a/mobile/src/utils/animations.js
+++ /dev/null
@@ -1,422 +0,0 @@
-/**
- * 动画工具类
- * 提供页面转场、组件动画和微交互效果的统一管理
- */
-
-class AnimationUtils {
- constructor() {
- this.animationDuration = 300;
- this.easingFunction = 'cubic-bezier(0.4, 0, 0.2, 1)';
- }
-
- /**
- * 页面进入动画
- * @param {string} selector - 选择器
- * @param {string} type - 动画类型
- */
- pageEnter(selector = '.page-container', type = 'fadeInUp') {
- const element = document.querySelector(selector);
- if (element) {
- element.style.animation = `${type} 0.6s ease-out`;
- }
- }
-
- /**
- * 页面退出动画
- * @param {string} selector - 选择器
- * @param {Function} callback - 回调函数
- */
- pageExit(selector = '.page-container', callback) {
- const element = document.querySelector(selector);
- if (element) {
- element.style.animation = 'fadeOutDown 0.3s ease-in forwards';
- setTimeout(() => {
- if (callback) callback();
- }, 300);
- }
- }
-
- /**
- * 卡片进入动画(错开显示)
- * @param {string} selector - 选择器
- */
- staggerCards(selector = '.card, .service-item, .stat-card') {
- const elements = document.querySelectorAll(selector);
- elements.forEach((element, index) => {
- element.style.animationDelay = `${index * 0.1}s`;
- element.style.animation = 'slideInUp 0.6s ease-out backwards';
- });
- }
-
- /**
- * 列表项动画
- * @param {string} selector - 选择器
- */
- animateList(selector = '.menu-item, .todo-item, .news-item') {
- const elements = document.querySelectorAll(selector);
- elements.forEach((element, index) => {
- element.style.animationDelay = `${index * 0.05}s`;
- element.style.animation = 'slideInRight 0.4s ease-out backwards';
- });
- }
-
- /**
- * 点击反馈动画
- * @param {HTMLElement} element - 元素
- */
- clickFeedback(element) {
- if (!element) return;
-
- element.style.transform = 'scale(0.95)';
- element.style.transition = 'transform 0.1s ease-out';
-
- setTimeout(() => {
- element.style.transform = 'scale(1)';
- }, 100);
- }
-
- /**
- * 加载动画
- * @param {string} selector - 选择器
- */
- showLoading(selector = 'body') {
- const loadingHtml = `
-
- `;
-
- const container = document.querySelector(selector);
- if (container) {
- container.insertAdjacentHTML('beforeend', loadingHtml);
- }
- }
-
- /**
- * 隐藏加载动画
- */
- hideLoading() {
- const loading = document.querySelector('.loading-overlay');
- if (loading) {
- loading.style.animation = 'fadeOut 0.3s ease-in forwards';
- setTimeout(() => {
- loading.remove();
- }, 300);
- }
- }
-
- /**
- * Toast 提示动画
- * @param {string} message - 消息内容
- * @param {number} duration - 持续时间
- */
- showToast(message, duration = 2000) {
- // 移除已存在的toast
- const existingToast = document.querySelector('.animated-toast');
- if (existingToast) {
- existingToast.remove();
- }
-
- const toast = document.createElement('div');
- toast.className = 'animated-toast';
- toast.textContent = message;
- toast.style.cssText = `
- position: fixed;
- top: 20px;
- left: 50%;
- transform: translateX(-50%);
- background: rgba(0, 0, 0, 0.8);
- color: white;
- padding: 12px 24px;
- border-radius: 25px;
- z-index: 10000;
- font-size: 14px;
- max-width: 280px;
- text-align: center;
- animation: slideInDown 0.3s ease-out;
- `;
-
- document.body.appendChild(toast);
-
- // 自动移除
- setTimeout(() => {
- toast.style.animation = 'slideOutUp 0.3s ease-in forwards';
- setTimeout(() => {
- if (toast.parentNode) {
- toast.remove();
- }
- }, 300);
- }, duration);
- }
-
- /**
- * 统计数字计数动画
- * @param {HTMLElement} element - 元素
- * @param {number} endValue - 结束值
- * @param {number} duration - 持续时间
- */
- countUp(element, endValue, duration = 1000) {
- if (!element) return;
-
- const startValue = 0;
- const startTime = Date.now();
-
- const animate = () => {
- const currentTime = Date.now();
- const elapsed = currentTime - startTime;
- const progress = Math.min(elapsed / duration, 1);
-
- // 使用缓动函数
- const easeProgress = 1 - Math.pow(1 - progress, 3);
- const currentValue = startValue + (endValue - startValue) * easeProgress;
-
- // 格式化显示
- if (endValue % 1 === 0) {
- element.textContent = Math.round(currentValue);
- } else {
- element.textContent = currentValue.toFixed(1);
- }
-
- if (progress < 1) {
- requestAnimationFrame(animate);
- }
- };
-
- animate();
- }
-
- /**
- * 下拉刷新动画
- * @param {HTMLElement} element - 元素
- */
- pullToRefresh(element) {
- if (!element) return;
-
- element.style.transform = 'translateY(50px)';
- element.style.transition = 'transform 0.3s ease-out';
-
- setTimeout(() => {
- element.style.transform = 'translateY(0)';
- }, 100);
- }
-
- /**
- * 搜索聚焦动画
- * @param {HTMLElement} searchBar - 搜索栏元素
- */
- searchFocus(searchBar) {
- if (!searchBar) return;
-
- searchBar.style.transform = 'translateY(-2px)';
- searchBar.style.boxShadow = '0 4px 20px rgba(0, 0, 0, 0.15)';
- searchBar.style.transition = 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)';
- }
-
- /**
- * 搜索失焦动画
- * @param {HTMLElement} searchBar - 搜索栏元素
- */
- searchBlur(searchBar) {
- if (!searchBar) return;
-
- searchBar.style.transform = 'translateY(0)';
- searchBar.style.boxShadow = '0 2px 12px rgba(0, 0, 0, 0.1)';
- }
-
- /**
- * 模态框显示动画
- * @param {HTMLElement} modal - 模态框元素
- */
- showModal(modal) {
- if (!modal) return;
-
- modal.style.display = 'flex';
- modal.style.animation = 'fadeIn 0.3s ease-out';
-
- const content = modal.querySelector('.modal-content');
- if (content) {
- content.style.animation = 'slideInUp 0.3s ease-out';
- }
- }
-
- /**
- * 模态框隐藏动画
- * @param {HTMLElement} modal - 模态框元素
- * @param {Function} callback - 回调函数
- */
- hideModal(modal, callback) {
- if (!modal) return;
-
- modal.style.animation = 'fadeOut 0.3s ease-in forwards';
-
- const content = modal.querySelector('.modal-content');
- if (content) {
- content.style.animation = 'slideOutDown 0.3s ease-in forwards';
- }
-
- setTimeout(() => {
- modal.style.display = 'none';
- if (callback) callback();
- }, 300);
- }
-}
-
-// 创建全局实例
-const animationUtils = new AnimationUtils();
-
-// 定义CSS动画关键帧
-const animationStyles = `
-@keyframes fadeIn {
- from { opacity: 0; }
- to { opacity: 1; }
-}
-
-@keyframes fadeOut {
- from { opacity: 1; }
- to { opacity: 0; }
-}
-
-@keyframes fadeInUp {
- from {
- opacity: 0;
- transform: translateY(20px);
- }
- to {
- opacity: 1;
- transform: translateY(0);
- }
-}
-
-@keyframes fadeOutDown {
- from {
- opacity: 1;
- transform: translateY(0);
- }
- to {
- opacity: 0;
- transform: translateY(20px);
- }
-}
-
-@keyframes slideInUp {
- from {
- opacity: 0;
- transform: translateY(30px);
- }
- to {
- opacity: 1;
- transform: translateY(0);
- }
-}
-
-@keyframes slideOutDown {
- from {
- opacity: 1;
- transform: translateY(0);
- }
- to {
- opacity: 0;
- transform: translateY(30px);
- }
-}
-
-@keyframes slideInRight {
- from {
- opacity: 0;
- transform: translateX(30px);
- }
- to {
- opacity: 1;
- transform: translateX(0);
- }
-}
-
-@keyframes slideInDown {
- from {
- opacity: 0;
- transform: translate(-50%, -100%);
- }
- to {
- opacity: 1;
- transform: translate(-50%, 0);
- }
-}
-
-@keyframes slideOutUp {
- from {
- opacity: 1;
- transform: translate(-50%, 0);
- }
- to {
- opacity: 0;
- transform: translate(-50%, -100%);
- }
-}
-
-@keyframes bounceIn {
- 0% {
- opacity: 0;
- transform: scale(0.3);
- }
- 50% {
- opacity: 1;
- transform: scale(1.05);
- }
- 70% {
- transform: scale(0.9);
- }
- 100% {
- opacity: 1;
- transform: scale(1);
- }
-}
-
-@keyframes spin {
- from { transform: rotate(0deg); }
- to { transform: rotate(360deg); }
-}
-
-@keyframes pulse {
- 0%, 100% {
- opacity: 1;
- }
- 50% {
- opacity: 0.5;
- }
-}
-
-@keyframes shake {
- 0%, 100% { transform: translateX(0); }
- 10%, 30%, 50%, 70%, 90% { transform: translateX(-5px); }
- 20%, 40%, 60%, 80% { transform: translateX(5px); }
-}
-`;
-
-// 插入动画样式到页面
-if (typeof document !== 'undefined') {
- const styleSheet = document.createElement('style');
- styleSheet.textContent = animationStyles;
- document.head.appendChild(styleSheet);
-}
-
-export default animationUtils;
-export { AnimationUtils };
diff --git a/mobile/src/utils/config.js b/mobile/src/utils/config.js
deleted file mode 100644
index 84fde029..00000000
--- a/mobile/src/utils/config.js
+++ /dev/null
@@ -1,353 +0,0 @@
-// 移动端配置文件 - 集中管理所有可配置参数
-// 使用方法:import { updateConfig } from '@/utils/config.js' 然后调用 updateConfig(newConfig)
-
-// 默认配置
-const DEFAULT_CONFIG = {
- // =========================
- // 加密相关配置
- // =========================
- encryption: {
- // 是否启用加密
- enabled: true,
-
- // RSA公钥 - 与后端保持一致
- rsaPublicKey:
- 'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdHnzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ==',
-
- // RSA私钥 - 前端不建议存放,仅用于测试环境
- rsaPrivateKey: '',
-
- // 加密请求头名称
- encryptHeader: 'encrypt-key',
-
- // AES密钥长度
- aesKeyLength: 32,
- },
-
- // =========================
- // 请求相关配置
- // =========================
- request: {
- // API基础URL
- baseURL: {
- development: '/api',
- production: 'https://your-api-domain.com',
- },
-
- // 请求超时时间(毫秒)
- timeout: 300000, // 5分钟
-
- // 下载超时时间(毫秒)
- downloadTimeout: 1200000, // 20分钟
-
- // 防重复提交间隔时间(毫秒)
- repeatSubmitInterval: 500,
-
- // 默认请求头
- defaultHeaders: {
- 'Content-Type': 'application/json',
- },
- },
-
- // =========================
- // 应用相关配置
- // =========================
- app: {
- // 客户端ID
- clientId: 'e5cd7e4891bf95d1d19206ce24a7b32e',
-
- // 租户ID
- tenantId: '000000',
-
- // 默认referer
- referer: 'http://localhost/login?redirect=/index',
-
- // 登录页面路径
- loginPath: '/pages/login/index',
- },
-
- // =========================
- // 缓存相关配置
- // =========================
- cache: {
- // 默认缓存过期时间(秒)
- defaultExpire: 3600, // 1小时
-
- // Token存储键名
- tokenKey: 'access_token',
-
- // 用户信息存储键名
- userInfoKey: 'userInfo',
-
- // 登录状态存储键名
- loginStatusKey: 'isLoggedIn',
-
- // 会话缓存前缀
- sessionPrefix: 'session_',
- },
-
- // =========================
- // 错误处理配置
- // =========================
- error: {
- // 静默处理的HTTP状态码
- silentCodes: [404],
-
- // 自动跳转登录的状态码
- loginRedirectCodes: [401],
-
- // 错误提示显示时间(毫秒)
- toastDuration: 2000,
-
- // 跳转登录延迟时间(毫秒)
- loginRedirectDelay: 500,
- },
-
- // =========================
- // 调试相关配置
- // =========================
- debug: {
- // 是否启用详细日志
- enableVerboseLog: process.env.NODE_ENV === 'development',
-
- // 是否显示请求日志
- showRequestLog: true,
-
- // 是否显示响应日志
- showResponseLog: true,
-
- // 是否显示加密解密日志
- showCryptoLog: true,
- },
-};
-
-// 当前配置 - 可被动态修改
-let currentConfig = JSON.parse(JSON.stringify(DEFAULT_CONFIG));
-
-// =========================
-// 配置管理方法
-// =========================
-
-/**
- * 获取完整配置
- * @returns {Object} 当前配置对象
- */
-export const getConfig = () => {
- return currentConfig;
-};
-
-/**
- * 获取特定配置项
- * @param {string} path 配置路径,支持嵌套 如: 'encryption.enabled' 或 'request.timeout'
- * @returns {any} 配置值
- */
-export const getConfigItem = (path) => {
- const keys = path.split('.');
- let value = currentConfig;
-
- for (const key of keys) {
- if (value && typeof value === 'object' && key in value) {
- value = value[key];
- } else {
- console.warn(`配置项 ${path} 不存在`);
- return undefined;
- }
- }
-
- return value;
-};
-
-/**
- * 更新配置
- * @param {Object} newConfig 新配置对象(部分更新)
- * @param {boolean} deep 是否深度合并,默认true
- */
-export const updateConfig = (newConfig, deep = true) => {
- if (deep) {
- currentConfig = deepMerge(currentConfig, newConfig);
- } else {
- currentConfig = { ...currentConfig, ...newConfig };
- }
-
- console.log('🔧 配置已更新:', currentConfig);
-
- // 通知其他模块配置已更新
- notifyConfigUpdate();
-};
-
-/**
- * 设置特定配置项
- * @param {string} path 配置路径
- * @param {any} value 配置值
- */
-export const setConfigItem = (path, value) => {
- const keys = path.split('.');
- let target = currentConfig;
-
- for (let i = 0; i < keys.length - 1; i++) {
- const key = keys[i];
- if (!target[key] || typeof target[key] !== 'object') {
- target[key] = {};
- }
- target = target[key];
- }
-
- const lastKey = keys[keys.length - 1];
- target[lastKey] = value;
-
- console.log(`🔧 配置项 ${path} 已更新为:`, value);
-
- // 通知其他模块配置已更新
- notifyConfigUpdate();
-};
-
-/**
- * 重置配置为默认值
- */
-export const resetConfig = () => {
- currentConfig = JSON.parse(JSON.stringify(DEFAULT_CONFIG));
- console.log('🔄 配置已重置为默认值');
-
- // 通知其他模块配置已更新
- notifyConfigUpdate();
-};
-
-/**
- * 获取当前环境的API基础URL
- * @returns {string} API基础URL
- */
-export const getBaseURL = () => {
- const env = process.env.NODE_ENV || 'development';
- return currentConfig.request.baseURL[env] || currentConfig.request.baseURL.development;
-};
-
-// =========================
-// 配置预设方案
-// =========================
-
-/**
- * 开发环境配置
- */
-export const developmentConfig = {
- encryption: {
- enabled: true, // 开发环境也启用加密测试
- },
- debug: {
- enableVerboseLog: true,
- showRequestLog: true,
- showResponseLog: true,
- showCryptoLog: true,
- },
- error: {
- toastDuration: 3000, // 开发环境错误提示显示久一点
- },
-};
-
-/**
- * 生产环境配置
- */
-export const productionConfig = {
- encryption: {
- enabled: true,
- },
- debug: {
- enableVerboseLog: false,
- showRequestLog: false,
- showResponseLog: false,
- showCryptoLog: false,
- },
- request: {
- timeout: 300000, // 生产环境保持较长超时
- repeatSubmitInterval: 1000, // 生产环境防重复提交间隔更长
- },
-};
-
-/**
- * 测试环境配置
- */
-export const testConfig = {
- encryption: {
- enabled: true,
- },
- debug: {
- enableVerboseLog: true,
- showRequestLog: true,
- showResponseLog: false,
- showCryptoLog: true,
- },
-};
-
-/**
- * 应用预设配置
- * @param {string} preset 预设名称: 'development' | 'production' | 'test'
- */
-export const applyPreset = (preset) => {
- const presets = {
- development: developmentConfig,
- production: productionConfig,
- test: testConfig,
- };
-
- if (presets[preset]) {
- updateConfig(presets[preset]);
- console.log(`✅ 已应用 ${preset} 环境配置预设`);
- } else {
- console.error(`❌ 未知的配置预设: ${preset}`);
- }
-};
-
-// =========================
-// 工具方法
-// =========================
-
-/**
- * 深度合并对象
- * @param {Object} target 目标对象
- * @param {Object} source 源对象
- * @returns {Object} 合并后的对象
- */
-const deepMerge = (target, source) => {
- const result = { ...target };
-
- for (const key in source) {
- if (source.hasOwnProperty(key)) {
- if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) {
- result[key] = deepMerge(result[key] || {}, source[key]);
- } else {
- result[key] = source[key];
- }
- }
- }
-
- return result;
-};
-
-/**
- * 通知其他模块配置已更新
- */
-const notifyConfigUpdate = () => {
- // 这里可以实现事件通知机制
- // 例如使用 uni.$emit('configUpdated', currentConfig)
- if (typeof uni !== 'undefined' && uni.$emit) {
- uni.$emit('configUpdated', currentConfig);
- }
-};
-
-// =========================
-// 初始化配置
-// =========================
-
-// 根据环境自动应用配置预设
-const env = process.env.NODE_ENV || 'development';
-if (env !== 'development') {
- applyPreset(env);
-}
-
-// 导出常用配置项的快捷访问方法
-export const isEncryptionEnabled = () => getConfigItem('encryption.enabled');
-export const getClientId = () => getConfigItem('app.clientId');
-export const getTenantId = () => getConfigItem('app.tenantId');
-export const getTimeout = () => getConfigItem('request.timeout');
-export const isDebugMode = () => getConfigItem('debug.enableVerboseLog');
-
-console.log('📋 移动端配置初始化完成,当前环境:', env);
diff --git a/mobile/src/utils/device.js b/mobile/src/utils/device.js
deleted file mode 100644
index 5fd4a09a..00000000
--- a/mobile/src/utils/device.js
+++ /dev/null
@@ -1,204 +0,0 @@
-// 设备信息和兼容性工具
-export const deviceUtils = {
- // 获取系统信息
- getSystemInfo() {
- try {
- return uni.getSystemInfoSync()
- } catch (e) {
- console.error('获取系统信息失败:', e)
- return {}
- }
- },
-
- // 判断是否为低端设备
- isLowEndDevice() {
- const systemInfo = this.getSystemInfo()
-
- // 判断条件:内存小于4GB 或 CPU性能较差
- if (systemInfo.memSize && systemInfo.memSize < 4) {
- return true
- }
-
- // Android 设备版本较低
- if (systemInfo.platform === 'android' && systemInfo.system) {
- const version = parseFloat(systemInfo.system.replace('Android ', ''))
- if (version < 7.0) {
- return true
- }
- }
-
- // iOS 设备版本较低
- if (systemInfo.platform === 'ios' && systemInfo.system) {
- const version = parseFloat(systemInfo.system.replace('iOS ', ''))
- if (version < 12.0) {
- return true
- }
- }
-
- return false
- },
-
- // 获取屏幕适配信息
- getScreenInfo() {
- const systemInfo = this.getSystemInfo()
-
- return {
- screenWidth: systemInfo.screenWidth,
- screenHeight: systemInfo.screenHeight,
- statusBarHeight: systemInfo.statusBarHeight,
- safeAreaInsets: systemInfo.safeAreaInsets,
- // 判断是否为刘海屏
- hasNotch:
- systemInfo.safeAreaInsets &&
- systemInfo.safeAreaInsets.top > systemInfo.statusBarHeight,
- // 判断是否为横屏
- isLandscape: systemInfo.screenWidth > systemInfo.screenHeight,
- }
- },
-
- // 获取设备类型
- getDeviceType() {
- const systemInfo = this.getSystemInfo()
-
- if (systemInfo.uniPlatform === 'mp-weixin') {
- return 'wechat-miniprogram'
- } else if (systemInfo.uniPlatform === 'h5') {
- return 'h5'
- } else if (systemInfo.uniPlatform === 'app-plus') {
- return systemInfo.platform === 'ios' ? 'ios-app' : 'android-app'
- }
-
- return 'unknown'
- },
-}
-
-// 网络状态管理
-export const networkUtils = {
- // 获取网络状态
- getNetworkType() {
- return new Promise((resolve) => {
- uni.getNetworkType({
- success: (res) => {
- resolve(res.networkType)
- },
- fail: () => {
- resolve('unknown')
- },
- })
- })
- },
-
- // 监听网络状态变化
- onNetworkStatusChange(callback) {
- uni.onNetworkStatusChange(callback)
- },
-
- // 判断是否为弱网环境
- async isWeakNetwork() {
- const networkType = await this.getNetworkType()
- return ['2g', '3g', 'slow'].includes(networkType)
- },
-
- // 根据网络状况调整请求策略
- async getRequestConfig() {
- const isWeak = await this.isWeakNetwork()
-
- if (isWeak) {
- return {
- timeout: 30000, // 增加超时时间
- retryTimes: 3, // 增加重试次数
- useCache: true, // 优先使用缓存
- }
- }
-
- return {
- timeout: 10000,
- retryTimes: 1,
- useCache: false,
- }
- },
-}
-
-// 性能优化工具
-export const performanceUtils = {
- // 图片懒加载配置
- getImageLoadConfig() {
- const isLowEnd = deviceUtils.isLowEndDevice()
-
- return {
- // 低端设备减少同时加载的图片数量
- concurrent: isLowEnd ? 2 : 5,
- // 低端设备使用较小的图片
- quality: isLowEnd ? 'low' : 'high',
- // 预加载距离
- threshold: isLowEnd ? 100 : 300,
- }
- },
-
- // 动画性能配置
- getAnimationConfig() {
- const isLowEnd = deviceUtils.isLowEndDevice()
-
- return {
- // 低端设备禁用复杂动画
- enableComplexAnimation: !isLowEnd,
- // 动画持续时间
- duration: isLowEnd ? 200 : 300,
- // 动画缓动函数
- easing: isLowEnd ? 'linear' : 'ease-out',
- }
- },
-
- // 列表滚动优化
- getScrollConfig() {
- const isLowEnd = deviceUtils.isLowEndDevice()
-
- return {
- // 虚拟滚动阈值
- virtualScrollThreshold: isLowEnd ? 50 : 100,
- // 缓冲区大小
- bufferSize: isLowEnd ? 5 : 10,
- // 是否开启上拉加载节流
- enableThrottle: isLowEnd,
- }
- },
-}
-
-// 适配工具
-export const adaptUtils = {
- // 获取安全区域样式
- getSafeAreaStyle() {
- const screenInfo = deviceUtils.getScreenInfo()
-
- return {
- paddingTop: `${screenInfo.safeAreaInsets?.top || screenInfo.statusBarHeight || 0}px`,
- paddingBottom: `${screenInfo.safeAreaInsets?.bottom || 0}px`,
- paddingLeft: `${screenInfo.safeAreaInsets?.left || 0}px`,
- paddingRight: `${screenInfo.safeAreaInsets?.right || 0}px`,
- }
- },
-
- // 获取底部安全区域高度
- getBottomSafeHeight() {
- const screenInfo = deviceUtils.getScreenInfo()
- return screenInfo.safeAreaInsets?.bottom || 0
- },
-
- // 获取顶部安全区域高度
- getTopSafeHeight() {
- const screenInfo = deviceUtils.getScreenInfo()
- return screenInfo.safeAreaInsets?.top || screenInfo.statusBarHeight || 0
- },
-
- // rpx转px
- rpxToPx(rpx) {
- const systemInfo = deviceUtils.getSystemInfo()
- return (rpx * systemInfo.screenWidth) / 750
- },
-
- // px转rpx
- pxToRpx(px) {
- const systemInfo = deviceUtils.getSystemInfo()
- return (px * 750) / systemInfo.screenWidth
- },
-}
diff --git a/mobile/src/utils/encrypt-browser.js b/mobile/src/utils/encrypt-browser.js
deleted file mode 100644
index 1e467811..00000000
--- a/mobile/src/utils/encrypt-browser.js
+++ /dev/null
@@ -1,158 +0,0 @@
-// @ts-nocheck
-import CryptoJS from 'crypto-js';
-
-// 配置信息
-const ENCRYPT_CONFIG = {
- // RSA公钥 - 与后端保持一致 (更新为正确版本,包含ijT7h)
- RSA_PUBLIC_KEY:
- 'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdHnzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ==',
- CLIENT_ID: 'e5cd7e4891bf95d1d19206ce24a7b32e',
- TENANT_ID: '000000',
-};
-
-/**
- * 生成随机AES密钥
- * @param {number} length 密钥长度,默认32位
- * @returns {string} AES密钥
- */
-export function generateRandomAESKey(length = 32) {
- const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
- let result = '';
- for (let i = 0; i < length; i++) {
- result += characters.charAt(Math.floor(Math.random() * characters.length));
- }
- return result;
-}
-
-/**
- * AES加密数据
- * @param {string} data 待加密的数据
- * @param {string} key AES密钥
- * @returns {string} 加密后的数据
- */
-export function encryptWithAES(data, key) {
- const keyUtf8 = CryptoJS.enc.Utf8.parse(key);
- const encrypted = CryptoJS.AES.encrypt(data, keyUtf8, {
- mode: CryptoJS.mode.ECB,
- padding: CryptoJS.pad.Pkcs7,
- });
- return encrypted.toString();
-}
-
-/**
- * RSA加密AES密钥 - 浏览器兼容版本
- * @param {string} aesKey AES密钥
- * @returns {Promise} RSA加密后的AES密钥
- */
-export async function encryptAESKeyWithRSA(aesKey) {
- try {
- // 将Base64公钥转换为ArrayBuffer
- const publicKeyData = Uint8Array.from(atob(ENCRYPT_CONFIG.RSA_PUBLIC_KEY), (c) =>
- c.charCodeAt(0),
- );
-
- // 导入RSA公钥
- const publicKey = await crypto.subtle.importKey(
- 'spki',
- publicKeyData,
- {
- name: 'RSA-OAEP',
- hash: 'SHA-1',
- },
- false,
- ['encrypt'],
- );
-
- // 将AES密钥转为Base64
- const aesKeyBase64 = btoa(aesKey);
- const encoder = new TextEncoder();
- const data = encoder.encode(aesKeyBase64);
-
- // RSA加密
- const encrypted = await crypto.subtle.encrypt(
- {
- name: 'RSA-OAEP',
- },
- publicKey,
- data,
- );
-
- // 转换为Base64
- const encryptedArray = new Uint8Array(encrypted);
- return btoa(String.fromCharCode.apply(null, encryptedArray));
- } catch (error) {
- console.error('RSA加密失败:', error.message);
- console.log('🔄 回退到简化加密方案...');
-
- // 回退方案:使用AES密钥的Base64编码(临时方案)
- try {
- return btoa(aesKey);
- } catch (fallbackError) {
- console.error('回退加密也失败:', fallbackError.message);
- return null;
- }
- }
-}
-
-/**
- * 登录数据加密处理
- * @param {Object} loginData 登录数据对象
- * @returns {Promise