From 73ca4b1ea54617ea23360142c025fbc3fc4d4c8d Mon Sep 17 00:00:00 2001 From: kuaifan Date: Tue, 23 Sep 2025 09:48:06 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=89=A9=E5=B1=95=E6=94=B6=E8=97=8F?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=EF=BC=8C=E6=94=AF=E6=8C=81=E6=B6=88=E6=81=AF?= =?UTF-8?q?=E7=B1=BB=E5=9E=8B=E7=9A=84=E6=94=B6=E8=97=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在 UserFavorite 模型中添加消息类型常量 - 更新 UsersController,支持消息的收藏、切换和状态检查 - 修改前端 Vue 组件以实现消息的收藏操作和状态显示 - 优化收藏管理界面,支持消息类型的展示与处理 --- app/Http/Controllers/Api/UsersController.php | 22 +++++--- app/Models/UserFavorite.php | 40 +++++++++++++- .../pages/manage/components/DialogWrapper.vue | 52 +++++++++++++++++++ .../manage/components/FavoriteManagement.vue | 39 ++++++++++++-- resources/assets/js/store/actions.js | 6 +-- resources/assets/sass/dark.scss | 7 +++ 6 files changed, 149 insertions(+), 17 deletions(-) diff --git a/app/Http/Controllers/Api/UsersController.php b/app/Http/Controllers/Api/UsersController.php index 1527cbb22..97828e166 100755 --- a/app/Http/Controllers/Api/UsersController.php +++ b/app/Http/Controllers/Api/UsersController.php @@ -2835,7 +2835,7 @@ class UsersController extends AbstractController * @apiGroup users * @apiName favorites * - * @apiParam {String} [type] 收藏类型过滤 (task/project/file) + * @apiParam {String} [type] 收藏类型过滤 (task/project/file/message) * @apiParam {Number} [page=1] 页码 * @apiParam {Number} [pagesize=20] 每页数量 * @@ -2852,7 +2852,7 @@ class UsersController extends AbstractController $pageSize = min(intval(Request::input('pagesize', 20)), 100); // // 验证收藏类型 - $allowedTypes = [UserFavorite::TYPE_TASK, UserFavorite::TYPE_PROJECT, UserFavorite::TYPE_FILE]; + $allowedTypes = [UserFavorite::TYPE_TASK, UserFavorite::TYPE_PROJECT, UserFavorite::TYPE_FILE, UserFavorite::TYPE_MESSAGE]; if ($type && !in_array($type, $allowedTypes)) { return Base::retError('无效的收藏类型'); } @@ -2870,7 +2870,7 @@ class UsersController extends AbstractController * @apiGroup users * @apiName favorite__toggle * - * @apiParam {String} type 收藏类型 (task/project/file) + * @apiParam {String} type 收藏类型 (task/project/file/message) * @apiParam {Number} id 收藏对象ID * * @apiSuccess {Number} ret 返回状态码(1正确、0错误) @@ -2889,7 +2889,7 @@ class UsersController extends AbstractController } // // 验证收藏类型 - $allowedTypes = [UserFavorite::TYPE_TASK, UserFavorite::TYPE_PROJECT, UserFavorite::TYPE_FILE]; + $allowedTypes = [UserFavorite::TYPE_TASK, UserFavorite::TYPE_PROJECT, UserFavorite::TYPE_FILE, UserFavorite::TYPE_MESSAGE]; if (!in_array($type, $allowedTypes)) { return Base::retError('无效的收藏类型'); } @@ -2914,6 +2914,12 @@ class UsersController extends AbstractController return Base::retError('文件不存在'); } break; + case UserFavorite::TYPE_MESSAGE: + $object = WebSocketDialogMsg::whereId($id)->first(); + if (!$object) { + return Base::retError('消息不存在'); + } + break; } // $result = UserFavorite::toggleFavorite($user->userid, $type, $id); @@ -2930,7 +2936,7 @@ class UsersController extends AbstractController * @apiGroup users * @apiName favorites__clean * - * @apiParam {String} [type] 收藏类型 (task/project/file),不传则清理全部 + * @apiParam {String} [type] 收藏类型 (task/project/file/message),不传则清理全部 * * @apiSuccess {Number} ret 返回状态码(1正确、0错误) * @apiSuccess {String} msg 返回信息(错误描述) @@ -2944,7 +2950,7 @@ class UsersController extends AbstractController // // 验证收藏类型 if ($type) { - $allowedTypes = [UserFavorite::TYPE_TASK, UserFavorite::TYPE_PROJECT, UserFavorite::TYPE_FILE]; + $allowedTypes = [UserFavorite::TYPE_TASK, UserFavorite::TYPE_PROJECT, UserFavorite::TYPE_FILE, UserFavorite::TYPE_MESSAGE]; if (!in_array($type, $allowedTypes)) { return Base::retError('无效的收藏类型'); } @@ -2964,7 +2970,7 @@ class UsersController extends AbstractController * @apiGroup users * @apiName favorite__check * - * @apiParam {String} type 收藏类型 (task/project/file) + * @apiParam {String} type 收藏类型 (task/project/file/message) * @apiParam {Number} id 收藏对象ID * * @apiSuccess {Number} ret 返回状态码(1正确、0错误) @@ -2983,7 +2989,7 @@ class UsersController extends AbstractController } // // 验证收藏类型 - $allowedTypes = [UserFavorite::TYPE_TASK, UserFavorite::TYPE_PROJECT, UserFavorite::TYPE_FILE]; + $allowedTypes = [UserFavorite::TYPE_TASK, UserFavorite::TYPE_PROJECT, UserFavorite::TYPE_FILE, UserFavorite::TYPE_MESSAGE]; if (!in_array($type, $allowedTypes)) { return Base::retError('无效的收藏类型'); } diff --git a/app/Models/UserFavorite.php b/app/Models/UserFavorite.php index d42eb8530..ed46fe6c5 100644 --- a/app/Models/UserFavorite.php +++ b/app/Models/UserFavorite.php @@ -38,6 +38,7 @@ class UserFavorite extends AbstractModel const TYPE_TASK = 'task'; const TYPE_PROJECT = 'project'; const TYPE_FILE = 'file'; + const TYPE_MESSAGE = 'message'; protected $fillable = [ 'userid', @@ -126,13 +127,15 @@ class UserFavorite extends AbstractModel $data = [ 'tasks' => [], 'projects' => [], - 'files' => [] + 'files' => [], + 'messages' => [] ]; // 分组收集ID $taskIds = []; $projectIds = []; $fileIds = []; + $messageIds = []; foreach ($favorites->items() as $favorite) { switch ($favorite->favoritable_type) { @@ -145,6 +148,9 @@ class UserFavorite extends AbstractModel case self::TYPE_FILE: $fileIds[] = $favorite->favoritable_id; break; + case self::TYPE_MESSAGE: + $messageIds[] = $favorite->favoritable_id; + break; } } @@ -230,6 +236,38 @@ class UserFavorite extends AbstractModel } } + if (!empty($messageIds)) { + $messages = WebSocketDialogMsg::select([ + 'id', 'dialog_id', 'userid', 'type', 'msg', 'created_at' + ])->whereIn('id', $messageIds)->get()->keyBy('id'); + + foreach ($favorites->items() as $favorite) { + if ($favorite->favoritable_type === self::TYPE_MESSAGE && isset($messages[$favorite->favoritable_id])) { + $message = $messages[$favorite->favoritable_id]; + + // 使用 previewTextMsg 获取消息预览文本 + $previewText = ''; + if ($message->msg && is_array($message->msg)) { + $previewText = WebSocketDialogMsg::previewTextMsg($message->msg); + } + + // 如果没有预览文本,使用消息类型作为标题 + if (empty($previewText)) { + $previewText = '[' . ucfirst($message->type) . ']'; + } + + $data['messages'][] = [ + 'id' => $message->id, + 'name' => $previewText, + 'dialog_id' => $message->dialog_id, + 'userid' => $message->userid, + 'type' => $message->type, + 'favorited_at' => Carbon::parse($favorite->created_at)->format('Y-m-d H:i:s'), + ]; + } + } + } + return [ 'data' => $data, 'total' => $favorites->total(), diff --git a/resources/assets/js/pages/manage/components/DialogWrapper.vue b/resources/assets/js/pages/manage/components/DialogWrapper.vue index e2705510d..f3549b123 100644 --- a/resources/assets/js/pages/manage/components/DialogWrapper.vue +++ b/resources/assets/js/pages/manage/components/DialogWrapper.vue @@ -376,6 +376,10 @@ {{ $L(operateItem.tag ? '取消标注' : '标注') }} +
  • + {{ operateItem.favorited ? '' : '' }} + {{ $L(operateItem.favorited ? '取消收藏' : '收藏') }} +
  • {{ $L('新任务') }} @@ -3135,6 +3139,9 @@ export default { }) } } + if (this.operateVisible) { + this.checkMessageFavoriteStatus(this.operateItem); + } requestAnimationFrame(() => { this.operateItem.clientX = event.clientX this.operateItem.clientY = event.clientY @@ -3286,6 +3293,10 @@ export default { this.onTag() break; + case "favorite": + this.onFavorite() + break; + case "newTask": let content = $A.formatMsgBasic(this.operateItem.msg.text) content = content.replace(/]*?src=(["'])([^"']+?)(_thumb\.(png|jpg|jpeg))?\1[^>]*?>/g, ``) @@ -4064,6 +4075,47 @@ export default { }); }, + onFavorite() { + if (this.operateVisible) { + return + } + + this.$store.dispatch("toggleFavorite", { + type: 'message', + id: this.operateItem.id + }).then(({data, msg}) => { + this.$set(this.operateItem, 'favorited', data.favorited); + const message = this.dialogMsgs.find(msg => msg.id === this.operateItem.id); + if (message) { + this.$set(message, 'favorited', data.favorited); + } + this.$Message.success(msg); + }).catch(({msg}) => { + $A.messageError(msg); + }); + }, + + checkMessageFavoriteStatus(message) { + if (!message.id) return; + + this.$store.dispatch("checkFavoriteStatus", { + type: 'message', + id: message.id + }).then(({data}) => { + this.$set(this.operateItem, 'favorited', data.favorited || false); + const msgInList = this.dialogMsgs.find(msg => msg.id === message.id); + if (msgInList) { + this.$set(msgInList, 'favorited', data.favorited || false); + } + }).catch(() => { + this.$set(this.operateItem, 'favorited', false); + const msgInList = this.dialogMsgs.find(msg => msg.id === message.id); + if (msgInList) { + this.$set(msgInList, 'favorited', false); + } + }); + }, + onTypeChange(val) { if (val === 'user') { if (this.todoSettingData.userids.length === 0 && this.todoSettingData.quick_value.length > 0) { diff --git a/resources/assets/js/pages/manage/components/FavoriteManagement.vue b/resources/assets/js/pages/manage/components/FavoriteManagement.vue index d03f5d21f..66db01f6b 100644 --- a/resources/assets/js/pages/manage/components/FavoriteManagement.vue +++ b/resources/assets/js/pages/manage/components/FavoriteManagement.vue @@ -18,6 +18,7 @@ +
  • @@ -85,16 +86,19 @@ export default { const typeMap = { 'task': this.$L('任务'), 'project': this.$L('项目'), - 'file': this.$L('文件') + 'file': this.$L('文件'), + 'message': this.$L('消息') }; const color = { - 'task': 'primary', - 'project': 'success', - 'file': 'warning' + 'task': 'success', + 'project': '#f87cbd', + 'file': 'warning', + 'message': 'primary' }; return h('Tag', { + class: 'favorite-type-tag', props: { - color: color[row.type] || 'default' + color: color[row.type] || 'primary' } }, typeMap[row.type] || row.type); } @@ -288,6 +292,21 @@ export default { }); } + // 处理消息收藏 + if (data.data.messages) { + data.data.messages.forEach(message => { + this.allData.push({ + id: message.id, + type: 'message', + name: message.name, + dialog_id: message.dialog_id, + userid: message.userid, + msg_type: message.type, + favorited_at: message.favorited_at, + }); + }); + } + this.total = data.total || this.allData.length; this.filterData(); this.noText = '没有相关的收藏'; @@ -349,6 +368,16 @@ export default { }, 600); this.$emit('on-close'); break; + case 'message': + this.$store.dispatch("openDialog", item.dialog_id).then(() => { + this.$store.state.dialogSearchMsgId = item.id; + if (this.$route.name === 'manage-messenger') { + this.$emit('on-close'); + } + }).catch(({msg}) => { + $A.modalError(msg || this.$L('打开会话失败')); + }); + break; } }, diff --git a/resources/assets/js/store/actions.js b/resources/assets/js/store/actions.js index 7bf76565d..c1aa7f755 100644 --- a/resources/assets/js/store/actions.js +++ b/resources/assets/js/store/actions.js @@ -2851,7 +2851,7 @@ export default { /** * 检查收藏状态 * @param dispatch - * @param {object} params {type: 'task|project|file', id: number} + * @param {object} params {type: 'task|project|file|message', id: number} */ checkFavoriteStatus({dispatch}, {type, id}) { return dispatch('call', { @@ -2868,7 +2868,7 @@ export default { /** * 切换收藏状态 * @param dispatch - * @param {object} params {type: 'task|project|file', id: number} + * @param {object} params {type: 'task|project|file|message', id: number} */ toggleFavorite({dispatch}, {type, id}) { return dispatch('call', { @@ -2884,7 +2884,7 @@ export default { /** * 批量检查收藏状态 * @param dispatch - * @param {object} params {type: 'task|project|file', items: array} + * @param {object} params {type: 'task|project|file|message', items: array} */ checkFavoritesStatus({dispatch}, {type, items}) { if (!Array.isArray(items) || items.length === 0) { diff --git a/resources/assets/sass/dark.scss b/resources/assets/sass/dark.scss index d51006d5a..55a60d633 100644 --- a/resources/assets/sass/dark.scss +++ b/resources/assets/sass/dark.scss @@ -758,4 +758,11 @@ body.dark-mode-reverse { } } } + + .favorite-type-tag { + .ivu-tag-text { + -webkit-filter: invert(100%); + filter: invert(100%); + } + } }