vite5-webchat:基于vite5+vue3.x+element-plus网页版聊天系统

xiaoyan_2018 2024-07-09 16:33:01 阅读 55

原创vite-vue3-wechat仿微信网页版界面聊天实例。

基于最新前端技术<code>vite5.x+vue3+vue-router+pinia+elementPlus搭建网页端聊天室。实现了聊天、通讯录、朋友圈、短视频、我的等功能模块。支持侧边栏展开/收缩、动态主题壁纸、锁屏、最大化等功能。

在这里插入图片描述

vite-wechat实现了文字/emoj混排、图片/视频预览、红包、地图、语音播放等功能。

在这里插入图片描述

使用技术

编辑器:vscode框架技术:vite5.2+vue3.4+vue-router4.3+pinia2UI组件库:element-plus^2.7.5 (饿了么网页端vue3组件库)状态管理:pinia^2.1.7地图插件:@amap/amap-jsapi-loader(高德地图组件)视频滑动:swiper^11.1.4预编译样式:sass^1.77.4构建工具:vite^5.2.0

在这里插入图片描述

在这里插入图片描述

项目结构目录

vite-wechat使用 <code>vite5.x 构建项目模板,采用 vue3 setup 语法糖编码开发模式。

在这里插入图片描述

在这里插入图片描述

main.js配置

<code>import { createApp } from 'vue'

import './style.scss'

import App from './App.vue'

// 引入组件库

import ElementPlus from 'element-plus'

import 'element-plus/dist/index.css'

import VEPlus from 've-plus'

import 've-plus/dist/ve-plus.css'

// 引入路由/状态管理

import Router from './router'

import Pinia from './pinia'

const app = createApp(App)

app

.use(ElementPlus)

.use(VEPlus)

.use(Router)

.use(Pinia)

.mount('#app')

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

vue3实现滑动解锁

在这里插入图片描述

<code><script setup>

import { ref, computed, inject, nextTick } from 'vue'

import { useRouter } from 'vue-router'

import { authState } from '@/pinia/modules/auth'

import { uuid, guid } from '@/utils'

const authstate = authState()

const router = useRouter()

// 启动页

const splashScreen = ref(true)

const authPassed = ref(false)

// 滑动距离

const touchY = ref(0)

const touchable = ref(false)

// 数字键盘输入值

const pwdValue = ref('')

const keyNumbers = ref([

{ letter: 'a'},

{ letter: 'b'},

{ letter: 'c'},

{ letter: 'd'},

{ letter: 'e'},

{ letter: 'f'},

{ letter: 'g'},

{ letter: 'h'},

{ letter: 'i'},

{ letter: 'j'},

{ letter: 'k'},

{ letter: 'l'},

{ letter: 'm'},

{ letter: 'n'},

{ letter: 'o'},

{ letter: 'p'},

{ letter: 'q'},

{ letter: 'r'},

{ letter: 's'},

{ letter: 't'},

{ letter: 'u'},

{ letter: 'v'},

{ letter: 'w'},

{ letter: 'x'},

{ letter: 'y'},

{ letter: 'z'},

{ letter: '1'},

{ letter: '2'},

{ letter: '3'},

{ letter: '4'},

{ letter: '5'},

{ letter: '6'},

{ letter: '7'},

{ letter: '8'},

{ letter: '9'},

{ letter: '0'},

{ letter: '@'},

{ letter: '#'},

{ letter: '%'},

{ letter: '&'},

{ letter: '!'},

{ letter: '*'},

])

//...

// 触摸事件(开始/更新)

const handleTouchStart = (e) => {

touchY.value = e.clientY

touchable.value = true

}

const handleTouchUpdate = (e) => {

let swipeY = touchY.value - e.clientY

if(touchable.value && swipeY > 100) {

splashScreen.value = false

touchable.value = false

}

}

const handleTouchEnd = (e) => {

touchY.value = 0

touchable.value = false

}

// 点击数字键盘

