feat: 添加emoji表情删除按钮

This commit is contained in:
kuaifan 2025-09-26 20:18:12 +08:00
parent 0fb66358cc
commit 1b30582dd9
3 changed files with 117 additions and 5 deletions

View File

@ -16,13 +16,26 @@
</div>
<Scrollbar>
<ul :class="[type, 'no-dark-content']">
<li v-for="item in list" @click="onSelect($event, item)">
<li v-for="(item, index) in list" :key="index" @click="onSelect($event, item)">
<Imgs v-if="item.type === 'emoticon'" :src="item.src" :title="item.name" :alt="item.name"/>
<span v-else v-html="item.html" :title="item.name"></span>
</li>
<template v-if="type === 'emoji'">
<li class="delete-placeholder"></li>
<li class="delete-placeholder"></li>
</template>
</ul>
</Scrollbar>
</div>
<div
v-if="showEmojiDelete && type === 'emoji'"
class="chat-emoji-delete-btn"
@click="onDelete">
<svg viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" width="64" height="64" stroke="currentColor" fill="currentColor">
<path d="M889.202 167.878H343.626c-22.574 0-45.15 11.28-60.202 26.322L76.484 457.442c-22.576 30.084-22.576 67.688 0 94.016l206.94 263.236c15.052 18.802 37.628 30.084 60.202 30.084h545.572c41.39 0 75.252-33.842 75.252-75.208V243.092c-0.002-41.37-33.864-75.214-75.248-75.214z m3.76 601.69H347.39l-206.94-263.24 203.176-263.236h549.336v526.476z"></path>
<path d="M410.164 641.746c0 11.292 3.764 18.822 11.306 26.352 15.07 11.292 33.918 11.292 45.224 0l113.06-112.934 113.058 112.934c7.536 3.766 11.306 7.526 22.612 7.526s18.842-7.526 22.612-11.292c3.77-7.53 7.536-15.06 7.536-22.588 0-7.53 0-15.06-7.536-22.586l-113.058-112.94 113.058-112.934c3.77-3.766 7.536-11.296 7.536-18.822 0-15.062-15.072-26.352-22.612-30.118l-3.766-3.764h-3.77c-7.536 0-15.076 0-22.612 7.53l-113.058 112.934-116.83-120.466c-3.77-3.766-11.306-7.532-18.842-7.532-11.306 0-18.846 3.766-26.382 11.296-3.766 7.532-7.536 15.056-7.536 22.588 0 11.296 3.764 18.822 7.536 22.588l113.06 120.466-113.06 112.934c-3.766 3.772-7.536 11.298-7.536 18.828z"></path>
</svg>
</div>
<div v-if="!onlyEmoji" class="chat-emoji-menu-wrap">
<span v-show="showEmojiMenuScrollLeftBtn" class="left-btn" @click="onEmojiMenuScroll('left')"><i class="taskfont">&#xe72d;</i></span>
<ul ref="chatEmojiMenuRef" class="chat-emoji-menu" @scroll="onHandleScroll">
@ -32,7 +45,7 @@
<li :class="{active: type === 'emoji'}" @click="type='emoji'">
<span class="no-dark-content">&#128512;</span>
</li>
<li v-for="item in emoticonData" :class="{active: type === 'emoticon' && emoticonPath == item.path}" @click="onEmoticon(item.path)">
<li v-for="(item, index) in emoticonData" :key="index" :class="{active: type === 'emoticon' && emoticonPath == item.path}" @click="onEmoticon(item.path)">
<Imgs :title="item.name" :alt="item.name" :src="item.src"/>
</li>
</ul>
@ -52,6 +65,11 @@ export default {
onlyEmoji: {
type: Boolean,
default: false
},
// emoji
showEmojiDelete: {
type: Boolean,
default: false
}
},
data() {
@ -222,6 +240,10 @@ export default {
}
},
onDelete() {
this.$emit('on-delete');
},
onMonitorWheel() {
const container = this.$refs['chatEmojiMenuRef'];
container?.addEventListener("wheel", (event) =>{

View File

@ -227,7 +227,12 @@
</div>
<!-- 移动端表情底部 -->
<ChatEmoji v-if="emojiBottom && showEmoji" @on-select="onSelectEmoji" :searchKey="emojiQuickKey"/>
<ChatEmoji
v-if="emojiBottom && showEmoji"
@on-select="onSelectEmoji"
@on-delete="onEmojiDelete"
:searchKey="emojiQuickKey"
showEmojiDelete/>
<!-- 录音浮窗 -->
<transition name="fade">
@ -414,6 +419,7 @@ export default {
quill: null,
isFocus: false,
rangeIndex: 0,
rangeLength: 0,
_content: '',
_options: {},
@ -868,9 +874,13 @@ export default {
if (this.quill) {
const range = this.quill.selection.savedRange;
this.rangeIndex = range ? range.index : 0
this.rangeLength = range ? range.length : 0
}
} else {
this.rangeLength = 0;
if (this.rangeIndex > 0) {
this.quill.setSelection(this.rangeIndex)
}
} else if (this.rangeIndex > 0) {
this.quill.setSelection(this.rangeIndex)
}
},
@ -1741,6 +1751,8 @@ export default {
if (item.type === 'emoji') {
this.quill.insertText(this.rangeIndex, item.text);
this.rangeIndex += item.text.length
this.rangeLength = 0;
this.quill.setSelection(this.rangeIndex, 0, 'silent');
if (this.windowLandscape && !this.isModKey) {
this.showEmoji = false;
}
@ -1755,6 +1767,58 @@ export default {
}
},
onEmojiDelete() {
if (!this.quill) {
return;
}
const savedRange = this.quill.selection?.savedRange || this.quill.getSelection();
if (savedRange && typeof savedRange.index === 'number') {
this.rangeIndex = savedRange.index;
this.rangeLength = savedRange.length || 0;
}
if (this.rangeLength > 0) {
this.quill.deleteText(this.rangeIndex, this.rangeLength);
this.rangeLength = 0;
} else if (this.rangeIndex > 0) {
const deleteLength = this.getPreviousGraphemeLength(this.rangeIndex);
if (deleteLength > 0) {
this.quill.deleteText(this.rangeIndex - deleteLength, deleteLength);
this.rangeIndex -= deleteLength;
}
}
this.quill.setSelection(this.rangeIndex, 0, 'silent');
},
getPreviousGraphemeLength(index) {
if (!this.quill || index <= 0) {
return 0;
}
const textBeforeCursor = this.quill.getText(0, index);
if (!textBeforeCursor) {
return 0;
}
if (typeof Intl !== 'undefined' && typeof Intl.Segmenter === 'function') {
if (!this.graphemeSegmenter) {
this.graphemeSegmenter = new Intl.Segmenter(undefined, {granularity: 'grapheme'});
}
let lastSegment;
for (const segment of this.graphemeSegmenter.segment(textBeforeCursor)) {
lastSegment = segment;
}
if (lastSegment && lastSegment.segment) {
return lastSegment.segment.length;
}
}
const fallbackWindow = Math.min(index, 8);
const fallbackText = this.quill.getText(index - fallbackWindow, fallbackWindow);
if (!fallbackText) {
return 0;
}
const fallbackGraphemes = Array.from(fallbackText);
const lastGrapheme = fallbackGraphemes.pop();
return lastGrapheme ? lastGrapheme.length : 0;
},
onToolbar(action) {
this.hidePopover();
switch (action) {

View File

@ -511,6 +511,28 @@
flex-direction: column;
position: relative;
.chat-emoji-delete-btn {
position: absolute;
right: 12px;
bottom: 60px;
border: none;
padding: 10px 14px;
border-radius: 8px;
background: #eee;
color: #666;
font-size: 20px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
outline: none;
> svg {
width: 24px;
height: 24px;
}
}
.chat-emoji-emosearch {
flex-shrink: 0;
padding: 8px 8px 0;
@ -1298,6 +1320,10 @@ body.window-portrait {
height: 50px;
line-height: 50px;
font-size: 28px;
&.delete-placeholder {
height: 60px;
}
}
&.emosearch,
&.emoticon {