uniapp_vue3_wechatuni-app+vue3+vite4+pinia2+uni-ui
is a three-terminal (h5 + mini program + APP) imitation WeChat chat instance originally developed based on the latest cross-terminal technology .
Contents
Preview effect
The effect of compiling to h5+mini program+App is as follows:
The entire project vue3 setup
was developed using grammatical structure coding.
Use technology
- Development tools: HbuilderX 4.0.8
- Technical framework: Uniapp+Vue3+Pinia2+Vite4.x
- UI component library: uni-ui+uv-ui
- Pop-up component: uv3-popup (uniapp+vue3 multi-terminal custom pop-up component)
- Custom component: uv3-navbar+uv3-tabbar component
- Cache service: pinia-plugin-unistorage
- Compilation support: H5 + applet + APP
Project structure
Entrance main.js configuration
/** * Entry file main.js */ import { createSSRApp } from 'vue' import App from './App' //Introduce pinia state management import pinia from '@/pinia' export function createApp () { const app = createSSRApp ( App ) app. use (pinia) return { app, pinia } }
In addition, it supports compiling to the web side and displaying it at 750px pixels.
This project has been updated and launched in the studio. If you happen to need it, you are welcome to take pictures~ Thank you for your support!
App.vue configuration
Use vue3 setup syntax to initialize some custom navigation bar parameters and page styles.
<script setup> import { provide } from 'vue' import { onLaunch, onShow, onHide, onPageNotFound } from '@dcloudio/uni-app' onLaunch ( () => { console . log ( 'App Launch' ) uni. hideTabBar () loadSystemInfo () }) onShow ( () => { console . log ( 'App Show' ) }) onHide ( () => { console . log ( 'App Hide' ) }) onPageNotFound ( ( e ) => { console . warn ( 'Route Error: ' , ` ${e.path} ` ) }) // Get system device information const loadSystemInfo = () => { uni. getSystemInfo ({ success : ( e ) => { // Get the phone status bar height let statusBar = e. statusBarHeight let customBar // #ifndef MP customBar = statusBar + (e. platform == 'android' ? 50 : 45 ) // #endif // #ifdef MP-WEIXIN // Get the layout position information of the capsule button let menu = wx. getMenuButtonBoundingClientRect () // Navigation bar height = capsule bottom distance + capsule top distance - status bar height customBar = menu. bottom + menu. top - statusBar // #endif // #ifdef MP-ALIPAY customBar = statusBar + e. titleBarHeight // #endif // Due to compatibility issues with globalData in vue3 setup, the provide/inject alternative was changed to 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 */ </ style > < style lang = "scss" > @import 'styles/reset.scss ' ; @import 'styles/layout.scss' ; </ style >
uni-vue3-chat layout
The overall project structure adopts three large modules: top navigation area + main content area + bottom area .
<!-- Public layout template --> <!-- #ifdef MP - WEIXIN --> < script > export default { /** * Solve the problem of mini program class and ID transparent transmission * Configure mergeVirtualHostAttributes: true in manifest.json. It does not take effect on the WeChat applet platform. The class passed in from outside the component is not hung on the root node of the component. Add options: { virtualHost: true } to the component. * https://github.com/dcloudio/uni-ui/issues/753 */ options : { virtualHost : true } } </script> <!-- #endif --> < script setup > const props = defineProps ({ // Whether to display a custom tabbar showTabBar : { type : [ Boolean , String ], default : false }, }) </script> < template > < view class = "uv3__container flexbox flex-col flex1" > <!-- Top slot --> < slot name = "header" /> <!-- Content area--> < view class = "uv3__scrollview flex1" > < slot /> </ view > <!-- Bottom slot--> < slot name = "footer" /> <!-- tabbar bar --> < uv3-tabbar v-if = "showTabBar" hideTabBar fixed /> </ view > </ template >
uniapp imitates WeChat Jiugongge image group
<script setup> import { onMounted, ref, computed, watch, getCurrentInstance } from 'vue' const props = defineProps ({ // Image group avatar : { type : Array , default : null }, }) const instance = getCurrentInstance () const uuid = computed ( () => Math . floor ( Math . random () * 10000 )) const avatarPainterId = ref ( 'canvasid' + uuid. value ) const createAvatar = () => { const ctx = uni. createCanvasContext (avatarPainterId. value , instance) // Calculate the coordinates of the image on the canvas const avatarSize = 12 const gap = 2 for ( let i = 0 , len = props. avatar . length ; i < len; i++) { const row = Math . floor (i / 3 ) const col = i % 3 const x = col * (avatarSize + gap) const y = row * (avatarSize + gap) ctx. drawImage (props. avatar [i], x, y, avatarSize, avatarSize) } ctx.draw ( false , ( ) => { // Output temporary picture /* uni.canvasToTempFilePath({ canvasId: avatarPainterId.value, success: (res) => { console.log(res.tempFilePath) } }) */ }) } onMounted ( () => { createAvatar () }) watch ( () => props. avatar , () => { createAvatar () }) </script> < template > < template v-if = "avatar.length > 1" > < view class = "uv3__avatarPainter" > < canvas :canvas-id = "avatarPainterId" class = "uv3__avatarPainter-canvas" > </ canvas > </ view > </ template > < template v-else > < image class = "uv3__avatarOne" :src = "avatar[0]" /> </ template > </ template > < style lang = "scss" scoped > .uv3__avatarPainter { background-color : #eee ; border-radius : 5px ; overflow : hidden; padding : 2px ; height : 44px ; width : 44px ;} .uv3__avatarPainter-canvas { height : 100 % ; width : 100% ;} .uv3__avatarOne { border-radius : 5px ; height : 44px ; width : 44px ;} </ style >
<uv3-navbar :back= "true" title= "Title content" bgcolor= "#07c160" color= "#fff" fixed zIndex= "1010" /> < uv3-navbar custom bgcolor = "linear-gradient(to right, #07c160, #0000ff)" color = "#fff" center transparent zIndex = "2024" > < template # back > < uni-icons type = "close" /> </ template > < template # backText > < text > Homepage </ text > </ template > < template # title > < image src = "/static/logo.jpg" style = "height:20px;width:20px ;" /> Admin </ template > < template # right > < view class = "ml-20" @ click = "handleAdd" > < text class = "iconfont icon-tianjia" > </ text > </ view > < view class = "ml-20" > < text class = "iconfont icon-msg" > </ text > </ view > </ template > </ uv3-navbar >
uniapp+vue3 customized mobile phone pop-up window
uv3-popup supports component + function calls.
<script setup> import { onMounted, ref, computed, watch, nextTick, getCurrentInstance } from 'vue' const props = defineProps ({ ... }) const emit = defineEmits ([ 'update:modelValue' , 'open' , 'close' ]) const instance = getCurrentInstance () const opts = ref ({ ...props }) const visible = ref ( false ) const closeAnim = ref ( false ) const stopTimer = ref ( null ) const oIndex = ref (props. zIndex ) const uuid = computed ( () => Math . floor ( Math . random () * 10000 )) const positionStyle = ref ({ position : 'absolute' , left : '-999px' , top : '-999px' }) const toastIcon = { ... } // Open the pop-up box const open = ( options ) => { if (visible. value ) return opts. value = Object . assign ({}, props, options) // console.log('-=-=Mixed parameters: ', opts.value) visible. value = true // Each component of nvue is transparent by default on the Android side. If background-color is not set, ghosting problems may occur // #ifdef APP-NVUE if (opts. value . customStyle && !opts. value . customStyle [ 'background' ] && !opts. value . customStyle [ 'background-color' ]) { opts. value . customStyle [ 'background' ] = '#fff' } // #endif let _index = ++index oIndex. value = _index + parseInt (opts. value . zIndex ) emit ( 'open' ) typeof opts. value . onOpen === 'function' && opts. value . onOpen () // Long press processing if (opts. value . follow ) { nextTick ( () => { let winW = uni. getSystemInfoSync (). windowWidth let winH = uni. getSystemInfoSync (). windowHeight // console.log('Coordinate point Information: ', opts.value.follow) getDom (uuid. value ). then ( res => { // console.log('Dom size information: ', res) if (!res) return let pos = getPos (opts. value . follow [ 0 ], opts. value . follow [ 1 ], res. width + 15 , res. height + 15 , winW, winH) positionStyle. value . left = pos[ 0 ] + 'px' positionStyle. value . top = pos[ 1 ] + 'px' }) }) } if (opts. value . time ) { if (stopTimer. value ) clearTimeout (stopTimer. value ) stopTimer. value = setTimeout ( () => { close () }, parseInt (opts. value . time ) * 1000 ) } } // Close the popup const close = () => { if (!visible. value ) return closeAnim. value = true setTimeout ( () => { visible. value = false closeAnim. value = false emit ( 'update:modelValue' , false ) emit ( 'close' ) typeof opts. value . onClose === 'function' && opts. value . onClose () positionStyle. value . left = '-999px' positionStyle. value . top = '-999px' stopTimer. value && clearTimeout (stopTimer. value ) }, 200 ) } // Click on the mask layer const handleShadeClick = () => { if ( JSON . parse (opts . value . shadeClose )) { close () } } // Button event const handleBtnClick = ( e, index ) => { let btn = opts. value . btns [index] if (!btn?. disabled ) { console . log ( 'Button event type: ' , typeof btn. click ) typeof btn. click === 'function' && btn. click (e) } } // Get the dom width and height const getDom = ( id ) => { return new Promise ( ( resolve, inject ) => { // uni.createSelectorQuery().in(this) in uniapp vue3 will report an error __route__ is not defined https ://ask.dcloud.net.cn/question/140192 uni. createSelectorQuery (). in (instance). select ( '#uapopup-' + id). fields ({ size : true , }, data => { resolve (data) }). exec () }) } // Adaptive coordinate point const getPos = ( x, y, ow, oh, winW, winH ) => { let l = (x + ow) > winW ? x - ow : x let t = (y + oh) > winH ? y - oh : y return [l, t] } onMounted ( () => { if (props. modelValue ) { open () } }) watch ( () => props. modelValue , ( val ) => { // console.log(val) if (val) { open () } else { close () } }) defineExpose ({ open, close }) </script>
uniapp+vue3 chat module
At present, the plug-in has been released to the plug-in market for free for download and use by friends who need it.
<!-- Voice Panel--> < view v-if = "voicePanelEnable" class = "uv3__voicepanel-popup" > < view class = "uv3__voicepanel-body flexbox flex-col" > <!-- Cancel sending + voice to text --> < view v-if = "!voiceToTransfer" class = "uv3__voicepanel-transfer" > <!-- Tip animation --> < view class = "animtips flexbox" :class = "voiceType == 2 ? 'left ' : voiceType == 3 ? 'right' : null" > < Waves :lines = "[2, 3].includes(voiceType) ? 10 : 20" /> </ view > <!-- Action items --> < view class = "icobtns flexbox" > < view class = "vbtn cancel flexbox flex-col" :class = "{'hover': voiceType == 2}" @ click = "handleVoiceCancel" > < text class = "vicon uv3 -icon uv3-icon-close" > </ text > </ view > < view class = "vbtn word flexbox flex-col" :class = "{'hover': voiceType == 3}" > < text class = " vicon uv3-icon uv3-icon-word" > </ text > </ view > </ view > </ view > <!-- Voice to text (recognition result status) --> < view v-if = "voiceToTransfer" class = "uv3__voicepanel-transfer result fail" > <!-- Prompt animation --> < view class = "animtips flexbox" > < uni-icons type = "info-filled" color = "#fff" size = "20" > </ uni-icons > < text class = "c-fff" > No text recognized </ text > </ view > < view class = "icobtns flexbox" > < view class = "vbtn cancel flexbox flex-col" @ click = "handleVoiceCancel" > < text class = "vicon uv3-icon uv3-icon-chexiao" > </ text > Cancel </ view > < view class = "vbtn word flexbox flex-col" > < text class = "vicon uv3-icon uv3-icon-audio" > </ text > Send original voice </ view > < view class = "vbtn check flexbox flex-col" > < text class = "vicon uv3-icon uv3-icon-duigou" > </ text > </ view > </ view > </ view > <!-- Background voice image--> < view class = "uv3__voicepanel-cover" > < image v-if = "!voiceToTransfer" src = "/static/voice_bg.webp" :webp = "true" mode = "widthFix " style = "width: 100%;" /> </ view > <!-- // Prompt text --> < view v-if = "!voiceToTransfer" class = "uv3__voicepanel-tooltip" > {{voiceTypeMap[voiceType ]}} </ view > <!-- Background icon --> < view v-if = "!voiceToTransfer" class = "uv3__voicepanel-fixico" > < text class = "uv3-icon uv3-icon-audio fs-50 " > </ text > </ view > </ view > </ view >
Okay, the above is some sharing of the practical project of developing WeChat chat with uni-app+vue3. I hope it will be helpful to everyone~
https://segmentfault.com/a/1190000044796609