const handleClickNum = (num) => {

let pwdLen = passwordArr.value.length

if(pwdValue.value.length >= pwdLen) return

pwdValue.value += num

if(pwdValue.value.length == pwdLen) {

// 验证通过

if(pwdValue.value == password.value) {

// ...

}else {

setTimeout(() => {

pwdValue.value = ''

}, 200)

}

}

}

// 删除

const handleDel = () => {

let num = Array.from(pwdValue.value)

num.splice(-1, 1)

pwdValue.value = num.join('')

}

// 清空

const handleClear = () => {

pwdValue.value = ''

}

// 返回

const handleBack = () => {

splashScreen.value = true

}

</script>

<template>

<div class="uv3__launch">code>

<div

v-if="splashScreen"code>

class="uv3__launch-splash"code>

@mousedown="handleTouchStart"code>

@mousemove="handleTouchUpdate"code>

@mouseup="handleTouchEnd"code>

>

<div class="uv3__launch-splashwrap">code>

...

</div>

</div>

<div v-else class="uv3__launch-keyboard">code>

<div class="uv3__launch-pwdwrap">code>

<div class="text">密码解锁</div>code>

<div class="circle flexbox">code>

<div v-for="(num, index) in passwordArr" :key="index" class="dot" :class="{'active': num <= pwdValue.length}"></div>code>

</div>

</div>

<div class="uv3__launch-numwrap">code>

<div v-for="(item, index) in keyNumbers" :key="index" class="numbox flex-c" @click="handleClickNum(item.letter)">code>

<div class="num">{ { item.letter}}</div>code>

</div>

</div>

<div class="foot flexbox">code>

<Button round icon="ve-icon-clean" @click="handleClear">清空</Button>code>

<Button type="danger" v-if="pwdValue" round icon="ve-icon-backspace" @click="handleDel">删除</Button>code>

<Button v-else round icon="ve-icon-rollback" @click="handleBack">返回</Button>code>

</div>

</div>

</div>

</template>

vue3-wechat布局模板

在这里插入图片描述

<code><template>

<div class="vu__container" :style="{'--themeSkin': appstate.config.skin}">code>

<div class="vu__layout">code>

<div class="vu__layout-body">code>

<!-- 菜单栏 -->

<slot v-if="!route?.meta?.hideMenuBar" name="menubar">code>

<MenuBar />

</slot>

<!-- 侧边栏 -->

<div v-if="route?.meta?.showSideBar" class="vu__layout-sidebar" :class="{'hidden': appstate.config.collapsed}">code>

<aside class="vu__layout-sidebar__body flexbox flex-col">code>

<slot name="sidebar">code>

<SideBar />

</slot>

<!-- 折叠 -->

<Collapse />

</aside>

</div>

<!-- 内容区 -->

<div class="vu__layout-main flex1 flexbox flex-col">code>

<Winbtn v-if="!route?.meta?.hideWinBar" />code>

<router-view v-slot="{ Component, route }">code>

<keep-alive>

<component :is="Component" :key="route.path" />code>

</keep-alive>

</router-view>

</div>

</div>

</div>

</div>

</template>

vue3路由管理

在这里插入图片描述

<code>/**

* 路由管理Router

* @author andy

*/

import { createRouter, createWebHashHistory } from 'vue-router'

import { authState } from '@/pinia/modules/auth'

import Layout from '@/layouts/index.vue'

// 批量导入路由

const modules = import.meta.glob('./modules/*.js', { eager: true })

const patchRouters = Object.keys(modules).map(key => modules[key].default).flat()

/**

* meta配置

* @param meta.requireAuth 需登录验证页面

* @param meta.hideWinBar 隐藏右上角按钮组

* @param meta.hideMenuBar 隐藏菜单栏

* @param meta.showSideBar 显示侧边栏

* @param meta.canGoBack 是否可回退上一页

*/

const routes = [

...patchRouters,

// 错误模块

{

path: '/:pathMatch(.*)*',

redirect: '/404',

component: Layout,

meta: {

title: '404error',

hideMenuBar: true,

hideWinBar: true,

},

children: [

{

path: '404',

component: () => import('@/views/error/404.vue'),

}

]

},

]

