perf: 优化转发消息

This commit is contained in:
kuaifan 2025-03-16 20:43:09 +08:00
parent 2d5ce87605
commit 62b40ddb84
7 changed files with 489 additions and 326 deletions

View File

@ -466,89 +466,14 @@
</div>
</Modal>
<!-- 转发选择 -->
<UserSelect
ref="forwardSelect"
:multiple-max="50"
<!-- 转发 -->
<Forwarder
ref="forwarder"
:title="$L('转发')"
:before-submit="onForwardBefore"
:show-select-all="false"
show-dialog
module/>
<!-- 转发确认 -->
<Modal
v-model="forwardhow"
:title="`${$L('转发给')}:`"
class-name="common-user-select-modal dialog-forward-message-modal"
:mask-closable="false"
width="420">
<div class="user-modal-search">
<Scrollbar class="search-selected" enable-x :enable-y="false">
<ul>
<li v-for="item in forwardData" :data-id="item.userid">
<div v-if="item.type=='group'" class="user-modal-avatar">
<EAvatar v-if="item.avatar" class="img-avatar" :src="item.avatar" :size="32"></EAvatar>
<i v-else-if="item.group_type=='department'" class="taskfont icon-avatar department">&#xe75c;</i>
<i v-else-if="item.group_type=='project'" class="taskfont icon-avatar project">&#xe6f9;</i>
<i v-else-if="item.group_type=='task'" class="taskfont icon-avatar task">&#xe6f4;</i>
<i v-else-if="item.group_type=='okr'" class="taskfont icon-avatar task">&#xe6f4;</i>
<Icon v-else class="icon-avatar" type="ios-people" />
<div v-if="forwardData.length == 1" class="avatar-name">
<span>{{item.name}}</span>
</div>
</div>
<UserAvatar v-else :userid="item.userid" :size="32" :show-name="forwardData.length == 1"/>
</li>
</ul>
</Scrollbar>
</div>
<div class="twice-affirm-body-extend">
<div class="dialog-wrapper-forward-body">
<div class="dialog-wrapper inde-list">
<Scrollbar class-name="dialog-scroller">
<DialogItem
:source="operateItem"
@on-view-text="onViewText"
@on-view-file="onViewFile"
@on-down-file="onDownFile"
@on-emoji="onEmoji"
@on-other="onOther"
simpleView/>
</Scrollbar>
</div>
<div class="leave-message">
<ChatInput
v-if="forwardDialogId > 0"
v-model="forwardMessage"
:dialog-id="forwardDialogId"
:emoji-bottom="windowPortrait"
:maxlength="200000"
:placeholder="$L('留言')"
disabled-record
simple-mode/>
<Input
v-else
type="textarea"
:autosize="{minRows: 1,maxRows: 3}"
v-model="forwardMessage"
:maxlength="200000"
:placeholder="$L('留言')"
clearable/>
</div>
</div>
</div>
<template #footer>
<div class="dialog-wrapper-forward-footer" :class="{selected: !forwardSource}" @click="forwardSource = !forwardSource">
<Icon class="user-modal-icon" :type="forwardSource ? 'ios-radio-button-off' : 'ios-checkmark-circle'" />
<span class="forward-text-tip">{{$L('不显示原发送者信息')}}</span>
</div>
<Button type="primary" :loading="forwardLoad > 0" @click="onForwardAffirm">
{{$L('确定')}}
<template v-if="forwardData.length > 0">({{forwardData.length}})</template>
</Button>
</template>
</Modal>
:confirm-title="$L('转发确认')"
:multiple-max="50"
:msg-detail="operateItem"
:before-submit="onForward"/>
<!-- 设置待办 -->
<Modal
@ -724,10 +649,12 @@ import touchclick from "../../../directives/touchclick";
import {languageList} from "../../../language";
import {isLocalResourcePath} from "../../../components/Replace/utils";
import emitter from "../../../store/events";
import Forwarder from "./Forwarder/index.vue";
export default {
name: "DialogWrapper",
components: {
Forwarder,
UserAvatarTip,
UserSelect,
ImgUpload,
@ -805,13 +732,6 @@ export default {
modifyData: {},
modifyLoad: 0,
forwardhow: false,
forwardData: [],
forwardLoad: 0,
forwardDialogId: 0,
forwardMessage: '',
forwardSource: true,
openId: 0,
errorId: 0,
dialogDrag: false,
@ -2944,55 +2864,26 @@ export default {
});
},
onForwardBefore() {
onForward(forwardData) {
return new Promise((resolve, reject) => {
this.forwardData = this.$refs.forwardSelect.formatSelect(this.$refs.forwardSelect.selects);
if (this.forwardData.length === 0) {
$A.messageError("请选择转发对话或成员");
} else {
this.forwardDialogId = 0;
if (this.forwardData.length === 1) {
const {type, userid} = this.forwardData[0];
if (type === "group" && /^d:/.test(userid)) {
this.forwardDialogId = parseInt(userid.replace(/^d:/, ''));
}
this.$store.dispatch("call", {
url: 'dialog/msg/forward',
data: {
dialogids: forwardData.dialogids,
userids: forwardData.userids,
msg_id: forwardData.msg_id,
show_source: forwardData.sender ? 1 : 0,
leave_message: forwardData.message
}
this.forwardMessage = '';
this.forwardSource = true;
this.forwardhow = true;
}
reject();
})
},
onForwardAffirm() {
const selects = this.$refs.forwardSelect.selects;
if (selects.length === 0) {
$A.messageError("请选择转发对话或成员");
return
}
const dialogids = selects.filter(value => $A.leftExists(value, 'd:')).map(value => value.replace('d:', ''));
const userids = selects.filter(value => !$A.leftExists(value, 'd:'));
this.forwardLoad++;
this.$store.dispatch("call", {
url: 'dialog/msg/forward',
data: {
dialogids,
userids,
msg_id: this.operateItem.id,
show_source: this.forwardSource ? 1 : 0,
leave_message: this.forwardMessage
}
}).then(({data, msg}) => {
this.$store.dispatch("saveDialogMsg", data.msgs);
this.$store.dispatch("updateDialogLastMsg", data.msgs);
$A.messageSuccess(msg);
this.$refs.forwardSelect.hide()
this.forwardhow = false;
}).catch(({msg}) => {
$A.modalError(msg);
}).finally(_ => {
this.forwardLoad--;
}).then(({data, msg}) => {
this.$store.dispatch("saveDialogMsg", data.msgs);
this.$store.dispatch("updateDialogLastMsg", data.msgs);
$A.messageSuccess(msg);
resolve()
}).catch(({msg}) => {
$A.modalError(msg);
reject()
});
});
},
@ -3240,7 +3131,7 @@ export default {
break;
case "forward":
this.$refs.forwardSelect.onSelection()
this.$refs.forwarder.onSelection()
break;
case "withdraw":

View File

@ -0,0 +1,203 @@
<template>
<!-- 转发确认 -->
<Modal
v-model="show"
:title="title"
class-name="common-user-select-modal forwarder-message-modal"
:mask-closable="false"
width="420">
<div class="user-modal-search">
<Scrollbar class="search-selected" enable-x :enable-y="false">
<ul>
<li v-for="item in forwardTo" :data-id="item.userid">
<div v-if="item.type=='group'" :title="item.name" class="user-modal-avatar">
<EAvatar v-if="item.avatar" class="img-avatar" :src="item.avatar" :size="32"></EAvatar>
<i v-else-if="item.group_type=='department'" class="taskfont icon-avatar department">&#xe75c;</i>
<i v-else-if="item.group_type=='project'" class="taskfont icon-avatar project">&#xe6f9;</i>
<i v-else-if="item.group_type=='task'" class="taskfont icon-avatar task">&#xe6f4;</i>
<i v-else-if="item.group_type=='okr'" class="taskfont icon-avatar task">&#xe6f4;</i>
<Icon v-else class="icon-avatar" type="ios-people" />
<div v-if="forwardTo.length == 1" class="avatar-name">
<span>{{item.name}}</span>
</div>
</div>
<UserAvatar v-else :userid="item.userid" :size="32" :show-name="forwardTo.length == 1"/>
</li>
</ul>
</Scrollbar>
</div>
<div class="twice-affirm-body-extend">
<div class="forwarder-wrapper-body">
<div class="dialog-wrapper inde-list">
<Scrollbar class-name="dialog-scroller">
<DialogItem
:source="msgDetail"
@on-view-text="onViewText"
@on-view-file="onViewFile"
@on-down-file="onDownFile"
@on-emoji="onEmoji"
@on-other="onOther"
simpleView/>
</Scrollbar>
</div>
<div class="leave-message">
<ChatInput
v-if="dialogId > 0"
v-model="message"
:dialog-id="dialogId"
:emoji-bottom="windowPortrait"
:maxlength="200000"
:placeholder="$L('留言')"
:disabled="loading"
disabled-record
simple-mode/>
<Input
v-else
type="textarea"
:autosize="{minRows: 1,maxRows: 3}"
v-model="message"
:maxlength="200000"
:placeholder="$L('留言')"
:disabled="loading"
clearable/>
</div>
</div>
</div>
<template #footer>
<div class="forwarder-wrapper-footer" :class="{selected: !sender}" @click="onSender">
<Icon class="user-modal-icon" :type="sender ? 'ios-radio-button-off' : 'ios-checkmark-circle'" />
<span class="forward-text-tip">{{$L('不显示原发送者信息')}}</span>
</div>
<Button type="primary" :loading="loading" @click="onSubmit">
{{$L('确定')}}
<template v-if="forwardTo.length > 0">({{forwardTo.length}})</template>
</Button>
</template>
</Modal>
</template>
<script>
import DialogItem from "../DialogItem.vue";
import ChatInput from "../ChatInput/index.vue";
export default {
components: {ChatInput, DialogItem},
props: {
value: {
type: Boolean,
default: false
},
//
title: {
type: String,
default: ''
},
//
senderHidden: {
type: Boolean,
default: false
},
//
beforeSubmit: Function,
// ID
dialogId: {
type: Number,
default: 0
},
//
forwardTo: {
type: Array,
default: () => []
},
//
msgDetail: {
type: Object,
default: () => ({})
},
},
data() {
return {
show: false,
loading: false,
message: '', //
sender: true, //
}
},
computed: {
},
watch: {
value(val) {
this.show = val;
},
show(val) {
this.$emit('input', val);
if (!val) {
this.loading = false;
this.message = '';
this.sender = true;
}
}
},
methods: {
onViewText(...args) {
this.$emit('on-view-text', ...args);
},
onViewFile(...args) {
this.$emit('on-view-file', ...args);
},
onDownFile(...args) {
this.$emit('on-down-file', ...args);
},
onEmoji(...args) {
this.$emit('on-emoji', ...args);
},
onOther(...args) {
this.$emit('on-other', ...args);
},
onSender() {
if (this.loading) {
return
}
this.sender = !this.sender
},
onSubmit() {
if (this.loading) {
return
}
if (!this.beforeSubmit) {
this.hide()
return
}
const before = this.beforeSubmit({
message: this.message,
sender: this.sender,
});
if (before && before.then) {
this.loading = true
before.then(() => {
this.hide()
}).catch(_ => {
// do nothing
}).finally(() => {
this.loading = false
})
} else {
this.hide()
}
},
hide() {
this.show = false
}
}
}
</script>

View File

@ -0,0 +1,128 @@
<template>
<div v-show="false">
<!-- 转发选择 -->
<UserSelect
ref="forwardSelect"
:title="title"
:multiple-max="userMaxSelect"
:before-submit="onSelectBefore"
:show-select-all="false"
show-dialog
module/>
<!-- 转发确认 -->
<ForwardConfirm
ref="forwardConfirm"
v-model="confirmShow"
:title="confirmTitle"
:sender-hidden="senderHidden"
:before-submit="onConfirmBefore"
:dialog-id="forwardDialogId"
:forward-to="forwardTo"
:msg-detail="msgDetail"/>
</div>
</template>
<script>
import UserSelect from "../../../../components/UserSelect.vue";
import ForwardConfirm from "./confirm.vue";
export default {
name: "Forwarder",
components: {UserSelect, ForwardConfirm},
props: {
//
title: {
type: String,
default: 'Forward'
},
//
confirmTitle: {
type: String,
default: 'Forward Confirm'
},
//
senderHidden: {
type: Boolean,
default: false
},
//
userMaxSelect: {
type: Number,
default: 50
},
//
beforeSubmit: Function,
//
msgDetail: {
type: Object,
default: () => ({})
},
},
data() {
return {
confirmShow: false,
forwardDialogId: 0,
forwardTo: [],
}
},
methods: {
onSelection() {
this.$refs.forwardSelect.onSelection()
},
onSelectBefore() {
return new Promise((_, reject) => {
this.forwardTo = this.$refs.forwardSelect.formatSelect(this.$refs.forwardSelect.selects);
if (this.forwardTo.length === 0) {
$A.messageError("请选择对话或成员");
} else {
this.forwardDialogId = 0;
if (this.forwardTo.length === 1) {
const {type, userid} = this.forwardTo[0];
if (type === "group" && /^d:/.test(userid)) {
this.forwardDialogId = parseInt(userid.replace(/^d:/, ''));
}
}
this.confirmShow = true;
}
reject();
})
},
onConfirmBefore(data) {
return new Promise((resolve, reject) => {
const selects = this.$refs.forwardSelect.selects;
if (selects.length === 0) {
$A.messageError("请选择对话或成员");
reject();
return;
}
//
data.dialogids = selects.filter(value => $A.leftExists(value, 'd:')).map(value => value.replace('d:', ''));
data.userids = selects.filter(value => !$A.leftExists(value, 'd:'));
data.msg_id = this.msgDetail.id;
//
const success = () => {
this.$refs.forwardSelect.hide();
resolve();
}
if (!this.beforeSubmit) {
success()
return
}
const before = this.beforeSubmit(data);
if (before && before.then) {
before.then(success).catch(reject)
} else {
success()
}
})
}
}
}
</script>

View File

@ -5,6 +5,7 @@
@import "dialog-session-history";
@import "dialog-wrapper";
@import "file-content";
@import "forwarder";
@import "general-operation";
@import "meeting-manager";
@import "project-archived";

View File

@ -3,62 +3,3 @@
display: none !important;
}
}
.dialog-wrapper-forward {
.forward-option {
max-width: 100%;
display: flex;
align-items: center;
.forward-avatar {
display: flex;
align-items: center;
.icon-avatar {
width: 26px;
height: 26px;
flex-grow: 0;
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
font-size: 18px;
background-color: #61B2F9;
color: #ffffff;
&.department {
background-color: #5BC7B0;
}
&.project {
background-color: #6E99EB;
}
&.task {
background-color: #9B96DF;
font-size: 16px;
}
}
}
.forward-name {
margin-left: 10px;
flex: 1;
width: 0;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
}
.ivu-select-item {
&.ivu-select-item-selected {
&:after {
top: 8px;
}
}
}
.forward-drop-prepend {
text-align: center;
color: #c5c8ce;
line-height: 20px;
padding-bottom: 5px;
font-size: 12px;
border-bottom: 1px solid #f1f1f1;
margin-bottom: 5px;
}
}

View File

@ -2235,136 +2235,6 @@ body:not(.window-touch) {
}
}
.dialog-forward-message-modal {
.ivu-modal {
margin: 10px auto;
.ivu-modal-body {
.user-modal-search {
padding-bottom: 16px;
.search-selected {
max-width: 100%;
}
.user-modal-avatar {
display: flex;
align-items: center;
gap: 5px;
.avatar-name {
max-width: 90%;
}
}
}
.twice-affirm-body-extend {
margin: 0 24px;
.dialog-wrapper-forward-body {
.dialog-wrapper {
position: relative;
z-index: 1;
&.inde-list {
border-radius: 0;
}
.dialog-scroller {
position: relative;
padding: 0;
.dialog-item {
.dialog-view {
width: 100%;
max-width: 100%;
margin: 0;
&.record {
width: auto;
}
.dialog-head {
width: 100%;
border-radius: 8px;
.dialog-content {
.content-file.file {
width: 100%;
.file-box {
width: 100%;
}
}
}
.dialog-emoji {
display: none;
}
}
.dialog-foot {
display: none;
}
}
.dialog-avatar {
display: none;
}
}
}
}
.leave-message {
position: relative;
z-index: 2;
padding-bottom: 16px;
textarea {
background: #f7f7f7;
}
}
}
}
}
.ivu-modal-footer {
display: flex;
justify-content: flex-end;
gap: 20px;
.dialog-wrapper-forward-footer {
flex: 1;
width: 0;
display: flex;
line-height: 34px;
cursor: pointer;
.user-modal-icon {
flex-shrink: 0;
font-size: 22px;
margin-right: 5px;
color: rgba($primary-desc-color, 0.7);
margin-top: 6px;
}
.forward-text-tip {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
&.selected {
.user-modal-icon {
color: $primary-color;
}
}
}
}
}
}
.dialog-wrapper-read-poptip {
width: 360px;
max-width: 72%;

View File

@ -0,0 +1,129 @@
.forwarder-message-modal {
.ivu-modal {
margin: 10px auto;
.ivu-modal-body {
.user-modal-search {
padding-bottom: 16px;
.search-selected {
max-width: 100%;
}
.user-modal-avatar {
display: flex;
align-items: center;
gap: 5px;
.avatar-name {
max-width: 90%;
}
}
}
.twice-affirm-body-extend {
margin: 0 24px;
.forwarder-wrapper-body {
.dialog-wrapper {
position: relative;
z-index: 1;
&.inde-list {
border-radius: 0;
}
.dialog-scroller {
position: relative;
padding: 0;
.dialog-item {
.dialog-view {
width: 100%;
max-width: 100%;
margin: 0;
&.record {
width: auto;
}
.dialog-head {
width: 100%;
border-radius: 8px;
.dialog-content {
.content-file.file {
width: 100%;
.file-box {
width: 100%;
}
}
}
.dialog-emoji {
display: none;
}
}
.dialog-foot {
display: none;
}
}
.dialog-avatar {
display: none;
}
}
}
}
.leave-message {
position: relative;
z-index: 2;
padding-bottom: 16px;
textarea {
background: #f7f7f7;
}
}
}
}
}
.ivu-modal-footer {
display: flex;
justify-content: flex-end;
gap: 20px;
.forwarder-wrapper-footer {
flex: 1;
width: 0;
display: flex;
line-height: 34px;
cursor: pointer;
.user-modal-icon {
flex-shrink: 0;
font-size: 22px;
margin-right: 5px;
color: rgba($primary-desc-color, 0.7);
margin-top: 6px;
}
.forward-text-tip {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
&.selected {
.user-modal-icon {
color: $primary-color;
}
}
}
}
}
}