perf: 右键或长按消息发送按钮可选无声发送、Markdown格式发送

This commit is contained in:
kuaifan 2023-04-04 20:57:21 +08:00
parent 1cef313689
commit 0aa6911159
3 changed files with 151 additions and 43 deletions

View File

@ -36,7 +36,7 @@
@paste="handlePaste"></div>
<!-- 工具栏 -->
<ul class="chat-toolbar" @click.stop="">
<ul class="chat-toolbar" @click.stop>
<!-- 桌面端表情漂浮 -->
<li>
<EPopover
@ -101,22 +101,42 @@
</li>
<!-- 发送按钮 -->
<li class="chat-send" :class="sendClass" v-touchmouse="clickSend">
<ETooltip placement="top" :disabled="windowSmall || $isEEUiApp" :content="$L(sendClass === 'recorder' ? '长按录音' : '发送')">
<div v-if="loading">
<div class="chat-load">
<Loading/>
<li
ref="chatSend"
class="chat-send"
:class="sendClass"
v-touchmouse="clickSend"
v-longpress="{callback: longSend, delay: 300}">
<EPopover
v-model="showMenu"
:visibleArrow="false"
trigger="manual"
placement="top"
popperClass="chat-input-more-popover">
<ETooltip slot="reference" ref="sendTip" placement="top" :disabled="windowSmall || $isEEUiApp || showMenu" :content="$L(sendContent)">
<div v-if="loading">
<div class="chat-load">
<Loading/>
</div>
</div>
<div v-else>
<transition name="mobile-send">
<i v-if="sendClass === 'recorder'" class="taskfont">&#xe609;</i>
</transition>
<transition name="mobile-send">
<i v-if="sendClass !== 'recorder'" class="taskfont">&#xe606;</i>
</transition>
</div>
</ETooltip>
<div class="chat-input-popover-item" @click="onSend('silence')">
<i class="taskfont">&#xe7d7;</i>
{{$L('无声发送')}}
</div>
<div v-else>
<transition name="mobile-send">
<i v-if="sendClass === 'recorder'" class="taskfont">&#xe609;</i>
</transition>
<transition name="mobile-send">
<i v-if="sendClass !== 'recorder'" class="taskfont">&#xe606;</i>
</transition>
<div class="chat-input-popover-item" @click="onSend('md')">
<i class="taskfont">&#xe647;</i>
{{$L('Markdown 格式发送')}}
</div>
</ETooltip>
</EPopover>
</li>
<!-- 录音效果 -->
@ -156,12 +176,13 @@ import ChatEmoji from "./emoji";
import touchmouse from "../../../../directives/touchmouse";
import TransferDom from "../../../../directives/transfer-dom";
import clickoutside from "../../../../directives/clickoutside";
import longpress from "../../../../directives/longpress";
import {Store} from "le5le-store";
export default {
name: 'ChatInput',
components: {ChatEmoji},
directives: {touchmouse, TransferDom, clickoutside},
directives: {touchmouse, TransferDom, clickoutside, longpress},
props: {
value: {
type: [String, Number],
@ -226,10 +247,12 @@ export default {
taskList: null,
fileList: {},
showMenu: false,
showMore: false,
showEmoji: false,
emojiQuickTimer: null,
emojiQuickShow: false,
emojiQuickTimer: null,
emojiQuickKey: '',
emojiQuickItems: [],
@ -356,6 +379,9 @@ export default {
array.push('record-ready');
}
}
if (this.showMenu) {
array.push('show-menu');
}
if (this.showMore) {
array.push('show-more');
}
@ -378,6 +404,20 @@ export default {
return ''
},
sendContent() {
const {sendTip} = this.$refs
if (sendTip && sendTip.$refs.popper) {
sendTip.$refs.popper.style.visibility = 'hidden'
sendTip.showPopper = false
setTimeout(_ => {
if (sendTip.$refs.popper) {
sendTip.$refs.popper.style.visibility = 'visible'
}
}, 300)
}
return this.sendClass === 'recorder' ? '长按录音' : '发送'
},
recordFormatDuration() {
const {recordDuration} = this;
let minute = Math.floor(recordDuration / 60000),
@ -446,6 +486,24 @@ export default {
this.loadInputDraft()
},
showMenu(val) {
if (val) {
// this.showMenu = false;
this.showMore = false;
this.showEmoji = false;
this.emojiQuickShow = false;
}
},
showMore(val) {
if (val) {
this.showMenu = false;
// this.showMore = false;
this.showEmoji = false;
this.emojiQuickShow = false;
}
},
showEmoji(val) {
if (this.emojiBottom) {
if (val) {
@ -466,7 +524,9 @@ export default {
this.emojiQuickKey = "";
}
//
this.showMenu = false;
this.showMore = false;
// this.showEmoji = false;
this.emojiQuickShow = false;
if (this.quill) {
const range = this.quill.selection.savedRange;
@ -478,11 +538,13 @@ export default {
this.$emit('on-emoji-visible-change', val)
},
showMore(val) {
emojiQuickShow(val) {
if (val) {
this.showMenu = false;
this.showMore = false;
this.showEmoji = false;
// this.emojiQuickShow = false;
}
this.$emit('on-more-visible-change', val)
},
isFocus(val) {
@ -787,6 +849,13 @@ export default {
}, 100)
},
getText() {
if (this.quill) {
return this.quill.getText()
}
return "";
},
setText(value) {
if (this.quill) {
this.quill.setText(value)
@ -843,7 +912,7 @@ export default {
this.touchLimitX = false;
this.touchLimitY = false;
this.touchStart = event.type === "touchstart" ? event.touches[0] : event;
if (this.startRecord()) {
if (event.button === 0 && this.startRecord()) {
return;
}
break;
@ -855,6 +924,9 @@ export default {
break;
case 'up':
if (this.showMenu) {
return;
}
if (this.stopRecord(this.touchLimitY)) {
return;
}
@ -866,10 +938,22 @@ export default {
}
},
onSend() {
longSend() {
if (this.sendClass === 'recorder') {
return;
}
this.showMenu = true;
},
onSend(type) {
this.hidePopover()
this.rangeIndex = 0
this.$store.state.messengerSearchKey = {dialog: '', contacts: ''}
this.$emit('on-send')
if (type) {
this.$emit('on-send', null, type)
} else {
this.$emit('on-send')
}
},
startRecord() {
@ -931,6 +1015,7 @@ export default {
},
hidePopover() {
this.showMenu = false;
this.showMore = false;
this.showEmoji = false;
this.emojiQuickShow = false;

View File

@ -1104,25 +1104,38 @@ export default {
/**
* 发送消息
* @param text
* @param type
*/
sendMsg(text) {
let msgText;
let emptied = false;
sendMsg(text, type) {
let textBody,
textType = "text",
silence = "no",
emptied = false;
if (typeof text === "string" && text) {
msgText = text;
textBody = text;
} else {
msgText = this.msgText;
textBody = this.msgText;
emptied = true;
}
if (msgText == '') {
if (type === "md") {
textBody = this.$refs.input.getText()
textType = "md"
} else if (type === "silence") {
silence = "yes"
}
if (textBody == '') {
this.inputFocus();
return;
}
msgText = msgText.replace(/<\/span> <\/p>$/, "</span></p>")
if (textType === "text") {
textBody = textBody.replace(/<\/span> <\/p>$/, "</span></p>")
}
//
if (this.quoteUpdate) {
//
msgText = msgText.replace(new RegExp(`src=(["'])${$A.apiUrl('../')}`, "g"), "src=$1{{RemoteURL}}")
if (textType === "text") {
textBody = textBody.replace(new RegExp(`src=(["'])${$A.apiUrl('../')}`, "g"), "src=$1{{RemoteURL}}")
}
const update_id = this.quoteId
this.$store.dispatch("setLoad", {
key: `msg-${update_id}`,
@ -1136,7 +1149,9 @@ export default {
data: {
dialog_id: this.dialogId,
update_id,
text: msgText,
text: textBody,
text_type: textType,
silence,
},
method: 'post',
complete: _ => this.$store.dispatch("cancelLoad", `msg-${update_id}`)
@ -1148,7 +1163,7 @@ export default {
});
} else {
//
const typeLoad = $A.stringLength(msgText.replace(/<img[^>]*?>/g, '')) > 5000
const typeLoad = $A.stringLength(textBody.replace(/<img[^>]*?>/g, '')) > 5000
const tempMsg = {
id: this.getTempId(),
dialog_id: this.dialogData.id,
@ -1157,7 +1172,8 @@ export default {
type: typeLoad ? 'loading' : 'text',
userid: this.userId,
msg: {
text: typeLoad ? '' : msgText,
text: typeLoad ? '' : textBody,
type: textType,
},
}
this.tempMsgs.push(tempMsg)
@ -1171,7 +1187,9 @@ export default {
data: {
dialog_id: tempMsg.dialog_id,
reply_id: tempMsg.reply_id,
text: msgText,
text: textBody,
text_type: textType,
silence,
},
method: 'post',
}).then(({data}) => {
@ -1179,7 +1197,7 @@ export default {
this.sendSuccess(data)
}).catch(error => {
this.$set(tempMsg, 'error', true)
this.$set(tempMsg, 'errorData', {type: 'text', content: error.msg, msg: msgText})
this.$set(tempMsg, 'errorData', {type: 'text', content: error.msg, msg: textBody})
});
}
if (emptied) {
@ -2244,16 +2262,20 @@ export default {
const {type} = this.operateItem
this.onReply(type === 'text' ? 'update' : 'reply')
if (type === 'text') {
let {text} = this.operateItem.msg
if (text.indexOf("mention") > -1) {
text = text.replace(/<a class="mention file" href="([^'"]*)"([^>]*)>~([^>]*)<\/a>/g, '<span class="mention" data-denotation-char="~" data-id="$1" data-value="$3">&#xFEFF;<span contenteditable="false"><span class="ql-mention-denotation-char">~</span>$3</span>&#xFEFF;</span>')
text = text.replace(/<span class="mention ([^'"]*)" data-id="(\d+)">([@#])([^>]*)<\/span>/g, '<span class="mention" data-denotation-char="$3" data-id="$2" data-value="$4">&#xFEFF;<span contenteditable="false"><span class="ql-mention-denotation-char">$3</span>$4</span>&#xFEFF;</span>')
}
text = text.replace(/<img[^>]*>/gi, match => {
return match.replace(/(width|height)="\d+"\s*/ig, "");
})
let {text, type} = this.operateItem.msg
this.$refs.input.setPasteMode(false)
this.msgText = $A.formatMsgBasic(text)
if (type === 'md') {
this.$refs.input.setText(text)
} else {
if (text.indexOf("mention") > -1) {
text = text.replace(/<a class="mention file" href="([^'"]*)"([^>]*)>~([^>]*)<\/a>/g, '<span class="mention" data-denotation-char="~" data-id="$1" data-value="$3">&#xFEFF;<span contenteditable="false"><span class="ql-mention-denotation-char">~</span>$3</span>&#xFEFF;</span>')
text = text.replace(/<span class="mention ([^'"]*)" data-id="(\d+)">([@#])([^>]*)<\/span>/g, '<span class="mention" data-denotation-char="$3" data-id="$2" data-value="$4">&#xFEFF;<span contenteditable="false"><span class="ql-mention-denotation-char">$3</span>$4</span>&#xFEFF;</span>')
}
text = text.replace(/<img[^>]*>/gi, match => {
return match.replace(/(width|height)="\d+"\s*/ig, "");
})
this.msgText = $A.formatMsgBasic(text)
}
this.$nextTick(_ => this.$refs.input.setPasteMode(true))
}
},

View File

@ -43,6 +43,7 @@
}
}
&.show-menu,
&.show-more,
&.show-emoji {
.chat-input-wrapper {