const router = createRouter({

history: createWebHashHistory(),

routes,

})

// 全局路由钩子拦截

router.beforeEach((to, from) => {

const authstate = authState()

// 登录验证

if(to?.meta?.requireAuth && !authstate.authorization) {

console.log('你还未登录!')

return {

path: '/login'

}

}

})

router.afterEach((to, from) => {

// 阻止浏览器回退

if(to?.meta?.canGoBack == false && from.path != null) {

history.pushState(history.state, '', document.URL)

}

})

vue3小视频模块

vite5-wechat项目加入了小视频模块。使用swiper组件实现上下滑动小视频。

在这里插入图片描述

视频底部mini播放进度条,采用<code>Slider组件实现功能。支持拖拽到指定时间点。

在这里插入图片描述

<code><!-- 短视频模块 -->

<div class="vu__video-container">code>

<!-- tabs操作栏 -->

<div class="vu__video-tabswrap flexbox">code>

<el-tabs v-model="activeName" class="vu__video-tabs">code>

<el-tab-pane label="关注" name="attention" />code>

<el-tab-pane label="推荐" name="recommend" />code>

</el-tabs>

</div>

<swiper-container

class="vu__swiper"code>

direction="vertical"code>

:speed="150"code>

:grabCursor="true"code>

:mousewheel="{invert: true}"code>

@swiperslidechange="onSlideChange"code>

>

<swiper-slide v-for="(item, index) in videoList" :key="index">code>

<!-- 视频层 -->

<video

class="vu__player"code>

:id="'vuplayer-' + index"code>

:src="item.src"code>

:poster="item.poster"code>

loop

preload="auto"code>

:autoplay="index == currentVideo"code>

webkit-playsinline="true" code>

x5-video-player-type="h5-page"code>

x5-video-player-fullscreen="true"code>

playsinline

@click="handleVideoClicked"code>

>

</video>

<div v-if="!isPlaying" class="vu__player-btn" @click="handleVideoClicked"></div>code>

<!-- 右侧操作栏 -->

<div class="vu__video-toolbar">code>

...

</div>

<!-- 底部信息区域 -->

<div class="vu__video-footinfo flexbox flex-col">code>

<div class="name">@{ { item.author}}</div>code>

<div class="content">{ { item.desc}}</div>code>

</div>

</swiper-slide>

</swiper-container>

<!-- ///底部进度条 -->

<el-slider class="vu__video-progressbar" v-model="progressBar" @input="handleSlider" @change="handlePlay" />code>

<div v-if="isDraging" class="vu__video-duration">{ { videoTime}} / { { videoDuration}}</div>code>

</div>

vue3聊天模块

在这里插入图片描述

聊天输入框支持多行文本输入、光标处插入gif图片、粘贴截图发送图片等功能。

在这里插入图片描述

在这里插入图片描述

<code><template>

<!-- 顶部导航 -->

...

<!-- 内容区 -->

<div class="vu__layout-main__body">code>

<Scrollbar ref="scrollRef" autohide gap="2">code>

<!-- 渲染聊天内容 -->

<div class="vu__chatview" @dragenter="handleDragEnter" @dragover="handleDragOver" @drop="handleDrop">code>

...

</div>

</Scrollbar>

</div>

<!-- 底部操作栏 -->

<div class="vu__footview">code>

<div class="vu__toolbar flexbox">code>

...

</div>

<div class="vu__editor">code>

<Editor ref="editorRef" v-model="editorValue" @paste="handleEditorPaste" />code>

</div>

<div class="vu__submit">code>

<button @click="handleSubmit">发送(S)</button>code>

</div>

</div>

...

</template>

vue3使用高德地图。

// 拾取地图位置

let map = null

