界面大概效果
页面样式大概就是仿了微信、lark等。
这期先说前端大概的吧。 后面一期我再说一下后端。
后管端
后管主要用到的是Vue3 + vite + elemnt ui
.其他界面没有比较复杂的。就聊天界面,需要保持,左侧的聊天框和中间的对话框以及右边的用户端 进行联动。
Vue3用到的状态管理是 pinia
。项目中大概建立了这么些个store用来控制管理。
图片预览用到的是v-viewer
。具体使用这里就不阐述了,网上一大堆。
然后就是前段这里处理字节,上传文件了。
/**
* 文件处理成字节
*
* @param file
* @param callback
*/
export function readFileAsByteArray(file: any, callback: Function) {
var reader: any = new FileReader()
reader.onload = function (event: any) {
var byteArray = new Uint8Array(reader.result)
callback(byteArray)
}
reader.readAsArrayBuffer(file)
}
至于websocket地方基本就是 依据后端定义好的类型。 进行处理会话,由于不可能每次来消息都要请求后端,类似未读消息,前端这里会存入store里。 还有一些置顶啊,标记未读啊等方法。
import { defineStore } from 'pinia'
import { useUserStore } from './userStore'
import { useMessageStore } from './messageStore'
import { useChatListStore } from './chatListStore'
//@ts-ignore
import { v4 as uuidv4 } from 'uuid'
import { SessionChatRecordResponse } from '@/server/session_request'
import moment from 'moment'
// import { Base64 } from 'js-base64'
interface IWebSocket {
ws: any
isConnected: boolean
//是否主动关闭socket
isTakeClose: boolean
interval: any
sessionId: string
userId: string
merchantInfo: IMessageMerchantParam
userInfo: IMessageUserParam
}
interface IMessageMerchantParam {
merchantId?: string
merchantName?: string
merchantImgUrl?: string
}
interface IMessageUserParam {
userId?: string
userImgUrl?: string
}
interface IMessageChatParam {
userType?: UserType
MessageType?: MessageType
content?: string
}
interface IMessageParam {
message: IMessageChatParam[]
}
/**
* 消息类型
*/
export enum MessageType {
Audio = 'AUDIO',
Emoji = 'EMOJI',
File = 'FILE',
Image = 'IMAGE',
Text = 'TEXT',
Tip = 'TIP',
Video = 'VIDEO'
}
export enum UserType {
CustomerService = 'CUSTOMER_SERVICE',
System = 'SYSTEM',
User = 'USER'
}
export const useWebSocketStore = defineStore('websocket', {
state: (): IWebSocket => {
return {
ws: null,
isConnected: true,
isTakeClose: false,
interval: null,
sessionId: '',
userId: '',
merchantInfo: {},
userInfo: {}
}
},
actions: {
//---------------------------功能相关------------------------------------
/**
* 塞入sessionId
* @param value
*/
setSessionId(value: string) {
if (!value) return
this.sessionId = value
},
/**
* 塞入UserId
*/
setUserId(value: string) {
if (!value) return
this.userId = value
},
//--------------------------websocket相关--------------------------------------
//连接
connect() {
if (this.ws && this.ws.readyState == 1) {
console.log(`连接已建立,无需再次连接`)
return
}
const wsUrl = import.meta.env.VITE_WS_URL
this.ws = new WebSocket(`${wsUrl}`)
this.ws.addEventListener('open', () => {
console.log('WebSocket连接已建立')
this.isConnected = true
this.isTakeClose = false
// this.cancelHeart()
})
this.ws.addEventListener('close', (e: any) => {
this.isConnected = false
console.error('WebSocket连接断开', JSON.stringify(e))
if (!this.isTakeClose) {
console.error('WebSocket意外断开,正在尝试重新连接...')
setTimeout(() => {
this.connect()
}, 2000)
}
})
this.ws.addEventListener('message', (event: any) => {
//一大坨逻辑。这里就不放出来了。
})
},
/**
* 发送websocket消息
*
* @param data
*/
send(data: any) {
try {
if (this.isConnected) {
this.ws.send(data)
} else {
console.warn('webSocket is closed')
}
} catch (error) {
console.log(error)
}
},
/**
* 关闭websocket连接
*/
close() {
this.isConnected = false
this.isTakeClose = true
//清空定时器
this.ws && this.ws.close()
this.ws &&
this.ws.removeEventListener('message', () => {
console.log('断开消息监听')
})
this.ws &&
this.ws.removeEventListener('open', () => {
console.log('断开连接监听')
})
this.ws &&
this.ws.removeEventListener('close', () => {
console.log('断开关闭监听')
})
this.cancelHeart()
},
/**
* 维持socket心跳
*
*/
keepHeart() {
if (this.interval) {
console.log('定时器已存在,无需ping')
return
}
this.interval = setInterval(() => {
this.send('ping')
}, 6000)
},
/**
* 取消socket心跳
*/
cancelHeart() {
this.interval && clearInterval(this.interval)
},
updateChatListStore(messageInfo: any, isNeedUpdate: boolean) {
},
presist: {
path: ['interval'],
storage: sessionStorage
}
}
})
使用emoji.
emoji
H5这里,我使用的是。vue3-emoji-picker
。
后管端,是fork了一份开源的项目,改了一下。
icon
icon方案一开始用的是iconfont . 后面使用了 iconpark. 然后使用的时候注意一下,在main.ts
里设置一下。标记iconpark为自定义组件。 不然会一直报错。
//解决iconpark-icon报错问题 (Failed to resolve component: iconpark-icon)
app.config.compilerOptions.isCustomElement = (tag) => tag === 'iconpark-icon'
用户端
用户端更没啥说的。一个很简单的聊天界面。 适配做好就行, 我这里的适配方案基本用的是 vw + vh
+ rem
以上
个人比较懒 想到什么就写什么。源码暂时先不分享了。因为前端权限管理那里,我本来图省事,组件、路由啥,基本都是v-if
来控制,不够优雅。等我改一下,改成RBAC
模式。后端用的是Sa-token