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

View File

@ -1104,25 +1104,38 @@ export default {
/** /**
* 发送消息 * 发送消息
* @param text * @param text
* @param type
*/ */
sendMsg(text) { sendMsg(text, type) {
let msgText; let textBody,
let emptied = false; textType = "text",
silence = "no",
emptied = false;
if (typeof text === "string" && text) { if (typeof text === "string" && text) {
msgText = text; textBody = text;
} else { } else {
msgText = this.msgText; textBody = this.msgText;
emptied = true; emptied = true;
} }
if (msgText == '') { if (type === "md") {
textBody = this.$refs.input.getText()
textType = "md"
} else if (type === "silence") {
silence = "yes"
}
if (textBody == '') {
this.inputFocus(); this.inputFocus();
return; return;
} }
msgText = msgText.replace(/<\/span> <\/p>$/, "</span></p>") if (textType === "text") {
textBody = textBody.replace(/<\/span> <\/p>$/, "</span></p>")
}
// //
if (this.quoteUpdate) { 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 const update_id = this.quoteId
this.$store.dispatch("setLoad", { this.$store.dispatch("setLoad", {
key: `msg-${update_id}`, key: `msg-${update_id}`,
@ -1136,7 +1149,9 @@ export default {
data: { data: {
dialog_id: this.dialogId, dialog_id: this.dialogId,
update_id, update_id,
text: msgText, text: textBody,
text_type: textType,
silence,
}, },
method: 'post', method: 'post',
complete: _ => this.$store.dispatch("cancelLoad", `msg-${update_id}`) complete: _ => this.$store.dispatch("cancelLoad", `msg-${update_id}`)
@ -1148,7 +1163,7 @@ export default {
}); });
} else { } else {
// //
const typeLoad = $A.stringLength(msgText.replace(/<img[^>]*?>/g, '')) > 5000 const typeLoad = $A.stringLength(textBody.replace(/<img[^>]*?>/g, '')) > 5000
const tempMsg = { const tempMsg = {
id: this.getTempId(), id: this.getTempId(),
dialog_id: this.dialogData.id, dialog_id: this.dialogData.id,
@ -1157,7 +1172,8 @@ export default {
type: typeLoad ? 'loading' : 'text', type: typeLoad ? 'loading' : 'text',
userid: this.userId, userid: this.userId,
msg: { msg: {
text: typeLoad ? '' : msgText, text: typeLoad ? '' : textBody,
type: textType,
}, },
} }
this.tempMsgs.push(tempMsg) this.tempMsgs.push(tempMsg)
@ -1171,7 +1187,9 @@ export default {
data: { data: {
dialog_id: tempMsg.dialog_id, dialog_id: tempMsg.dialog_id,
reply_id: tempMsg.reply_id, reply_id: tempMsg.reply_id,
text: msgText, text: textBody,
text_type: textType,
silence,
}, },
method: 'post', method: 'post',
}).then(({data}) => { }).then(({data}) => {
@ -1179,7 +1197,7 @@ export default {
this.sendSuccess(data) this.sendSuccess(data)
}).catch(error => { }).catch(error => {
this.$set(tempMsg, 'error', true) 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) { if (emptied) {
@ -2244,16 +2262,20 @@ export default {
const {type} = this.operateItem const {type} = this.operateItem
this.onReply(type === 'text' ? 'update' : 'reply') this.onReply(type === 'text' ? 'update' : 'reply')
if (type === 'text') { if (type === 'text') {
let {text} = this.operateItem.msg let {text, type} = 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, "");
})
this.$refs.input.setPasteMode(false) 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)) this.$nextTick(_ => this.$refs.input.setPasteMode(true))
} }
}, },

View File

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