const handlePickMapLocation = () => {

popoverChooseRef?.value?.hide()

mapLocationVisible.value = true

// 初始化地图

AMapLoader.load({

key: "af10789c28b6ef1929677bc5a2a3d443", // 申请好的Web端开发者Key,首次调用 load 时必填

version: "2.0", // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15

}).then((AMap) => {

// JS API 加载完成后获取AMap对象

map = new AMap.Map("vu__mapcontainer", {

viewMode: "3D", // 默认使用 2D 模式

zoom: 10, // 初始化地图级别

resizeEnable: true,

})

// 获取当前位置

AMap.plugin('AMap.Geolocation', function() {

var geolocation = new AMap.Geolocation({

// 是否使用高精度定位,默认:true

enableHighAccuracy: true,

// 设置定位超时时间,默认:无穷大

timeout: 10000,

// 定位按钮的停靠位置的偏移量,默认:Pixel(10, 20)

buttonOffset: new AMap.Pixel(10, 20),

// 定位成功后调整地图视野范围使定位位置及精度范围视野内可见,默认:false

zoomToAccuracy: true,

// 定位按钮的排放位置, RB表示右下

buttonPosition: 'RB'

})

map.addControl(geolocation)

geolocation.getCurrentPosition(function(status, result){

if(status == 'complete'){

onComplete(result)

}else{

onError(result)

}

})

})

// 定位成功的回调函数

function onComplete(data) {

var str = ['定位成功']

str.push('经度:' + data.position.getLng())

str.push('纬度:' + data.position.getLat())

if(data.accuracy){

str.push('精度:' + data.accuracy + ' 米')

}

// 可以将获取到的经纬度信息进行使用

console.log(str.join('<br>'))

}

// 定位失败的回调函数

function onError(data) {

console.log('定位失败:' + data.message)

}

}).catch((e) => {

// 加载错误提示

console.log('amapinfo', e)

})

}

// 打开预览地图位置

const handleOpenMapLocation = (data) => {

mapLocationVisible.value = true

// 初始化地图

AMapLoader.load({

key: "af10789c28b6ef1929677bc5a2a3d443", // 申请好的Web端开发者Key,首次调用 load 时必填

version: "2.0", // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15

}).then((AMap) => {

// JS API 加载完成后获取AMap对象

map = new AMap.Map("vu__mapcontainer", {

viewMode: "3D", // 默认使用 2D 模式

zoom: 13, // 初始化地图级别

center: [data.longitude, data.latitude], // 初始化地图中心点位置

})

// 添加插件

AMap.plugin(["AMap.ToolBar", "AMap.Scale", "AMap.HawkEye"], function () {

//异步同时加载多个插件

map.addControl(new AMap.ToolBar()) // 缩放工具条

map.addControl(new AMap.HawkEye()) // 显示缩略图

map.addControl(new AMap.Scale()) // 显示当前地图中心的比例尺

})

mapPosition.value = [data.longitude, data.latitude]

addMarker()

// 实例化点标记

function addMarker() {

const marker = new AMap.Marker({

icon: "//a.amap.com/jsapi_demos/static/demo-center/icons/poi-marker-default.png",

position: mapPosition.value,

offset: new AMap.Pixel(-26, -54),

})

marker.setMap(map)

/* marker.setLabel({

direction:'top',

offset: new AMap.Pixel(0, -10), //设置文本标注偏移量

content: "<div class='info'>我是 marker 的 label 标签</div>", //设置文本标注内容code>

}) */

//鼠标点击marker弹出自定义的信息窗体

marker.on('click', function () {

infoWindow.open(map, marker.getPosition())

})

const infoWindow = new AMap.InfoWindow({

offset: new AMap.Pixel(0, -60),

content: `

<div style="padding: 10px;">code>

<p style="font-size: 14px;">${ data.name}</p>code>

<p style="color: #999; font-size: 12px;">${ data.address}</p>code>

</div>

`

})

}

}).catch((e) => {

// 加载错误提示

console.log('amapinfo', e)

})

}

// 关闭预览地图位置

const handleCloseMapLocation = () => {

map?.destroy()

mapLocationVisible.value = false

}

OK,以上就是vue3+vite5+element-plus开发网页聊天项目的一些分享。

https://blog.csdn.net/yanxinyun1990/article/details/139510447

https://blog.csdn.net/yanxinyun1990/article/details/138317354

https://blog.csdn.net/yanxinyun1990/article/details/136996521

在这里插入图片描述



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。