feat: 转发消息 - 添加来源显示

This commit is contained in:
weifashi 2023-12-04 18:45:42 +08:00
parent bc250ad4b8
commit 5fdd5adef8
10 changed files with 416 additions and 188 deletions

View File

@ -1358,9 +1358,11 @@ class DialogController extends AbstractController
* @apiGroup dialog * @apiGroup dialog
* @apiName msg__forward * @apiName msg__forward
* *
* @apiParam {Number} msg_id 消息ID * @apiParam {Number} msg_id 消息ID
* @apiParam {Array} dialogids 转发给的对话ID * @apiParam {Array} dialogids 转发给的对话ID
* @apiParam {Array} userids 转发给的成员ID * @apiParam {Array} userids 转发给的成员ID
* @apiParam {Number} show_source 是否显示原发送者信息
* @apiParam {Array} leave_message 转发留言
* *
* @apiSuccess {Number} ret 返回状态码1正确、0错误 * @apiSuccess {Number} ret 返回状态码1正确、0错误
* @apiSuccess {String} msg 返回信息(错误描述) * @apiSuccess {String} msg 返回信息(错误描述)
@ -1373,6 +1375,8 @@ class DialogController extends AbstractController
$msg_id = intval(Request::input("msg_id")); $msg_id = intval(Request::input("msg_id"));
$dialogids = Request::input('dialogids'); $dialogids = Request::input('dialogids');
$userids = Request::input('userids'); $userids = Request::input('userids');
$show_source = intval(Request::input("show_source"));
$leave_message = Request::input('leave_message');
// //
if (empty($dialogids) && empty($userids)) { if (empty($dialogids) && empty($userids)) {
return Base::retError("请选择转发对话或成员"); return Base::retError("请选择转发对话或成员");
@ -1384,7 +1388,7 @@ class DialogController extends AbstractController
} }
WebSocketDialog::checkDialog($msg->dialog_id); WebSocketDialog::checkDialog($msg->dialog_id);
// //
return $msg->forwardMsg($dialogids, $userids, $user); return $msg->forwardMsg($dialogids, $userids, $user, $show_source, $leave_message);
} }
/** /**

View File

@ -72,6 +72,7 @@ class WebSocketDialogMsg extends AbstractModel
protected $appends = [ protected $appends = [
'percentage', 'percentage',
'reply_data', 'reply_data',
'forward_data',
]; ];
protected $hidden = [ protected $hidden = [
@ -114,6 +115,21 @@ class WebSocketDialogMsg extends AbstractModel
return $this->appendattrs['reply_data']; return $this->appendattrs['reply_data'];
} }
/**
* 转发消息详情
* @return WebSocketDialogMsg|null
*/
public function getForwardDataAttribute()
{
if (!isset($this->appendattrs['forward_data'])) {
$this->appendattrs['forward_data'] = null;
if ($this->forward_id > 0) {
$this->appendattrs['forward_data'] = self::find($this->forward_id, ['id', 'userid', 'type', 'msg'])?->cancelAppend() ?: null;
}
}
return $this->appendattrs['forward_data'];
}
/** /**
* 消息格式化 * 消息格式化
* @param $value * @param $value
@ -369,11 +385,13 @@ class WebSocketDialogMsg extends AbstractModel
* @param array|int $dialogids * @param array|int $dialogids
* @param array|int $userids * @param array|int $userids
* @param User $user 发送的会员 * @param User $user 发送的会员
* @param int $showSource 是否显示原发送者信息
* @param string $leaveMessage 转发留言
* @return mixed * @return mixed
*/ */
public function forwardMsg($dialogids, $userids, $user) public function forwardMsg($dialogids, $userids, $user, $showSource = 1, $leaveMessage = '')
{ {
return AbstractModel::transaction(function() use ($dialogids, $user, $userids) { return AbstractModel::transaction(function() use ($dialogids, $user, $userids, $showSource, $leaveMessage) {
$originalMsg = Base::json2array($this->getRawOriginal('msg')); $originalMsg = Base::json2array($this->getRawOriginal('msg'));
$msgs = []; $msgs = [];
$already = []; $already = [];
@ -382,11 +400,14 @@ class WebSocketDialogMsg extends AbstractModel
$dialogids = [$dialogids]; $dialogids = [$dialogids];
} }
foreach ($dialogids as $dialogid) { foreach ($dialogids as $dialogid) {
$res = self::sendMsg(null, $dialogid, $this->type, $originalMsg, $user->userid); $res = self::sendMsg('forward-'.( $showSource ? 1 : 0).'-'.($this->forward_id ?: $this->id), $dialogid, $this->type, $originalMsg, $user->userid);
if (Base::isSuccess($res)) { if (Base::isSuccess($res)) {
$msgs[] = $res['data']; $msgs[] = $res['data'];
$already[] = $dialogid; $already[] = $dialogid;
} }
if($leaveMessage){
self::sendMsg(null, $dialogid, 'text', ['text' => $leaveMessage], $user->userid);
}
} }
} }
if ($userids) { if ($userids) {
@ -399,10 +420,13 @@ class WebSocketDialogMsg extends AbstractModel
} }
$dialog = WebSocketDialog::checkUserDialog($user, $userid); $dialog = WebSocketDialog::checkUserDialog($user, $userid);
if ($dialog && !in_array($dialog->id, $already)) { if ($dialog && !in_array($dialog->id, $already)) {
$res = self::sendMsg(null, $dialog->id, $this->type, $originalMsg, $user->userid); $res = self::sendMsg('forward-'.( $showSource ? 1 : 0).'-'.($this->forward_id ?: $this->id), $dialog->id, $this->type, $originalMsg, $user->userid);
if (Base::isSuccess($res)) { if (Base::isSuccess($res)) {
$msgs[] = $res['data']; $msgs[] = $res['data'];
} }
if($leaveMessage){
self::sendMsg(null, $dialog->id, 'text', ['text' => $leaveMessage], $user->userid);
}
} }
} }
} }
@ -772,6 +796,7 @@ class WebSocketDialogMsg extends AbstractModel
* - reply-98回复消息ID=98 * - reply-98回复消息ID=98
* - update-99更新消息ID=99(标记修改) * - update-99更新消息ID=99(标记修改)
* - change-99更新消息ID=99(不标记修改) * - change-99更新消息ID=99(不标记修改)
* - forward-99转发消息ID=99
* @param int $dialog_id 会话ID 聊天室ID * @param int $dialog_id 会话ID 聊天室ID
* @param string $type 消息类型 * @param string $type 消息类型
* @param array $msg 发送的消息 * @param array $msg 发送的消息
@ -812,6 +837,7 @@ class WebSocketDialogMsg extends AbstractModel
$update_id = preg_match("/^update-(\d+)$/", $action, $match) ? $match[1] : 0; $update_id = preg_match("/^update-(\d+)$/", $action, $match) ? $match[1] : 0;
$change_id = preg_match("/^change-(\d+)$/", $action, $match) ? $match[1] : 0; $change_id = preg_match("/^change-(\d+)$/", $action, $match) ? $match[1] : 0;
$reply_id = preg_match("/^reply-(\d+)$/", $action, $match) ? $match[1] : 0; $reply_id = preg_match("/^reply-(\d+)$/", $action, $match) ? $match[1] : 0;
$forward_id = preg_match("/^forward-(\d+)-(\d+)$/", $action, $match) ? $match[2] : 0;
$sender = $sender === null ? User::userid() : $sender; $sender = $sender === null ? User::userid() : $sender;
// //
$dialog = WebSocketDialog::find($dialog_id); $dialog = WebSocketDialog::find($dialog_id);
@ -862,6 +888,10 @@ class WebSocketDialogMsg extends AbstractModel
if ($reply_id && !self::whereId($reply_id)->increment('reply_num')) { if ($reply_id && !self::whereId($reply_id)->increment('reply_num')) {
throw new ApiException('回复的消息不存在'); throw new ApiException('回复的消息不存在');
} }
// 转发
if ($forward_id && !self::whereId($forward_id)->increment('forward_num')) {
throw new ApiException('转发的消息不存在');
}
// //
$dialogMsg = self::createInstance([ $dialogMsg = self::createInstance([
'dialog_id' => $dialog_id, 'dialog_id' => $dialog_id,
@ -873,6 +903,8 @@ class WebSocketDialogMsg extends AbstractModel
'link' => $link, 'link' => $link,
'msg' => $msg, 'msg' => $msg,
'read' => 0, 'read' => 0,
'forward_id' => $forward_id,
'forward_show' => $forward_id ? $match[1] : 1,
]); ]);
AbstractModel::transaction(function () use ($dialog, $dialogMsg) { AbstractModel::transaction(function () use ($dialog, $dialogMsg) {
$dialog->last_at = Carbon::now(); $dialog->last_at = Carbon::now();

View File

@ -0,0 +1,38 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class WebSocketDialogMsgsAddForwardId extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('web_socket_dialog_msgs', function (Blueprint $table) {
if (!Schema::hasColumn('web_socket_dialog_msgs', 'forward_id')) {
$table->bigInteger('forward_id')->nullable()->default(0)->after('reply_id')->comment('转发ID');
$table->bigInteger('forward_num')->nullable()->default(0)->after('forward_id')->comment('被转发多少次');
$table->boolean('forward_show')->nullable()->default(1)->after('forward_num')->comment('是否显示转发的来源');
}
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('web_socket_dialog_msgs', function (Blueprint $table) {
$table->dropColumn("forward_id");
$table->dropColumn("forward_num");
$table->dropColumn("forward_show");
});
}
}

View File

@ -1420,3 +1420,7 @@ APP推送
任务协助人 任务协助人
搜索项目名称 搜索项目名称
服务器版本过低,请升级服务器。 服务器版本过低,请升级服务器。
不显示原发送者信息
转发给:
留言

View File

@ -21,7 +21,7 @@
<div v-if="isFullscreen" class="user-modal-header"> <div v-if="isFullscreen" class="user-modal-header">
<div class="user-modal-close" @click="showModal=false">{{$L('关闭')}}</div> <div class="user-modal-close" @click="showModal=false">{{$L('关闭')}}</div>
<div class="user-modal-title"><span>{{localTitle}}</span></div> <div class="user-modal-title"><span>{{localTitle}}</span></div>
<div class="user-modal-submit" @click="onSubmit"> <div class="user-modal-submit" @click="onSubmit(1)">
<div v-if="submittIng > 0" class="submit-loading"><Loading /></div> <div v-if="submittIng > 0" class="submit-loading"><Loading /></div>
{{$L('确定')}} {{$L('确定')}}
<template v-if="selects.length > 0"> <template v-if="selects.length > 0">
@ -134,7 +134,7 @@
<!-- 底部 --> <!-- 底部 -->
<template #footer> <template #footer>
<Button type="primary" :loading="submittIng > 0" @click="onSubmit"> <Button type="primary" :loading="submittIng > 0" @click="onSubmit(1)">
{{$L('确定')}} {{$L('确定')}}
<template v-if="selects.length > 0"> <template v-if="selects.length > 0">
({{selects.length}}<span v-if="multipleMax">/{{multipleMax}}</span>) ({{selects.length}}<span v-if="multipleMax">/{{multipleMax}}</span>)
@ -142,6 +142,42 @@
</Button> </Button>
</template> </template>
</Modal> </Modal>
<!-- 二次确认 -->
<Modal
v-model="showAffirmModal"
:title="twiceAffirmTitle"
class-name="common-user-select-modal twice-affirm-modal"
:mask-closable="false"
width="420">
<div class="user-modal-search twice-affirm">
<Scrollbar class="search-selected" v-if="selects?.length > 0" enable-x :enable-y="false">
<ul>
<li v-for="item in formatSelect(selects)" :data-id="item.userid">
<template v-if="item.type=='group'">
<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" />
</template>
<UserAvatar v-else :userid="item.userid" :size="32" :show-name="selects?.length == 1" tooltip-disabled />
</li>
</ul>
</Scrollbar>
</div>
<div class="twice-affirm-body-extend">
<slot name="twice-affirm-body-extend"></slot>
</div>
<template #footer>
<slot name="twice-affirm-footer-extend"></slot>
<Button type="primary" :loading="submittIng > 0" @click="onSubmit(2)">
{{$L('确定')}}
<template v-if="selects.length > 0">({{selects.length}})</template>
</Button>
</template>
</Modal>
</div> </div>
</template> </template>
@ -253,147 +289,147 @@ export default {
type: Boolean, type: Boolean,
default: false default: false
}, },
// //
disable: { disable: {
type: Boolean, type: Boolean,
default: false default: false
}, },
//
submitBtnTwoText: {
type: String,
default: ''
},
//
twiceAffirm: {
type: Boolean,
default: false
},
//
twiceAffirmTitle: {
type: String,
default: ''
},
// //
beforeSubmit: Function beforeSubmit: Function
}, },
data() { data() {
return { return {
switchItems: [ switchItems: [
{key: 'recent', label: '最近'}, { key: 'recent', label: '最近' },
{key: 'contact', label: '通讯录'}, { key: 'contact', label: '通讯录' },
{key: 'project', label: '项目成员'}, { key: 'project', label: '项目成员' },
], ],
switchActive: 'recent', switchActive: 'recent',
loadIng: 0,
loadIng: 0, // waitIng: 0,
waitIng: 0, // submittIng: 0,
submittIng: 0, //
values: [], values: [],
selects: [], selects: [],
recents: [], recents: [],
contacts: [], contacts: [],
projects: [], projects: [],
showModal: false, showModal: false,
searchKey: null, searchKey: null,
searchCache: [], searchCache: [],
} showAffirmModal: false,
};
}, },
watch: { watch: {
value: { value: {
handler(value) { handler(value) {
if (typeof value === 'number') { if (typeof value === 'number') {
this.$emit('input', value > 0 ? [value] : []) this.$emit('input', value > 0 ? [value] : []);
} else if (typeof value === 'string') {
value = value.indexOf(',') > -1 ? value.split(',') : [value]
this.$emit('input', value.map(item => $A.runNum(item)).filter(item => item > 0))
} }
this.values = value else if (typeof value === 'string') {
value = value.indexOf(',') > -1 ? value.split(',') : [value];
this.$emit('input', value.map(item => $A.runNum(item)).filter(item => item > 0));
}
this.values = value;
}, },
immediate: true immediate: true
}, },
isWhole: { isWhole: {
handler(value) { handler(value) {
if (value) { if (value) {
this.switchActive = 'recent' this.switchActive = 'recent';
} else { }
this.switchActive = 'contact' else {
this.switchActive = 'contact';
} }
}, },
immediate: true immediate: true
}, },
showModal(value) { showModal(value) {
if (value) { if (value) {
this.searchBefore() this.searchBefore();
} else {
this.searchKey = ""
} }
this.$emit("on-show-change",value,this.values) else {
this.searchKey = "";
}
this.$emit("on-show-change", value, this.values);
}, },
searchKey() { searchKey() {
this.searchBefore() this.searchBefore();
}, },
switchActive() { switchActive() {
this.searchBefore() this.searchBefore();
}, },
}, },
computed: { computed: {
...mapState([ ...mapState([
'cacheDialogs', 'cacheDialogs',
]), ]),
isFullscreen({ windowWidth }) {
isFullscreen({windowWidth}) { return windowWidth < 576;
return windowWidth < 576
}, },
isWhole({ projectId, noProjectId, dialogId }) {
isWhole({projectId, noProjectId, dialogId}) { return projectId === 0 && noProjectId === 0 && dialogId === 0;
return projectId === 0 && noProjectId === 0 && dialogId === 0
}, },
lists({ switchActive, searchKey, recents, contacts, projects }) {
lists({switchActive, searchKey, recents, contacts, projects}) {
switch (switchActive) { switch (switchActive) {
case 'recent': case 'recent':
if (searchKey) { if (searchKey) {
return recents.filter(item => { return recents.filter(item => {
return `${item.name}`.indexOf(searchKey) > -1 return `${item.name}`.indexOf(searchKey) > -1;
}) });
} }
return recents return recents;
case 'contact': case 'contact':
return contacts return contacts;
case 'project': case 'project':
return projects return projects;
} }
return [] return [];
}, },
isSelectAll({ lists, selects }) {
isSelectAll({lists, selects}) {
return lists.length > 0 && lists.filter(item => selects.includes(item.userid)).length === lists.length; return lists.length > 0 && lists.filter(item => selects.includes(item.userid)).length === lists.length;
}, },
warpClass() { warpClass() {
return { return {
'select-module': this.module, 'select-module': this.module,
'select-border': this.border, 'select-border': this.border,
'select-whole': this.isWhole, 'select-whole': this.isWhole,
} };
}, },
addStyle({ avatarSize }) {
addStyle({avatarSize}) {
return { return {
width: avatarSize + 'px', width: avatarSize + 'px',
height: avatarSize + 'px', height: avatarSize + 'px',
} };
}, },
localTitle({ title }) {
localTitle({title}) {
if (title === undefined) { if (title === undefined) {
return this.$L('选择会员') return this.$L('选择会员');
} else { }
else {
return title; return title;
} }
}, },
localPlaceholder({ placeholder }) {
localPlaceholder({placeholder}) {
if (placeholder === undefined) { if (placeholder === undefined) {
return this.$L('搜索') return this.$L('搜索');
} else { }
else {
return placeholder; return placeholder;
} }
} }
@ -405,32 +441,29 @@ export default {
} }
return this.uncancelable.includes(value); return this.uncancelable.includes(value);
}, },
isDisabled(userid) { isDisabled(userid) {
if (this.disabledChoice.length === 0) { if (this.disabledChoice.length === 0) {
return false; return false;
} }
return this.disabledChoice.includes(userid) return this.disabledChoice.includes(userid);
}, },
formatSelect(list) { formatSelect(list) {
return list.map(userid => { return list.map(userid => {
if ($A.leftExists(userid, 'd:')) { if ($A.leftExists(userid, 'd:')) {
return this.recents.find(item => item.userid === userid) return this.recents.find(item => item.userid === userid);
} }
return { return {
type: 'user', type: 'user',
userid, userid,
} };
}) });
}, },
selectIcon(value) { selectIcon(value) {
if (value === 'all') { if (value === 'all') {
return this.isSelectAll ? 'ios-checkmark-circle' : 'ios-radio-button-off'; return this.isSelectAll ? 'ios-checkmark-circle' : 'ios-radio-button-off';
} }
if ($A.isArray(value) && value.length > 0) { if ($A.isArray(value) && value.length > 0) {
const len = value.filter(value => this.selects.includes(value)).length const len = value.filter(value => this.selects.includes(value)).length;
if (len === value.length) { if (len === value.length) {
return 'ios-checkmark-circle'; return 'ios-checkmark-circle';
} }
@ -440,7 +473,6 @@ export default {
} }
return 'ios-radio-button-off'; return 'ios-radio-button-off';
}, },
selectClass(value) { selectClass(value) {
switch (this.selectIcon(value)) { switch (this.selectIcon(value)) {
case 'ios-checkmark-circle': case 'ios-checkmark-circle':
@ -450,29 +482,29 @@ export default {
} }
return ''; return '';
}, },
searchBefore() { searchBefore() {
if (!this.showModal) { if (!this.showModal) {
return return;
} }
if (this.switchActive === 'recent') { if (this.switchActive === 'recent') {
this.searchRecent() this.searchRecent();
} else if (this.switchActive === 'contact') { }
this.searchContact() else if (this.switchActive === 'contact') {
} else if (this.switchActive === 'project') { this.searchContact();
this.searchProject() }
else if (this.switchActive === 'project') {
this.searchProject();
} }
}, },
searchRecent() { searchRecent() {
this.recents = this.cacheDialogs.filter(dialog => { this.recents = this.cacheDialogs.filter(dialog => {
if (dialog.name === undefined || dialog.dialog_delete === 1) { if (dialog.name === undefined || dialog.dialog_delete === 1) {
return false return false;
} }
if (!this.showBot && dialog.bot) { if (!this.showBot && dialog.bot) {
return false return false;
} }
return this.showDialog || dialog.type === 'user' return this.showDialog || dialog.type === 'user';
}).sort((a, b) => { }).sort((a, b) => {
if (a.top_at || b.top_at) { if (a.top_at || b.top_at) {
return $A.Date(b.top_at) - $A.Date(a.top_at); return $A.Date(b.top_at) - $A.Date(a.top_at);
@ -481,33 +513,32 @@ export default {
return b.todo_num - a.todo_num; return b.todo_num - a.todo_num;
} }
return $A.Date(b.last_at) - $A.Date(a.last_at); return $A.Date(b.last_at) - $A.Date(a.last_at);
}).map(({id, name, type, group_type, avatar, dialog_user}) => { }).map(({ id, name, type, group_type, avatar, dialog_user }) => {
return { return {
name, name,
type, type,
group_type, group_type,
avatar, avatar,
userid: type === 'user' ? dialog_user.userid : `d:${id}`, userid: type === 'user' ? dialog_user.userid : `d:${id}`,
} };
}); });
}, },
searchContact() { searchContact() {
let key = this.searchKey; let key = this.searchKey;
const cache = this.searchCache.find(item => item.type === 'contact' && item.key == key); const cache = this.searchCache.find(item => item.type === 'contact' && item.key == key);
if (cache) { if (cache) {
this.contacts = cache.data this.contacts = cache.data;
} }
// //
this.waitIng++ this.waitIng++;
setTimeout(() => { setTimeout(() => {
if (this.searchKey != key) { if (this.searchKey != key) {
this.waitIng-- this.waitIng--;
return; return;
} }
setTimeout(() => { setTimeout(() => {
this.loadIng++ this.loadIng++;
}, 300) }, 300);
this.$store.dispatch("call", { this.$store.dispatch("call", {
url: 'users/search', url: 'users/search',
data: { data: {
@ -521,43 +552,43 @@ export default {
}, },
take: 50 take: 50
}, },
}).then(({data}) => { }).then(({ data }) => {
data = data.map(item => Object.assign(item, {type: 'user'})) data = data.map(item => Object.assign(item, { type: 'user' }));
this.contacts = data this.contacts = data;
// //
const index = this.searchCache.findIndex(item => item.key == key); const index = this.searchCache.findIndex(item => item.key == key);
const tmpData = {type: 'contact', key, data, time: $A.Time()}; const tmpData = { type: 'contact', key, data, time: $A.Time() };
if (index > -1) { if (index > -1) {
this.searchCache.splice(index, 1, tmpData) this.searchCache.splice(index, 1, tmpData);
} else {
this.searchCache.push(tmpData)
} }
}).catch(({msg}) => { else {
this.contacts = [] this.searchCache.push(tmpData);
$A.messageWarning(msg) }
}).catch(({ msg }) => {
this.contacts = [];
$A.messageWarning(msg);
}).finally(_ => { }).finally(_ => {
this.loadIng--; this.loadIng--;
this.waitIng--; this.waitIng--;
}); });
}, this.searchCache.length > 0 ? 300 : 0) }, this.searchCache.length > 0 ? 300 : 0);
}, },
searchProject() { searchProject() {
let key = this.searchKey; let key = this.searchKey;
const cache = this.searchCache.find(item => item.type === 'project' && item.key == key); const cache = this.searchCache.find(item => item.type === 'project' && item.key == key);
if (cache) { if (cache) {
this.projects = cache.data this.projects = cache.data;
} }
// //
this.waitIng++ this.waitIng++;
setTimeout(() => { setTimeout(() => {
if (this.searchKey != key) { if (this.searchKey != key) {
this.waitIng-- this.waitIng--;
return; return;
} }
setTimeout(() => { setTimeout(() => {
this.loadIng++ this.loadIng++;
}, 300) }, 300);
this.$store.dispatch("call", { this.$store.dispatch("call", {
url: 'project/lists', url: 'project/lists',
data: { data: {
@ -568,137 +599,146 @@ export default {
getuserid: 'yes', getuserid: 'yes',
getstatistics: 'no' getstatistics: 'no'
}, },
}).then(({data}) => { }).then(({ data }) => {
data = data.data.map(item => Object.assign(item, {type: 'project'})) data = data.data.map(item => Object.assign(item, { type: 'project' }));
this.projects = data this.projects = data;
// //
const index = this.searchCache.findIndex(item => item.key == key); const index = this.searchCache.findIndex(item => item.key == key);
const tmpData = {type: 'project', key, data, time: $A.Time()}; const tmpData = { type: 'project', key, data, time: $A.Time() };
if (index > -1) { if (index > -1) {
this.searchCache.splice(index, 1, tmpData) this.searchCache.splice(index, 1, tmpData);
} else {
this.searchCache.push(tmpData)
} }
}).catch(({msg}) => { else {
this.projects = [] this.searchCache.push(tmpData);
$A.messageWarning(msg) }
}).catch(({ msg }) => {
this.projects = [];
$A.messageWarning(msg);
}).finally(_ => { }).finally(_ => {
this.loadIng--; this.loadIng--;
this.waitIng--; this.waitIng--;
}); });
}, this.searchCache.length > 0 ? 300 : 0) }, this.searchCache.length > 0 ? 300 : 0);
}, },
onSelection() { onSelection() {
if(this.disable){ if (this.disable) {
return return;
} }
this.$nextTick(_ => { this.$nextTick(_ => {
this.selects = $A.cloneJSON(this.values) this.selects = $A.cloneJSON(this.values);
this.showModal = true this.showModal = true;
}) });
}, },
onSelectAll() { onSelectAll() {
if (this.isSelectAll) { if (this.isSelectAll) {
this.selects = $A.cloneJSON(this.uncancelable) this.selects = $A.cloneJSON(this.uncancelable);
return return;
} }
this.lists.some(item => { this.lists.some(item => {
if (this.isDisabled(item.userid)) { if (this.isDisabled(item.userid)) {
return false return false;
} }
if (this.multipleMax && this.selects.length >= this.multipleMax) { if (this.multipleMax && this.selects.length >= this.multipleMax) {
$A.messageWarning("已超过最大选择数量") $A.messageWarning("已超过最大选择数量");
return true return true;
} }
if (!this.selects.includes(item.userid)) { if (!this.selects.includes(item.userid)) {
this.selects.push(item.userid) this.selects.push(item.userid);
} }
}) });
}, },
onSelectItem({ userid }) {
onSelectItem({userid}) {
if (this.selects.includes(userid)) { if (this.selects.includes(userid)) {
if (this.isUncancelable(userid)) { if (this.isUncancelable(userid)) {
return return;
} }
this.selects = this.selects.filter(value => value != userid) this.selects = this.selects.filter(value => value != userid);
} else { }
else {
if (this.isDisabled(userid)) { if (this.isDisabled(userid)) {
return return;
} }
if (this.multipleMax && this.selects.length >= this.multipleMax) { if (this.multipleMax && this.selects.length >= this.multipleMax) {
$A.messageWarning("已超过最大选择数量") $A.messageWarning("已超过最大选择数量");
return return;
} }
this.selects.push(userid) this.selects.push(userid);
// //
this.$nextTick(() => { this.$nextTick(() => {
$A.scrollIntoViewIfNeeded(this.$refs.selected.querySelector(`li[data-id="${userid}"]`)) $A.scrollIntoViewIfNeeded(this.$refs.selected.querySelector(`li[data-id="${userid}"]`));
}) });
} }
}, },
onSelectProject(userid_list) { onSelectProject(userid_list) {
switch (this.selectIcon(userid_list)) { switch (this.selectIcon(userid_list)) {
case 'ios-checkmark-circle': case 'ios-checkmark-circle':
// //
const removeList = userid_list.filter(userid => !this.isUncancelable(userid)) const removeList = userid_list.filter(userid => !this.isUncancelable(userid));
if (removeList.length != userid_list.length) { if (removeList.length != userid_list.length) {
$A.messageWarning("部分成员禁止取消") $A.messageWarning("部分成员禁止取消");
} }
this.selects = this.selects.filter(userid => !removeList.includes(userid)) this.selects = this.selects.filter(userid => !removeList.includes(userid));
break; break;
default: default:
// //
const addList = userid_list.filter(userid => !this.isDisabled(userid)) const addList = userid_list.filter(userid => !this.isDisabled(userid));
if (addList.length != userid_list.length) { if (addList.length != userid_list.length) {
$A.messageWarning("部分成员禁止选择") $A.messageWarning("部分成员禁止选择");
} }
this.selects = this.selects.concat(addList.filter(userid => !this.selects.includes(userid))) this.selects = this.selects.concat(addList.filter(userid => !this.selects.includes(userid)));
// //
if (this.multipleMax && this.selects.length > this.multipleMax) { if (this.multipleMax && this.selects.length > this.multipleMax) {
$A.messageWarning("已超过最大选择数量") $A.messageWarning("已超过最大选择数量");
this.selects = this.selects.slice(0, this.multipleMax) this.selects = this.selects.slice(0, this.multipleMax);
} }
break; break;
} }
}, },
onRemoveItem(userid) { onRemoveItem(userid) {
if (this.isUncancelable(userid)) { if (this.isUncancelable(userid)) {
return return;
} }
this.selects = this.selects.filter(value => value != userid) this.selects = this.selects.filter(value => value != userid);
}, },
onSubmit(index) {
onSubmit() {
if (this.submittIng > 0) { if (this.submittIng > 0) {
return return;
} }
const clone = $A.cloneJSON(this.values) const clone = $A.cloneJSON(this.values);
this.values = $A.cloneJSON(this.selects) this.values = $A.cloneJSON(this.selects);
this.$emit('input', this.values) //
this.$emit('onSubmit', this.values) if (index !=2 && this.twiceAffirm) {
if (this.values.length < 1) {
$A.messageError("请选择对话或成员");
return;
}
this.showAffirmModal = true;
return;
}
//
this.$emit('input', this.values);
this.$emit('onSubmit', this.values);
if (!this.beforeSubmit) { if (!this.beforeSubmit) {
this.showModal = false this.showModal = false;
return this.showAffirmModal = false;
return;
} }
const before = this.beforeSubmit(); const before = this.beforeSubmit();
if (before && before.then) { if (before && before.then) {
this.submittIng++ this.submittIng++;
before.then(() => { before.then(() => {
this.showModal = false this.showModal = false;
this.showAffirmModal = false;
}).catch(() => { }).catch(() => {
this.values = clone this.values = clone;
this.$emit('input', this.values) this.$emit('input', this.values);
}).finally(() => { }).finally(() => {
this.submittIng-- this.submittIng--;
}) });
} else { }
this.showModal = false else {
this.showModal = false;
this.showAffirmModal = false;
} }
}, },
} }

View File

@ -21,7 +21,7 @@
{{source.msg.notice}} {{source.msg.notice}}
</div> </div>
<template v-else> <template v-else>
<div class="dialog-avatar"> <div class="dialog-avatar" v-if="dialogAvatar">
<UserAvatar <UserAvatar
v-longpress="{callback: onMention, delay: 300}" v-longpress="{callback: onMention, delay: 300}"
@open-dialog="onOpenDialog" @open-dialog="onOpenDialog"
@ -94,6 +94,10 @@ export default {
type: Number, type: Number,
default: 0 default: 0
}, },
dialogAvatar: {
type: Boolean,
default: true
},
}, },
data() { data() {

View File

@ -14,6 +14,10 @@
<UserAvatar :userid="msgData.reply_data.userid" :show-icon="false" :show-name="true" :tooltip-disabled="true"/> <UserAvatar :userid="msgData.reply_data.userid" :show-icon="false" :show-name="true" :tooltip-disabled="true"/>
<div class="reply-desc" v-html="$A.getMsgSimpleDesc(msgData.reply_data, 'image-preview')"></div> <div class="reply-desc" v-html="$A.getMsgSimpleDesc(msgData.reply_data, 'image-preview')"></div>
</div> </div>
<!--转发-->
<div v-if="msgData.forward_show && msgData.forward_data && msgData.forward_data.userid" class="dialog-reply no-dark-content" @click="openDialog(msgData.forward_data.userid)">
<UserAvatar :userid="msgData.forward_data.userid" :show-icon="false" :show-name="true" :tooltip-disabled="true"/>
</div>
<!--详情--> <!--详情-->
<div ref="content" class="dialog-content" :class="contentClass"> <div ref="content" class="dialog-content" :class="contentClass">
<!--文本--> <!--文本-->
@ -453,6 +457,14 @@ export default {
}); });
}, },
openDialog(userid) {
this.$store.dispatch("openDialogUserid", userid).then(_ => {
this.goForward({name: 'manage-messenger'})
}).catch(({msg}) => {
$A.modalError(msg)
});
},
viewReply() { viewReply() {
this.$emit("on-view-reply", { this.$emit("on-view-reply", {
msg_id: this.msgData.id, msg_id: this.msgData.id,

View File

@ -391,10 +391,32 @@
v-model="forwardData" v-model="forwardData"
:multiple-max="50" :multiple-max="50"
:title="$L('转发')" :title="$L('转发')"
:twice-affirm="true"
:twice-affirm-title="$L('转发给:')"
:before-submit="onForward" :before-submit="onForward"
:show-select-all="false" :show-select-all="false"
show-dialog show-dialog
module/> module>
<template #twice-affirm-body-extend>
<div class="dialog-wrapper-forward-body">
<div class="dialog-wrapper ">
<div class="dialog-scroller">
<DialogItem :source="operateItem" simpleView :dialogAvatar="false"/>
</div>
</div>
<div class="leave-message">
<Input type="textarea" :autosize="{minRows: 1,maxRows: 3}" v-model="forwardLeaveMessage" :placeholder="$L('留言')" clearable />
</div>
</div>
</template>
<template #twice-affirm-footer-extend>
<div class="dialog-wrapper-forward-footer" :class="{'selected': !forwardShowOriginal}" @click="forwardShowOriginal = !forwardShowOriginal">
<Icon v-if="!forwardShowOriginal" class="user-modal-icon" type="ios-checkmark-circle" />
<Icon v-else class="user-modal-icon" type="ios-radio-button-off" />
{{$L('不显示原发送者信息')}}
</div>
</template>
</UserSelect>
<!-- 设置待办 --> <!-- 设置待办 -->
<Modal <Modal
@ -601,6 +623,8 @@ export default {
modifyLoad: 0, modifyLoad: 0,
forwardData: [], forwardData: [],
forwardShowOriginal: true,
forwardLeaveMessage: '',
openId: 0, openId: 0,
dialogDrag: false, dialogDrag: false,
@ -662,7 +686,7 @@ export default {
approveDetailsShow: false, approveDetailsShow: false,
approvaUserStatus: '', approvaUserStatus: '',
mountedNow: 0, mountedNow: 0
} }
}, },
@ -2203,6 +2227,7 @@ export default {
reject(); reject();
return return
} }
const dialogids = this.forwardData.filter(value => $A.leftExists(value, 'd:')).map(value => value.replace('d:', '')); const dialogids = this.forwardData.filter(value => $A.leftExists(value, 'd:')).map(value => value.replace('d:', ''));
const userids = this.forwardData.filter(value => !$A.leftExists(value, 'd:')); const userids = this.forwardData.filter(value => !$A.leftExists(value, 'd:'));
this.$store.dispatch("call", { this.$store.dispatch("call", {
@ -2210,7 +2235,9 @@ export default {
data: { data: {
dialogids, dialogids,
userids, userids,
msg_id: this.operateItem.id msg_id: this.operateItem.id,
show_source: this.forwardShowOriginal ? 1 : 0,
leave_message: this.forwardLeaveMessage
} }
}).then(({data, msg}) => { }).then(({data, msg}) => {
this.$store.dispatch("saveDialogMsg", data.msgs); this.$store.dispatch("saveDialogMsg", data.msgs);
@ -2433,6 +2460,8 @@ export default {
case "forward": case "forward":
this.forwardData = []; this.forwardData = [];
this.forwardLeaveMessage = '';
this.forwardShowOriginal = true;
this.$refs.forwardSelect.onSelection() this.$refs.forwardSelect.onSelection()
break; break;

View File

@ -172,6 +172,13 @@
} }
} }
} }
&.twice-affirm{
padding-bottom: 20px;
.search-selected{
max-width: 100%;
}
}
} }
.user-modal-switch { .user-modal-switch {
@ -416,12 +423,19 @@
border-radius: 14px; border-radius: 14px;
} }
} }
.twice-affirm-body-extend{
margin: 0 24px;
}
} }
.ivu-modal-footer { .ivu-modal-footer {
border-top: 1px solid #f2f2f2 !important; border-top: 1px solid #f2f2f2 !important;
padding: 12px 0 !important; padding: 12px 0 !important;
margin: 0 24px !important; margin: 0 24px !important;
display: flex;
justify-content: flex-end;
gap: 20px;
} }
&.ivu-modal-fullscreen { &.ivu-modal-fullscreen {
@ -462,4 +476,10 @@
} }
} }
} }
&.twice-affirm-modal{
.ivu-modal {
max-width: 90%;
margin: 10px auto;
}
}
} }

View File

@ -1448,6 +1448,51 @@
} }
} }
.dialog-wrapper-forward-body{
.dialog-wrapper{
position: relative !important;
.dialog-scroller{
padding: 0 !important;
.dialog-view{
width: 100%;
max-width: 100% !important;
margin: 0 !important;
.dialog-head{
width: 100%;
border-radius: 8px !important;
}
.dialog-foot{
display: none !important;
}
}
}
}
.leave-message{
padding-bottom: 16px;
textarea{
background: #f7f7f7;
}
}
}
.dialog-wrapper-forward-footer{
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;
}
&.selected {
.user-modal-icon {
color: $primary-color;
}
}
}
.dialog-wrapper-read-poptip { .dialog-wrapper-read-poptip {
width: 360px; width: 360px;
max-width: 72%; max-width: 72%;