mi-assessment/uniapp/模板_README.md
2026-02-09 14:45:06 +08:00

454 lines
8.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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
<Loading type="page" :loading="true" />
<Loading type="more" :loading="true" :noMore="false" />
```
| 属性 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| type | string | 'page' | page/more |
| loading | boolean | false | 加载状态 |
| noMore | boolean | false | 没有更多 |
### Empty
```vue
<Empty text="暂无数据" :showButton="true" buttonText="去看看" @click="handleClick" />
```
### Popup
```vue
<Popup
:visible="show"
title="提示"
content="确定要执行此操作吗?"
buttonText="确定"
:showCancel="true"
@close="show = false"
@confirm="handleConfirm"
/>
```
### EmojiPicker
```vue
<EmojiPicker
:visible="showEmoji"
@close="showEmoji = false"
@select="handleEmojiSelect"
/>
```
### VoiceRecorder
```vue
<VoiceRecorder @send="handleSendVoice" />
// 回调数据
{
tempFilePath: '临时文件路径',
duration: 10 // 秒
}
```
## 页面开发
### 添加新页面
1.`pages/` 下创建页面目录和 vue 文件
2.`pages.json` 中注册页面:
```json
{
"pages": [
{
"path": "pages/new-page/index",
"style": {
"navigationStyle": "custom",
"navigationBarTitleText": "新页面"
}
}
]
}
```
### 页面模板
```vue
<template>
<view class="page">
<!-- 自定义导航栏 -->
<view class="navbar" :style="{ paddingTop: statusBarHeight + 'px' }">
<view class="navbar-content">
<text class="title">页面标题</text>
</view>
</view>
<!-- 内容区域 -->
<view class="content" :style="{ paddingTop: (statusBarHeight + 44) + 'px' }">
<!-- 页面内容 -->
</view>
</view>
</template>
<script setup>
import { ref, onMounted } from 'vue'
const statusBarHeight = ref(20)
onMounted(() => {
uni.getSystemInfo({
success: (res) => {
statusBarHeight.value = res.statusBarHeight || 20
}
})
})
</script>
```
## 后端接口约定
### 响应格式
```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