perf: 优化聊天输入框

This commit is contained in:
kuaifan 2024-04-16 19:02:53 +08:00 committed by Pang
parent 6823d87198
commit cc125cc292
6 changed files with 70 additions and 30 deletions

View File

@ -47,8 +47,8 @@
"openpgp_hi": "^5.7.0-1", "openpgp_hi": "^5.7.0-1",
"photoswipe": "^5.2.8", "photoswipe": "^5.2.8",
"postcss": "^8.4.5", "postcss": "^8.4.5",
"quill": "^2.0.0-rc.5", "quill-hitosea": "^2.0.0-rc.5.1",
"quill-mention-hi": "^4.0.0-2", "quill-mention-hitosea": "^4.0.0-6",
"resolve-url-loader": "^4.0.0", "resolve-url-loader": "^4.0.0",
"sass": "^1.71.1", "sass": "^1.71.1",
"sass-loader": "^14.1.1", "sass-loader": "^14.1.1",

View File

@ -40,7 +40,9 @@
@paste="handlePaste"></div> @paste="handlePaste"></div>
<!-- 工具栏占位 --> <!-- 工具栏占位 -->
<div class="chat-space"></div> <div class="chat-space">
<input class="space-input" @focus="onSpaceInputFocus"/>
</div>
<!-- 工具栏 --> <!-- 工具栏 -->
<ul class="chat-toolbar" @click.stop> <ul class="chat-toolbar" @click.stop>
@ -211,8 +213,8 @@
<script> <script>
import {mapState} from "vuex"; import {mapState} from "vuex";
import Quill from 'quill'; import Quill from 'quill-hitosea';
import "quill-mention-hi"; import "quill-mention-hitosea";
import ChatEmoji from "./emoji"; import ChatEmoji from "./emoji";
import touchmouse from "../../../../directives/touchmouse"; import touchmouse from "../../../../directives/touchmouse";
import touchclick from "../../../../directives/touchclick"; import touchclick from "../../../../directives/touchclick";
@ -324,10 +326,11 @@ export default {
emojiTimer: null, emojiTimer: null,
scrollTimer: null, scrollTimer: null,
selectTimer: null,
textTimer: null, textTimer: null,
fileTimer: null, fileTimer: null,
moreTimer: null, moreTimer: null,
selectTimer: null,
selectRange: null,
fullInput: false, fullInput: false,
fullQuill: null, fullQuill: null,
@ -640,7 +643,7 @@ export default {
keyboard: { keyboard: {
bindings: { bindings: {
'short enter': { 'short enter': {
key: 13, key: "Enter",
shortKey: true, shortKey: true,
handler: _ => { handler: _ => {
if (!this.isEnterSend) { if (!this.isEnterSend) {
@ -651,7 +654,7 @@ export default {
} }
}, },
'enter': { 'enter': {
key: 13, key: "Enter",
shiftKey: false, shiftKey: false,
handler: _ => { handler: _ => {
if (this.isEnterSend) { if (this.isEnterSend) {
@ -662,7 +665,7 @@ export default {
} }
}, },
'esc': { 'esc': {
key: 27, key: "Escape",
shiftKey: false, shiftKey: false,
handler: _ => { handler: _ => {
if (this.emojiQuickShow) { if (this.emojiQuickShow) {
@ -691,16 +694,16 @@ export default {
// Mark model as touched if editor lost focus // Mark model as touched if editor lost focus
this.quill.on('selection-change', range => { this.quill.on('selection-change', range => {
if (!range && document.activeElement) { if (range) {
// this.selectRange = range
if (['ql-editor', 'ql-clipboard'].includes(document.activeElement.className)) { } else if (this.selectRange && document.activeElement && /(ql-editor|ql-clipboard)/.test(document.activeElement.className)) {
// iOS
this.selectTimer && clearTimeout(this.selectTimer) this.selectTimer && clearTimeout(this.selectTimer)
this.selectTimer = setTimeout(_ => { this.selectTimer = setTimeout(_ => {
this.quill.setSelection(document.activeElement.className === 'ql-editor' ? 0 : this.quill.getLength()) this.quill.setSelection(this.selectRange.index, this.selectRange.length)
}, 100) }, 100)
return return
} }
}
this.isFocus = !!range; this.isFocus = !!range;
}) })
@ -765,8 +768,8 @@ export default {
this.$nextTick(_ => { this.$nextTick(_ => {
this.quill.root.addEventListener('keydown', e => { this.quill.root.addEventListener('keydown', e => {
if (e.key === '\r\r' && e.keyCode === 229) { if (e.key === '\r\r' && e.keyCode === 229) {
const length = this.quill.getSelection(true).index; const {index} = this.quill.getSelection(true);
this.quill.insertText(length, "\r\n"); this.quill.insertText(index, "\r\n");
// //
this.keyTimer && clearTimeout(this.keyTimer) this.keyTimer && clearTimeout(this.keyTimer)
this.keyTimer = setTimeout(_ => { this.keyTimer = setTimeout(_ => {
@ -950,7 +953,7 @@ export default {
setContent(value) { setContent(value) {
if (this.quill) { if (this.quill) {
this.quill.setContents(this.quill.clipboard.convert(value)) this.quill.setContents(this.quill.clipboard.convert({html: value}))
} }
}, },
@ -1310,6 +1313,13 @@ export default {
}) })
}, },
onSpaceInputFocus() {
if (this.selectRange) {
// Android
this.quill?.setSelection(this.selectRange.index, this.selectRange.length)
}
},
openMenu(char) { openMenu(char) {
if (!this.quill) { if (!this.quill) {
return; return;
@ -1330,7 +1340,10 @@ export default {
if (!this.quill) { if (!this.quill) {
return; return;
} }
this.quill.getModule("mention").insertItem(data, true); const {index} = this.quill.getSelection(true);
this.quill.insertEmbed(index, "mention", data, Quill.sources.USER);
this.quill.insertText(index + 1, " ", Quill.sources.USER);
this.quill.setSelection(index + 2, Quill.sources.USER);
}, },
getProjectId() { getProjectId() {
@ -1665,9 +1678,9 @@ export default {
array.forEach(image => { array.forEach(image => {
const t = new FileReader; const t = new FileReader;
t.onload = ({target}) => { t.onload = ({target}) => {
const length = this.quill.getSelection(true).index; const {index} = this.quill.getSelection(true);
this.quill.insertEmbed(length, "image", target.result); this.quill.insertEmbed(index, "image", target.result);
this.quill.setSelection(length + 1) this.quill.setSelection(index + 1)
}; };
t.readAsDataURL(image) t.readAsDataURL(image)
}) })

View File

@ -501,7 +501,7 @@ export default {
recordStyle(info) { recordStyle(info) {
const {duration} = info; const {duration} = info;
const width = 50 + Math.min(180, Math.floor(duration / 150)); const width = 50 + Math.min(180, Math.floor(duration / 200));
return { return {
width: width + 'px', width: width + 'px',
}; };

View File

@ -194,6 +194,12 @@ body.dark-mode-reverse {
} }
} }
.dialog-position {
.position-label {
color: #000000;
}
}
.dialog-scroller { .dialog-scroller {
.dialog-item { .dialog-item {
.dialog-view { .dialog-view {
@ -541,6 +547,12 @@ body.dark-mode-reverse {
} }
} }
.chat-input-record-transfer {
&.cancel {
color: #000000;
}
}
.ql-mention-list-container { .ql-mention-list-container {
.ql-mention-list-item { .ql-mention-list-item {
.mention-item-at { .mention-item-at {

View File

@ -1,5 +1,5 @@
@import "~quill/dist/quill.bubble.css"; @import "~quill-hitosea/dist/quill.bubble.css";
@import "~quill-mention-hi/dist/quill.mention.min.css"; @import "~quill-mention-hitosea/dist/quill.mention.min.css";
.chat-input-box { .chat-input-box {
display: inline-block; display: inline-block;
@ -224,6 +224,21 @@
float: right; float: right;
width: 170px; width: 170px;
height: 30px; height: 30px;
.space-input {
border: none;
outline: none;
box-shadow: none;
background: transparent;
height: 1px;
width: 1px;
overflow: hidden;
&:focus,
&:active {
border: none;
outline: none;
box-shadow: none;
}
}
} }
.chat-toolbar { .chat-toolbar {

4
vite.config.js vendored
View File

@ -49,8 +49,8 @@ export default defineConfig(({command, mode}) => {
resolve: { resolve: {
alias: { alias: {
'~element-sea': resolve(__dirname, 'node_modules/element-sea'), '~element-sea': resolve(__dirname, 'node_modules/element-sea'),
'~quill': resolve(__dirname, 'node_modules/quill'), '~quill-hitosea': resolve(__dirname, 'node_modules/quill-hitosea'),
'~quill-mention-hi': resolve(__dirname, 'node_modules/quill-mention-hi'), '~quill-mention-hitosea': resolve(__dirname, 'node_modules/quill-mention-hitosea'),
'../images': resolve(__dirname, command === 'serve' ? '/images' : 'resources/assets/statics/public/images'), '../images': resolve(__dirname, command === 'serve' ? '/images' : 'resources/assets/statics/public/images'),
'../css': resolve(__dirname, command === 'serve' ? '/css' : 'resources/assets/statics/public/css') '../css': resolve(__dirname, command === 'serve' ? '/css' : 'resources/assets/statics/public/css')
}, },