# uni-app 小程序模板 基于 uni-app + Vue 3 + Pinia 的微信小程序模板,内置即时通讯功能。 ## 技术栈 - uni-app (Vue 3 + Composition API) - Pinia 状态管理 - SignalR WebSocket 实时通讯 - SCSS 样式 ## 项目结构 ``` ├── api/ # 接口模块 │ ├── request.js # 请求封装(Token、重试、取消) │ ├── auth.js # 认证接口 │ ├── user.js # 用户接口 │ ├── chat.js # 聊天接口 │ └── index.js # 统一导出 ├── components/ # 公共组件 │ ├── Loading/ # 加载状态 │ ├── Empty/ # 空状态 │ ├── Popup/ # 弹窗 │ ├── EmojiPicker/ # 表情选择器 │ └── VoiceRecorder/ # 语音录制 ├── config/ │ └── index.js # 环境配置 ├── pages/ # 页面 │ ├── index/ # 首页 │ ├── message/ # 消息列表 │ ├── chat/ # 聊天页面 │ ├── mine/ # 我的 │ └── login/ # 登录 ├── store/ # 状态管理 │ ├── user.js # 用户状态 │ ├── chat.js # 聊天状态 │ └── index.js # 统一导出 ├── utils/ # 工具函数 │ ├── storage.js # 本地存储 │ ├── format.js # 格式化 │ ├── signalr.js # SignalR 客户端 │ └── emoji.js # 表情数据 └── static/ # 静态资源 ``` ## 快速开始 ### 安装依赖 ```bash npm install ``` ### 开发运行 ```bash npm run dev:mp-weixin ``` ### 生产构建 ```bash npm run build:mp-weixin ``` ## 配置说明 ### 环境配置 编辑 `config/index.js`: ```javascript const ENV = { development: { API_BASE_URL: 'http://localhost:5000/api', STATIC_BASE_URL: 'http://localhost:5000', SIGNALR_URL: 'ws://localhost:5000/hubs/chat' }, production: { API_BASE_URL: 'https://your-domain.com/api', STATIC_BASE_URL: 'https://your-domain.com', SIGNALR_URL: 'wss://your-domain.com/hubs/chat' } } // 切换环境 const CURRENT_ENV = 'development' ``` ### 请求配置 | 配置项 | 默认值 | 说明 | |--------|--------|------| | REQUEST_TIMEOUT | 30000 | 请求超时时间(ms) | | REQUEST_RETRY_COUNT | 2 | 失败重试次数 | | REQUEST_RETRY_DELAY | 1000 | 重试间隔(ms) | ## 核心模块 ### 请求封装 (api/request.js) ```javascript import { get, post, put, del } from '@/api/request' // GET 请求 const res = await get('/users/detail', { userId: 1 }) // POST 请求 const res = await post('/chat/send', { content: 'hello' }) // 带配置的请求 const res = await get('/data', {}, { needAuth: false, // 是否需要 Token retry: false, // 是否重试 cancelPrevious: true // 取消之前的相同请求 }) ``` 特性: - 自动携带 JWT Token - 401 状态码自动清除登录态 - 请求失败自动重试 - 支持取消请求 ### 状态管理 #### 用户状态 (store/user.js) ```javascript import { useUserStore } from '@/store' const userStore = useUserStore() // 登录 userStore.login({ token, userInfo }) // 登出 userStore.logout() // 判断登录状态 if (userStore.isLoggedIn) { ... } // 从存储恢复 userStore.restoreFromStorage() ``` #### 聊天状态 (store/chat.js) ```javascript import { useChatStore, MessageType, MessageStatus } from '@/store' const chatStore = useChatStore() // 设置会话列表 chatStore.setSessions(sessions) // 发送消息 chatStore.sendMessage(sessionId, { senderId: 1, receiverId: 2, messageType: MessageType.TEXT, content: 'hello' }) // 接收消息 chatStore.receiveMessage(message) // 标记已读 chatStore.markSessionAsRead(sessionId) ``` 消息类型: ```javascript MessageType.TEXT // 1 - 文本 MessageType.VOICE // 2 - 语音 MessageType.IMAGE // 3 - 图片 ``` 消息状态: ```javascript MessageStatus.SENDING // 0 - 发送中 MessageStatus.SENT // 1 - 已发送 MessageStatus.DELIVERED // 2 - 已送达 MessageStatus.READ // 3 - 已读 MessageStatus.FAILED // 4 - 失败 ``` ### SignalR 实时通讯 (utils/signalr.js) ```javascript import signalR from '@/utils/signalr' // 连接 await signalR.connect() // 监听消息 signalR.on('ReceiveMessage', (message) => { console.log('收到消息:', message) }) // 加入会话 await signalR.joinSession(sessionId) // 离开会话 await signalR.leaveSession(sessionId) // 调用服务端方法 const result = await signalR.invoke('MethodName', arg1, arg2) // 发送消息(不等待响应) signalR.send('MethodName', arg1, arg2) // 断开连接 signalR.disconnect() ``` 特性: - 自动重连(最多5次,指数退避) - 心跳保活(15秒间隔) - 支持方法调用和事件监听 ### 本地存储 (utils/storage.js) ```javascript import { setToken, getToken, removeToken, setUserInfo, getUserInfo, removeUserInfo, setStorage, getStorage, removeStorage, clearStorage } from '@/utils/storage' // Token setToken('xxx') const token = getToken() // 用户信息 setUserInfo({ userId: 1, nickname: '用户' }) const user = getUserInfo() // 通用存储 setStorage('key', value) const value = getStorage('key', defaultValue) ``` ### 格式化工具 (utils/format.js) ```javascript import { formatTimestamp, maskName, maskPhone } from '@/utils/format' // 时间格式化 formatTimestamp('2024-01-15 10:30:00') // 今天 10:30 / 昨天 / 前天 / 1月15号 // 姓名脱敏 maskName('张三') // 张* maskName('张三丰') // 张*丰 // 手机脱敏 maskPhone('13812345678') // 138****5678 ``` ## 公共组件 ### Loading ```vue ``` | 属性 | 类型 | 默认值 | 说明 | |------|------|--------|------| | type | string | 'page' | page/more | | loading | boolean | false | 加载状态 | | noMore | boolean | false | 没有更多 | ### Empty ```vue ``` ### Popup ```vue ``` ### EmojiPicker ```vue ``` ### VoiceRecorder ```vue // 回调数据 { tempFilePath: '临时文件路径', duration: 10 // 秒 } ``` ## 页面开发 ### 添加新页面 1. 在 `pages/` 下创建页面目录和 vue 文件 2. 在 `pages.json` 中注册页面: ```json { "pages": [ { "path": "pages/new-page/index", "style": { "navigationStyle": "custom", "navigationBarTitleText": "新页面" } } ] } ``` ### 页面模板 ```vue ``` ## 后端接口约定 ### 响应格式 ```json { "code": 0, "message": "success", "data": {} } ``` - code = 0 表示成功 - code != 0 表示失败,message 为错误信息 ### 认证接口 ``` POST /auth/login Request: { code: "微信登录code" } Response: { token: "JWT Token", userId: 1, nickname: "用户名", avatar: "头像URL" } ``` ### 聊天接口 ``` GET /chat/sessions Response: { items: [{ sessionId: 1, otherUserId: 2, otherNickname: "对方昵称", otherAvatar: "对方头像", lastMessageContent: "最后消息", lastMessageTime: "2024-01-15T10:30:00", unreadCount: 5 }] } GET /chat/messages?sessionId=1&pageIndex=1&pageSize=20 Response: { items: [{ messageId: 1, senderId: 1, receiverId: 2, messageType: 1, content: "消息内容", createTime: "2024-01-15T10:30:00", isSelf: true }] } POST /chat/send Request: { sessionId: 1, receiverId: 2, messageType: 1, content: "消息内容" } ``` ### SignalR Hub Hub 地址: `/hubs/chat` 服务端方法: - `JoinSession(sessionId)` - 加入会话 - `LeaveSession(sessionId)` - 离开会话 客户端事件: - `ReceiveMessage` - 接收新消息 ## License MIT