8.8 KiB
8.8 KiB
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/ # 静态资源
快速开始
安装依赖
npm install
开发运行
npm run dev:mp-weixin
生产构建
npm run build:mp-weixin
配置说明
环境配置
编辑 config/index.js:
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)
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)
import { useUserStore } from '@/store'
const userStore = useUserStore()
// 登录
userStore.login({ token, userInfo })
// 登出
userStore.logout()
// 判断登录状态
if (userStore.isLoggedIn) { ... }
// 从存储恢复
userStore.restoreFromStorage()
聊天状态 (store/chat.js)
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)
消息类型:
MessageType.TEXT // 1 - 文本
MessageType.VOICE // 2 - 语音
MessageType.IMAGE // 3 - 图片
消息状态:
MessageStatus.SENDING // 0 - 发送中
MessageStatus.SENT // 1 - 已发送
MessageStatus.DELIVERED // 2 - 已送达
MessageStatus.READ // 3 - 已读
MessageStatus.FAILED // 4 - 失败
SignalR 实时通讯 (utils/signalr.js)
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)
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)
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
<Loading type="page" :loading="true" />
<Loading type="more" :loading="true" :noMore="false" />
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| type | string | 'page' | page/more |
| loading | boolean | false | 加载状态 |
| noMore | boolean | false | 没有更多 |
Empty
<Empty text="暂无数据" :showButton="true" buttonText="去看看" @click="handleClick" />
Popup
<Popup
:visible="show"
title="提示"
content="确定要执行此操作吗?"
buttonText="确定"
:showCancel="true"
@close="show = false"
@confirm="handleConfirm"
/>
EmojiPicker
<EmojiPicker
:visible="showEmoji"
@close="showEmoji = false"
@select="handleEmojiSelect"
/>
VoiceRecorder
<VoiceRecorder @send="handleSendVoice" />
// 回调数据
{
tempFilePath: '临时文件路径',
duration: 10 // 秒
}
页面开发
添加新页面
- 在
pages/下创建页面目录和 vue 文件 - 在
pages.json中注册页面:
{
"pages": [
{
"path": "pages/new-page/index",
"style": {
"navigationStyle": "custom",
"navigationBarTitleText": "新页面"
}
}
]
}
页面模板
<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>
后端接口约定
响应格式
{
"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