This commit is contained in:
kuaifan 2022-05-26 15:57:43 +08:00
parent 173f5c84db
commit ac45ba633b
21 changed files with 512 additions and 365 deletions

View File

@ -96,5 +96,8 @@
"url": "https://t.hitosea.com/desktop/publish"
}
}
]
],
"dependencies": {
"grapheme-splitter": "^1.0.4"
}
}

2
public/css/app.css vendored

File diff suppressed because one or more lines are too long

Binary file not shown.

Binary file not shown.

Binary file not shown.

2
public/js/app.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
98d215c910f5969e
6c5499276179e032

View File

@ -1,19 +1,24 @@
<template>
<div class="chat-input-box">
<div class="chat-input-wrapper" :class="modeClass" @click.stop="focus">
<div ref="editor" class="no-dark-content" :style="editorStyle" @click.stop="" @paste="handlePaste"></div>
<div class="chat-input-toolbar" @click.stop="">
<slot name="toolbarBefore"/>
<EPopover
v-if="$isDesktop"
v-model="showEmoji"
:visibleArrow="false"
placement="top"
popperClass="chat-input-emoji-popover">
<ETooltip slot="reference" ref="emojiTip" :disabled="!$isDesktop || showEmoji" placement="top" :content="$L('表情')">
<i class="taskfont" @click="onToolbar('emoji')">&#xe7ad;</i>
<i class="taskfont">&#xe7ad;</i>
</ETooltip>
<ChatEmoji @on-select="onSelectEmoji"/>
</EPopover>
<ETooltip v-else ref="emojiTip" :disabled="!$isDesktop || showEmoji" placement="top" :content="$L('表情')">
<i class="taskfont" @click="showEmoji=!showEmoji">&#xe7ad;</i>
</ETooltip>
<ETooltip placement="top" :disabled="!$isDesktop" :content="$L('选择会员')">
<i class="taskfont" @click="onToolbar('user')">&#xe78f;</i>
@ -28,7 +33,7 @@
placement="top"
popperClass="chat-input-more-popover">
<ETooltip slot="reference" ref="moreTip" :disabled="!$isDesktop || showMore" placement="top" :content="$L('展开')">
<i class="taskfont" @click="onToolbar('more')">&#xe790;</i>
<i class="taskfont">&#xe790;</i>
</ETooltip>
<div class="chat-input-popover-item" @click="onToolbar('image')">
<i class="taskfont">&#xe64a;</i>
@ -50,6 +55,10 @@
<slot name="toolbarAfter"/>
</div>
</div>
<template v-if="!$isDesktop">
<ChatEmoji v-if="showEmoji" @on-select="onSelectEmoji"/>
</template>
</div>
</template>
<script>
@ -109,6 +118,7 @@ export default {
data() {
return {
quill: null,
rangeIndex: 0,
_content: '',
_options: {},
@ -124,7 +134,6 @@ export default {
wrapperWidth: 0,
editorHeight: 0,
timerScroll: null,
isSpecVersion: this.checkIOSVersion(),
};
},
@ -144,7 +153,6 @@ export default {
this.observer.observe(this.$refs.editor);
},
beforeDestroy() {
this.inputCache(this.dialogId, this.value);
if (this.quill) {
this.quill = null
}
@ -171,15 +179,16 @@ export default {
},
watch: {
// Watch content change
value(newVal) {
value(val) {
if (this.quill) {
if (newVal && newVal !== this._content) {
this._content = newVal
this.setContent(newVal)
} else if(!newVal) {
if (val && val !== this._content) {
this._content = val
this.setContent(val)
} else if(!val) {
this.quill.setText('')
}
}
this.setInputCache(val)
},
// Watch disabled change
@ -190,11 +199,10 @@ export default {
},
// Reset lists
dialogId(id1, id2) {
dialogId() {
this.userList = null;
this.taskList = null;
this.inputCache(id2, this.value)
this.$emit('input', this.inputCache(id1))
this.$emit('input', this.getInputCache())
},
taskId() {
this.userList = null;
@ -202,19 +210,29 @@ export default {
},
showEmoji(val) {
if (val) {
this.showMore = false;
if (this.quill) {
const range = this.quill.selection.savedRange;
this.rangeIndex = range ? range.index : 0
}
}
if (!val && this.$refs.emojiTip) {
this.$refs.emojiTip.updatePopper()
}
},
showMore(val) {
if (val) {
this.showEmoji = false;
}
if (!val && this.$refs.moreTip) {
this.$refs.moreTip.updatePopper()
}
},
dialogInputCache() {
this.$emit('input', this.inputCache(this.dialogId))
this.$emit('input', this.getInputCache())
}
},
methods: {
@ -302,7 +320,7 @@ export default {
if (this.value) {
this.setContent(this.value)
} else {
this.$emit('input', this.inputCache(this.dialogId))
this.$emit('input', this.getInputCache())
}
// Disabled editor
@ -312,25 +330,17 @@ export default {
// Mark model as touched if editor lost focus
this.quill.on('selection-change', range => {
if (this.timerScroll) {
clearInterval(this.timerScroll);
}
if (!range) {
this.$emit('on-blur', this.quill)
this.inputCache(this.dialogId, this.value)
} else {
this.$emit('on-focus', this.quill)
this.showEmoji = false
this.showMore = false
this.hidePopover()
if (this.isSpecVersion) {
// ios11.0-11.3 scrollTopscrolIntoViewbug
//
} else {
setTimeout(() => {
$A.scrollToView(this.$refs.editor, true)
this.timerScroll = setInterval(() => {
$A.scrollToView(this.$refs.editor, true)
}, 300);
}, 300);
}
}
@ -378,11 +388,14 @@ export default {
}
},
inputCache(key, cache) {
if (cache === undefined) {
getInputCache() {
const key = this.dialogId;
const item = this.dialogInputCache.find(item => item.key == key);
return item ? item.cache : '';
}
},
setInputCache(cache) {
const key = this.dialogId;
const index = this.dialogInputCache.findIndex(item => item.key == key);
const data = {key, cache}
if (index > -1) {
@ -390,9 +403,10 @@ export default {
} else {
this.$store.state.dialogInputCache.push(data)
}
setTimeout(_ => {
this.__setInputCache && clearTimeout(this.__setInputCache);
this.__setInputCache = setTimeout(_ => {
$A.setStorage("cacheDialogInput", this.$store.state.dialogInputCache);
})
}, 600)
},
focus() {
@ -412,7 +426,11 @@ export default {
return;
}
this.$emit('on-send')
this.inputCache(this.dialogId, null)
},
hidePopover() {
this.showEmoji = false;
this.showMore = false;
},
onSelectEmoji(item) {
@ -422,21 +440,21 @@ export default {
if (item.type === 'emoji') {
let element = document.createElement('span');
element.innerHTML = item.html;
this.quill.insertText(this.quill.getSelection(true).index, element.innerHTML);
this.quill.insertText(this.rangeIndex, element.innerHTML);
this.rangeIndex += element.innerHTML.length
element = null;
if (this.$isDesktop) {
this.showEmoji = false;
this.quill.setSelection(this.rangeIndex)
}
} else if (item.type === 'emoticon') {
this.$emit('on-send', `<img class="emoticon" data-asset="${item.asset}" data-name="${item.name}" src="${item.src}"/>`)
}
this.showEmoji = false;
}
},
onToolbar(action) {
if (action !== 'emoji') {
this.showEmoji = false;
}
if (action !== 'more') {
this.showMore = false;
}
this.hidePopover();
switch (action) {
case 'user':
this.openMenu("@");

View File

@ -400,6 +400,7 @@
<div class="no-tip">{{$L('暂无消息')}}</div>
<div class="no-input">
<ChatInput
ref="chatInput"
:task-id="taskId"
v-model="msgText"
:disabled="sendLoad > 0"
@ -408,7 +409,7 @@
:placeholder="$L('输入消息...')"
@on-more="onEventMore"
@on-file="onSelectFile"
@on-send="msgDialog">
@on-send="onSend">
<Badge slot="toolbarAfter" :count="taskDetail.msg_num"/>
</ChatInput>
</div>
@ -744,6 +745,7 @@ export default {
this.receiveShow = false;
this.$refs.owner && this.$refs.owner.handleClose();
this.$refs.assist && this.$refs.assist.handleClose();
this.$refs.chatInput && this.$refs.chatInput.hidePopover();
}
},
immediate: true
@ -1172,6 +1174,11 @@ export default {
this.msgDialog()
},
onSend() {
this.$refs.chatInput && this.$refs.chatInput.hidePopover();
this.msgDialog();
},
deleteFile(file) {
this.$set(file, '_show_menu', false);
this.$store.dispatch("forgetTaskFile", file.id)

View File

@ -410,6 +410,7 @@ body.dark-mode-reverse {
}
}
.chat-input-box {
.chat-input-wrapper {
.ql-container {
.ql-editor {
@ -426,3 +427,4 @@ body.dark-mode-reverse {
}
}
}
}

View File

@ -1,6 +1,11 @@
@import "~quill/dist/quill.bubble.css";
@import "~quill-mention/dist/quill.mention.min.css";
.chat-input-box {
display: flex;
flex-direction: column;
width: 100%;
.chat-input-wrapper {
display: inline-block;
width: 100%;
@ -308,6 +313,60 @@
}
}
}
.chat-emoji-wrapper {
.chat-emoji-box {
width: auto;
padding: 8px 2px;
display: flex;
justify-content: space-around;
flex-wrap: wrap;
&::after {
content: "";
flex: auto;
}
> li {
transition: none;
&:hover {
transform: none;
}
}
}
.chat-emoji-menu {
width: 100%;
padding: 3px 0;
border-radius: 8px;
box-sizing: content-box;
> li {
position: relative;
&:before {
display: none;
content: "";
position: absolute;
top: 50%;
left: 50%;
width: 36px;
height: 36px;
border-radius: 8px;
transform: translate(-50%, -50%);
background-color: #ffffff;
z-index: 1;
}
> span,
> img {
position: static;
z-index: 2;
}
&.active {
background-color: transparent;
&:before {
display: block;
}
}
}
}
}
}
.chat-input-emoji-popover {
padding: 0;
@ -414,3 +473,47 @@
}
}
}
@media (max-width: 768px) {
.chat-input-box {
.chat-input-wrapper {
padding-left: 6px;
padding-right: 6px;
background-color: #ffffff;
}
.chat-emoji-wrapper {
margin-top: 8px;
margin-left: -10px;
margin-bottom: -8px;
width: calc(100% + 20px);
background-color: #ffffff;
.chat-emoji-box {
height: 246px;
> li {
width: 50px;
height: 50px;
line-height: 50px;
font-size: 28px;
}
&.emoticon > li {
width: 80px;
height: 80px;
padding: 8px;
}
}
.chat-emoji-menu {
border-radius: 0;
background-color: #f8f8f8;
padding: 4px;
width: calc(100% - 8px);
> li {
&.active {
&:before {
background-color: #e1e1e1;
}
}
}
}
}
}
}

View File

@ -687,14 +687,18 @@
display: flex;
align-items: center;
.chat-input-wrapper {
.chat-input-box {
flex: 1;
width: 0;
display: flex;
flex-direction: column;
.chat-input-wrapper {
background-color: #F4F5F7;
padding: 10px 12px;
border-radius: 10px;
}
}
}
.chat-upload {
display: none;
@ -918,12 +922,14 @@
padding: 8px 10px;
margin-bottom: 0;
.dialog-input {
.chat-input-box {
.chat-input-wrapper {
padding-left: 6px;
padding-right: 8px;
padding-right: 6px;
background-color: #ffffff;
}
}
}
}
}
}

View File

@ -570,15 +570,23 @@
align-items: center;
margin: 22px 0 0 36px;
background-color: #F4F5F7;
padding: 10px 12px;
padding: 10px 11px;
border-radius: 10px;
.chat-input-box {
.chat-input-wrapper {
padding: 0;
background-color: #F4F5F7;
}
}
.chat-input-toolbar {
position: relative;
.ivu-badge {
position: absolute;
transform: scale(0.6);
top: -6px;
right: 14px;
transform: scale(0.6) translateX(100%);
transform-origin: right center;
top: -8px;
right: 10px;
z-index: 1;
}
}
}