uni-app+vue3跨端仿微信聊天模板实战
经过两周的开发,基于uni-app和vue3的仿微信聊天应用项目已经完成。该项目名为uni-vue3-wechat,使用最新的跨端技术uni-app和vue3的setup语法进行开发,实现了仿微信的长按说话语音面板、图片和视频预览、红包以及朋友圈等功能。
使用技术
该项目采用以下技术栈实现跨端兼容:
- 编辑器:HbuilderX 4.75
- 技术框架:uni-app + vue3 + pinia2 + vite5
- 状态管理:pinia2
- 组件库:uni-ui 和 uv-ui(专为uni-app和vue3设计的组件库)
- 弹框组件:uv3-popup(自定义的多端弹框组件)
- 自定义组件:uv3-navbar导航栏和uv3-tabbar菜单栏
- 缓存技术:pinia-plugin-unistorage
- 支持编译:H5、小程序和App端
项目结构目录
项目采用标准的uni-app目录结构,支持运行到H5、小程序和App端。在H5端,页面以750px宽度进行布局适配,确保在不同设备上的一致显示。
App.vue配置文件
以下是App.vue的主要配置代码,用于处理应用生命周期和系统信息:
<script setup>
import { provide } from 'vue'
import { onLaunch, onShow, onHide, onPageNotFound } from '@dcloudio/uni-app'
onLaunch(() => {
console.log('App Launch')
// 隐藏系统tabbar
uni.hideTabBar({
success: () => {},
fail: () => {
// 修复H5隐藏tabbar报错:Uncaught (in promise) {errMsg: 'hideTabBar:fail not TabBar page'}
// #ifdef H5
const uniTabbar = document.querySelector('.uni-tabbar-bottom')
if (uniTabbar) {
uniTabbar.style.display = 'none'
}
// #endif
}
})
launchApp()
})
onShow(() => {
console.log('App Show')
})
onHide(() => {
console.log('App Hide')
})
onPageNotFound((e) => {
console.warn('Route Error:', `${e.path}`)
})
// 获取系统设备信息
const launchApp = () => {
uni.getSystemInfo({
success: (e) => {
// 获取手机状态栏高度
let statusBar = e.statusBarHeight
let customBar
// #ifndef MP
customBar = statusBar + (e.platform == 'android' ? 50 : 45)
// #endif
// #ifdef MP-WEIXIN
// 获取胶囊按钮的布局位置信息
let menu = wx.getMenuButtonBoundingClientRect()
// 导航栏高度 = 胶囊下距离 + 胶囊上距离 - 状态栏高度
customBar = menu.bottom + menu.top - statusBar
// #endif
// #ifdef MP-ALIPAY
customBar = statusBar + e.titleBarHeight
// #endif
// 由于globalData在vue3 setup存在兼容性问题,改为provide/inject替代
provide('globalData', {
statusBarH: statusBar,
customBarH: customBar,
screenWidth: e.screenWidth,
screenHeight: e.screenHeight,
platform: e.platform
})
}
})
}
</script>
<style>
/* #ifndef APP-NVUE */
@import 'static/fonts/iconfont.css';
/* #endif */
/* 隐藏系统Tabbar */
.uni-app--showtabbar .uni-tabbar-bottom {display: none;}
</style>
<style lang="scss">
@import 'styles/reset.scss';
@import 'styles/layout.scss';
</style>
通用布局模板
项目布局分为顶部导航条、内容区域和底部操作栏三个主要模块。以下是一个通用布局组件的示例代码,它通过插槽灵活适应不同页面需求:
<!-- #ifdef MP-WEIXIN -->
<script>
export default {
/**
* 解决小程序class、id透传问题
* manifest.json中配置mergeVirtualHostAttributes: true, 在微信小程序平台不生效,组件外部传入的class没有挂到组件根节点上
* https://github.com/dcloudio/uni-ui/issues/753
*/
options: { virtualHost: true }
}
</script>
<!-- #endif -->
<script setup>
const props = defineProps({
// 是否显示自定义tabbar
showTabBar: { type: [Boolean, String], default: false },
})
</script>
<template>
<view class="uv3__container flexbox flex-col flex1">
<!-- 顶部插槽 -->
<slot name="header" />
<!-- 内容区 -->
<view class="uv3__scrollview flex1">
<slot />
</view>
<!-- 底部插槽 -->
<slot name="footer" />
<!-- tabbar栏 -->
<uv3-tabbar v-if="showTabBar" hideTabBar fixed />
</view>
</template>
uniapp+vue3自定义导航条和底部tabbar
自定义导航栏和tabbar组件位于项目的components目录下。导航栏组件支持多种配置,例如设置背景颜色、标题和固定位置。以下是一个基本使用示例:
<uv3-navbar :back="true" title="标题" bgcolor="#07c160" color="#fff" fixed zIndex="2025" />
此外,组件还支持通过插槽完全自定义内容,例如:
<uv3-navbar custom bgcolor="linear-gradient(to right, #07c160, #0000ff)" color="#fff" center transparent z-index="2024">
<template #back>自定义左侧返回按钮</template>
<template #backText>自定义返回文字</template>
<template #title>
自定义标题
</template>
<template #right>
自定义右侧
</template>
</uv3-navbar>
uniapp+vue3聊天模板
聊天界面采用自定义组件实现,包含增强版的输入编辑框,支持文本输入、语音消息、表情和附件发送等功能。以下是聊天工具栏的部分代码,展示了其结构和交互逻辑:
<view class="uv3__chattoolbar" :style="{'padding-bottom': fixPaddingBottom}">
<!-- 输入框 -->
<view class="uv3__chattoolbar-editor flexbox">
<view class="btn" @click="handleVoice"><text class="uv3-icon" :class="voiceBtnEnable ? 'uv3-icon-voice' : 'uv3-icon-keyboard'"></text></view>
<view class="editor flex1">
<template v-if="voiceBtnEnable">
<uv3-input
v-model="editorValue"
type="textarea"
:autosize="{maxRows: 6}"
:autofocus="autofocus"
:cursor="editorLastCursor"
clearable
style="width: 100%;"
@input="handleEditorInput"
@focus="handleEditorFocus"
@blur="handleEditorBlur"
/>
</template>
<template v-else>
<view class="uv3__voice-handle flexbox" @touchstart.prevent="handleTouchStart" @touchmove="handleTouchUpdate" @touchend="handleTouchEnd">{{voiceTypeMap[voiceType]}}</view>
</template>
</view>
<view class="btn" @click="handleEmojPlusView(0)" @mousedown.prevent><text class="uv3-icon uv3-icon-face"></text></view>
<view class="btn" @click="handleEmojPlusView(1)"><text class="uv3-icon uv3-icon-plus"></text></view>
<view class="btn" @click="handleSend" @mousedown.prevent><view class="send flexbox"><uni-icons type="arrow-up" color="#fff" size="16"></uni-icons></view></view>
</view>
<!-- 操作栏 -->
<view v-show="showToolbar" class="uv3__chattoolbar-operate">
<!-- 表情 -->
<view v-show="toolbarIndex == 0" class="uv3__chattoolbar-emotion flexbox flex-col">
<view class="uv3__emotion-tabs flexbox flex-alignc">
<view v-for="(item, index) in emojList" :key="index" class="item" :class="{'on': item.selected}" @click="handleEmojTab(index)">
<text v-if="item.type=='emoj'" class="emoj uv3-icon uv3-icon-face"></text>
<image v-else :src="item.pathLabel" />
</view>
</view>
<view v-for="(item, index) in emojList" :key="index" class="uv3__emotion-cells flex1" :class="{'active': item.selected}">
<view class="flexbox" :class="item.type == 'emoj' ? 'emojwrap' : 'gifwrap'">
<view v-for="(emoj, key) in item.nodes" :key="key" class="cells flexbox">
<view class="item">
<text v-if="item.type=='emoj'" class="emoj" @click="handleEmojClick(emoj)">{{emoj}}</text>
<image v-else :src="emoj" class="gif" @click="handleGifClick(emoj)" />
</view>
</view>
</view>
</view>
</view>
<!-- 操作栏 -->
<view v-show="toolbarIndex == 1" class="uv3__chattoolbar-pluschoose flexbox">
<view v-for="(item, index) in chooseList" :key="index" class="uv3__plusbtns-cells" @click="handlePlusAction(item)">
<view class="icon flexbox"><image :src="item.icon" /></view>
<view class="label">{{item.label}}</view>
</view>
</view>
</view>
</view>
该项目展示了如何利用uni-app和vue3构建功能完整的跨端聊天应用,涉及布局、自定义组件和复杂交互的实现。