perf(system): 优化艾特功能

This commit is contained in:
nongyehong 2024-03-30 19:41:00 +08:00
parent 8bd1f85043
commit f828546f1f
5 changed files with 42 additions and 38 deletions

View File

@ -8,7 +8,6 @@
contenteditable
spellcheck="false"
autofocus
@blur="saveCursor"
@paste="handlePaste"
@input="handleInput"
@keydown.enter="inputKeyDown"></div>
@ -17,7 +16,7 @@
<!-- @提及框 -->
<div v-if="ait && activeItem.type === RoomTypeEnum.GROUP" class="ait">
<n-virtual-list id="image-chat-msgInput" style="max-height: 180px" :item-size="26" :items="MockList">
<n-virtual-list id="image-chat-msgInput" style="max-height: 180px" :item-size="36" :items="MockList">
<template #default="{ item }">
<n-flex @click="handleAit(item)" :key="item.key" align="center" class="ait-item">
<n-avatar
@ -96,8 +95,6 @@ const menuList = ref([
const msgInput = ref('')
// dom
const messageInputDom = ref()
/* 光标的位置 */
const cursorLocat = ref()
const activeItem = ref(inject('activeItem') as MockItem)
/**
@ -113,7 +110,19 @@ const insertNode = (type: MsgEnum, dom: any) => {
//
range?.deleteContents()
//
if (type === MsgEnum.TEXT) {
if (type === MsgEnum.AIT) {
// span
const spanNode = document.createElement('span')
spanNode.id = 'aitSpan' // idaitSpan
spanNode.contentEditable = 'false' //
spanNode.classList.add('text-#13987f')
spanNode.classList.add('select-none')
spanNode.classList.add('cursor-default')
// span
spanNode.appendChild(document.createTextNode(`@${dom} `))
// span
range?.insertNode(spanNode)
} else if (type === MsgEnum.TEXT) {
range?.insertNode(document.createTextNode(dom))
} else {
range?.insertNode(dom)
@ -221,6 +230,7 @@ const getMessageContentType = () => {
/* 处理发送信息事件 */
// TODO (nyh -> 2024-03-01 07:03:43)
const send = () => {
ait.value = false
const contentType = getMessageContentType()
const msg = {
type: contentType,
@ -266,7 +276,7 @@ const handleInput = (e: Event) => {
const dom = document.querySelector('.ait') as HTMLElement
dom.style.position = 'fixed'
dom.style.left = `${res?.x - 20}px`
dom.style.top = `${res?.y - 175}px`
dom.style.top = `${res?.y - (dom.offsetHeight + 5)}px`
})
}
}
@ -289,27 +299,17 @@ const inputKeyDown = (e: KeyboardEvent) => {
}
}
/* 失焦时保存光标 */
const saveCursor = () => {
if (!window.getSelection) {
return null
}
const sel = window.getSelection()
if (sel?.getRangeAt && sel.rangeCount) {
cursorLocat.value = sel.getRangeAt(0)
}
}
/* 处理点击@提及框事件 */
// TODO @ (nyh -> 2024-03-29 23:43:34)
const handleAit = (item: MockItem) => {
// @
msgInput.value = msgInput.value.substring(0, msgInput.value.length - 1)
messageInputDom.value.innerHTML = msgInput.value
// ()
messageInputDom.value.focus()
const sel = window.getSelection()
sel?.removeAllRanges()
//
sel?.addRange(cursorLocat.value)
insertNode(MsgEnum.TEXT, item.accountName)
const res = sel?.getRangeAt(0)
res?.setStart(messageInputDom.value, messageInputDom.value.childNodes.length)
insertNode(MsgEnum.AIT, item.accountName)
triggerInputEvent(messageInputDom.value)
ait.value = false
}
@ -354,7 +354,7 @@ onUnmounted(() => {
word-break: break-word; /* 在长单词或URL地址内部进行换行 */
}
.ait {
@apply w-200px h-160px bg-[--center-bg-color] rounded-8px p-[5px_0_5px_5px];
@apply w-200px h-fit max-h-190px bg-[--center-bg-color] rounded-8px p-[5px_0_5px_5px];
box-shadow: 2px 2px 12px 2px var(--box-shadow-color);
border: 1px solid var(--box-shadow-color);
.ait-item {

View File

@ -94,8 +94,8 @@ export enum MsgEnum {
FILE,
/** 表情 */
EMOTICON,
/** 超链接 */
HYPERLINK
/** 艾特 */
AIT
}
/** 房间类型 1群聊 2单聊 */

View File

@ -9,7 +9,7 @@ import { WebviewWindow } from '@tauri-apps/api/window'
const { createWebviewWindow, checkWinExist } = useWindow()
/* 建议把此状态存入localStorage中 */
const activeItem = ref(-1)
const activeIndex = ref(-1)
const msgBoxShow = ref(false)
/* 独立窗口的集合 */
const aloneWin = ref(new Set())
@ -26,13 +26,13 @@ export const useMessage = () => {
/* 处理点击选中消息 */
const handleMsgClick = (item: MockItem) => {
msgBoxShow.value = true
activeItem.value = item.key
activeIndex.value = item.key
const data = { msgBoxShow, item }
Mitt.emit(MittEnum.MSG_BOX_SHOW, data)
// 判断是否打开了独立的窗口
if (aloneWin.value.has(EventEnum.ALONE + item.key)) {
checkWinExist(EventEnum.ALONE + item.key).then()
activeItem.value = -1
activeIndex.value = -1
Mitt.emit(MittEnum.MSG_BOX_SHOW, { item: -1 })
}
// 如果是收缩页面状态点击消息框就直接变成独立窗口
@ -51,8 +51,8 @@ export const useMessage = () => {
/* 打开独立窗口 */
const openAloneWin = async (item: MockItem) => {
itemRef.value = { ...item }
if (activeItem.value === item.key) {
activeItem.value = -1
if (activeIndex.value === item.key) {
activeIndex.value = -1
Mitt.emit(MittEnum.MSG_BOX_SHOW, { item: -1 })
}
// TODO 传递用户信息(这里的label最好使用用户唯一的id来代替) (nyh -> 2024-03-18 12:18:10)
@ -107,14 +107,14 @@ export const useMessage = () => {
// 如果找到了对应的元素,则移除
if (index !== -1) {
const removeItem = MockList.value.splice(index, 1)[0]
if (activeItem.value === removeItem.key) {
if (activeIndex.value === removeItem.key) {
if (index < MockList.value.length) {
// 需要使用新的索引位置找到key更新activeItem.value
activeItem.value = MockList.value[index].key
activeIndex.value = MockList.value[index].key
handleMsgClick(MockList.value[index])
} else {
// 如果我们删除的是最后一个元素,则需要选中前一个元素
activeItem.value = MockList.value[MockList.value.length - 1].key
activeIndex.value = MockList.value[MockList.value.length - 1].key
handleMsgClick(MockList.value[MockList.value.length - 1])
}
}
@ -135,5 +135,5 @@ export const useMessage = () => {
})
})
return { activeItem, msgBoxShow, handleMsgClick, handleMsgDblclick, menuList, specialMenuList }
return { activeIndex, msgBoxShow, handleMsgClick, handleMsgDblclick, menuList, specialMenuList }
}

View File

@ -51,4 +51,8 @@
}
.max:hover {
background-color: #f5dce1;
}
/*! 替换ait的字体颜色 */
:deep(#aitSpan) {
@apply text-inherit cursor-pointer;
}

View File

@ -12,7 +12,7 @@
:data-key="item.key"
:menu="menuList"
:special-menu="specialMenuList"
:class="{ active: activeItem === item.key }"
:class="{ active: activeIndex === item.key }"
class="msg-box w-full h-75px mb-5px"
v-for="item in MockList"
:key="item.key">
@ -55,7 +55,7 @@ import { MittEnum } from '@/enums'
const msgTotal = ref(0)
const scrollbar = ref()
const { activeItem, handleMsgClick, menuList, specialMenuList, handleMsgDblclick } = useMessage()
const { activeIndex, handleMsgClick, menuList, specialMenuList, handleMsgDblclick } = useMessage()
watchEffect(() => {
// TODO (nyh -> 2024-03-22 01:05:22)
@ -64,9 +64,9 @@ watchEffect(() => {
onMounted(() => {
// TODO keydata-key (nyh -> 2024-03-28 18:56:20)
if (activeItem.value !== -1) {
if (activeIndex.value !== -1) {
nextTick(() => {
const activeElement = document.querySelector(`.msg-box[data-key="${activeItem.value}"]`) as HTMLElement
const activeElement = document.querySelector(`.msg-box[data-key="${activeIndex.value}"]`) as HTMLElement
const rect = activeElement.getBoundingClientRect()
scrollbar.value.scrollTo({
top: rect.top - 75,