mirror of
https://github.com/kuaifan/dootask.git
synced 2026-01-15 03:08:11 +00:00
feat: 扩展收藏功能,支持消息类型的收藏
- 在 UserFavorite 模型中添加消息类型常量 - 更新 UsersController,支持消息的收藏、切换和状态检查 - 修改前端 Vue 组件以实现消息的收藏操作和状态显示 - 优化收藏管理界面,支持消息类型的展示与处理
This commit is contained in:
parent
18a922b5cd
commit
73ca4b1ea5
@ -2835,7 +2835,7 @@ class UsersController extends AbstractController
|
|||||||
* @apiGroup users
|
* @apiGroup users
|
||||||
* @apiName favorites
|
* @apiName favorites
|
||||||
*
|
*
|
||||||
* @apiParam {String} [type] 收藏类型过滤 (task/project/file)
|
* @apiParam {String} [type] 收藏类型过滤 (task/project/file/message)
|
||||||
* @apiParam {Number} [page=1] 页码
|
* @apiParam {Number} [page=1] 页码
|
||||||
* @apiParam {Number} [pagesize=20] 每页数量
|
* @apiParam {Number} [pagesize=20] 每页数量
|
||||||
*
|
*
|
||||||
@ -2852,7 +2852,7 @@ class UsersController extends AbstractController
|
|||||||
$pageSize = min(intval(Request::input('pagesize', 20)), 100);
|
$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)) {
|
if ($type && !in_array($type, $allowedTypes)) {
|
||||||
return Base::retError('无效的收藏类型');
|
return Base::retError('无效的收藏类型');
|
||||||
}
|
}
|
||||||
@ -2870,7 +2870,7 @@ class UsersController extends AbstractController
|
|||||||
* @apiGroup users
|
* @apiGroup users
|
||||||
* @apiName favorite__toggle
|
* @apiName favorite__toggle
|
||||||
*
|
*
|
||||||
* @apiParam {String} type 收藏类型 (task/project/file)
|
* @apiParam {String} type 收藏类型 (task/project/file/message)
|
||||||
* @apiParam {Number} id 收藏对象ID
|
* @apiParam {Number} id 收藏对象ID
|
||||||
*
|
*
|
||||||
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
* @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)) {
|
if (!in_array($type, $allowedTypes)) {
|
||||||
return Base::retError('无效的收藏类型');
|
return Base::retError('无效的收藏类型');
|
||||||
}
|
}
|
||||||
@ -2914,6 +2914,12 @@ class UsersController extends AbstractController
|
|||||||
return Base::retError('文件不存在');
|
return Base::retError('文件不存在');
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case UserFavorite::TYPE_MESSAGE:
|
||||||
|
$object = WebSocketDialogMsg::whereId($id)->first();
|
||||||
|
if (!$object) {
|
||||||
|
return Base::retError('消息不存在');
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
//
|
//
|
||||||
$result = UserFavorite::toggleFavorite($user->userid, $type, $id);
|
$result = UserFavorite::toggleFavorite($user->userid, $type, $id);
|
||||||
@ -2930,7 +2936,7 @@ class UsersController extends AbstractController
|
|||||||
* @apiGroup users
|
* @apiGroup users
|
||||||
* @apiName favorites__clean
|
* @apiName favorites__clean
|
||||||
*
|
*
|
||||||
* @apiParam {String} [type] 收藏类型 (task/project/file),不传则清理全部
|
* @apiParam {String} [type] 收藏类型 (task/project/file/message),不传则清理全部
|
||||||
*
|
*
|
||||||
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||||
* @apiSuccess {String} msg 返回信息(错误描述)
|
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||||
@ -2944,7 +2950,7 @@ class UsersController extends AbstractController
|
|||||||
//
|
//
|
||||||
// 验证收藏类型
|
// 验证收藏类型
|
||||||
if ($type) {
|
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)) {
|
if (!in_array($type, $allowedTypes)) {
|
||||||
return Base::retError('无效的收藏类型');
|
return Base::retError('无效的收藏类型');
|
||||||
}
|
}
|
||||||
@ -2964,7 +2970,7 @@ class UsersController extends AbstractController
|
|||||||
* @apiGroup users
|
* @apiGroup users
|
||||||
* @apiName favorite__check
|
* @apiName favorite__check
|
||||||
*
|
*
|
||||||
* @apiParam {String} type 收藏类型 (task/project/file)
|
* @apiParam {String} type 收藏类型 (task/project/file/message)
|
||||||
* @apiParam {Number} id 收藏对象ID
|
* @apiParam {Number} id 收藏对象ID
|
||||||
*
|
*
|
||||||
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
* @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)) {
|
if (!in_array($type, $allowedTypes)) {
|
||||||
return Base::retError('无效的收藏类型');
|
return Base::retError('无效的收藏类型');
|
||||||
}
|
}
|
||||||
|
|||||||
@ -38,6 +38,7 @@ class UserFavorite extends AbstractModel
|
|||||||
const TYPE_TASK = 'task';
|
const TYPE_TASK = 'task';
|
||||||
const TYPE_PROJECT = 'project';
|
const TYPE_PROJECT = 'project';
|
||||||
const TYPE_FILE = 'file';
|
const TYPE_FILE = 'file';
|
||||||
|
const TYPE_MESSAGE = 'message';
|
||||||
|
|
||||||
protected $fillable = [
|
protected $fillable = [
|
||||||
'userid',
|
'userid',
|
||||||
@ -126,13 +127,15 @@ class UserFavorite extends AbstractModel
|
|||||||
$data = [
|
$data = [
|
||||||
'tasks' => [],
|
'tasks' => [],
|
||||||
'projects' => [],
|
'projects' => [],
|
||||||
'files' => []
|
'files' => [],
|
||||||
|
'messages' => []
|
||||||
];
|
];
|
||||||
|
|
||||||
// 分组收集ID
|
// 分组收集ID
|
||||||
$taskIds = [];
|
$taskIds = [];
|
||||||
$projectIds = [];
|
$projectIds = [];
|
||||||
$fileIds = [];
|
$fileIds = [];
|
||||||
|
$messageIds = [];
|
||||||
|
|
||||||
foreach ($favorites->items() as $favorite) {
|
foreach ($favorites->items() as $favorite) {
|
||||||
switch ($favorite->favoritable_type) {
|
switch ($favorite->favoritable_type) {
|
||||||
@ -145,6 +148,9 @@ class UserFavorite extends AbstractModel
|
|||||||
case self::TYPE_FILE:
|
case self::TYPE_FILE:
|
||||||
$fileIds[] = $favorite->favoritable_id;
|
$fileIds[] = $favorite->favoritable_id;
|
||||||
break;
|
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 [
|
return [
|
||||||
'data' => $data,
|
'data' => $data,
|
||||||
'total' => $favorites->total(),
|
'total' => $favorites->total(),
|
||||||
|
|||||||
@ -376,6 +376,10 @@
|
|||||||
<i class="taskfont"></i>
|
<i class="taskfont"></i>
|
||||||
<span>{{ $L(operateItem.tag ? '取消标注' : '标注') }}</span>
|
<span>{{ $L(operateItem.tag ? '取消标注' : '标注') }}</span>
|
||||||
</li>
|
</li>
|
||||||
|
<li @click="onOperate('favorite')">
|
||||||
|
<i class="taskfont">{{ operateItem.favorited ? '' : '' }}</i>
|
||||||
|
<span>{{ $L(operateItem.favorited ? '取消收藏' : '收藏') }}</span>
|
||||||
|
</li>
|
||||||
<li v-if="actionPermission(operateItem, 'newTask')" @click="onOperate('newTask')">
|
<li v-if="actionPermission(operateItem, 'newTask')" @click="onOperate('newTask')">
|
||||||
<i class="taskfont"></i>
|
<i class="taskfont"></i>
|
||||||
<span>{{ $L('新任务') }}</span>
|
<span>{{ $L('新任务') }}</span>
|
||||||
@ -3135,6 +3139,9 @@ export default {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (this.operateVisible) {
|
||||||
|
this.checkMessageFavoriteStatus(this.operateItem);
|
||||||
|
}
|
||||||
requestAnimationFrame(() => {
|
requestAnimationFrame(() => {
|
||||||
this.operateItem.clientX = event.clientX
|
this.operateItem.clientX = event.clientX
|
||||||
this.operateItem.clientY = event.clientY
|
this.operateItem.clientY = event.clientY
|
||||||
@ -3286,6 +3293,10 @@ export default {
|
|||||||
this.onTag()
|
this.onTag()
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case "favorite":
|
||||||
|
this.onFavorite()
|
||||||
|
break;
|
||||||
|
|
||||||
case "newTask":
|
case "newTask":
|
||||||
let content = $A.formatMsgBasic(this.operateItem.msg.text)
|
let content = $A.formatMsgBasic(this.operateItem.msg.text)
|
||||||
content = content.replace(/<img[^>]*?src=(["'])([^"']+?)(_thumb\.(png|jpg|jpeg))?\1[^>]*?>/g, `<img src="$2">`)
|
content = content.replace(/<img[^>]*?src=(["'])([^"']+?)(_thumb\.(png|jpg|jpeg))?\1[^>]*?>/g, `<img src="$2">`)
|
||||||
@ -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) {
|
onTypeChange(val) {
|
||||||
if (val === 'user') {
|
if (val === 'user') {
|
||||||
if (this.todoSettingData.userids.length === 0 && this.todoSettingData.quick_value.length > 0) {
|
if (this.todoSettingData.userids.length === 0 && this.todoSettingData.quick_value.length > 0) {
|
||||||
|
|||||||
@ -18,6 +18,7 @@
|
|||||||
<Option value="task">{{$L('任务')}}</Option>
|
<Option value="task">{{$L('任务')}}</Option>
|
||||||
<Option value="project">{{$L('项目')}}</Option>
|
<Option value="project">{{$L('项目')}}</Option>
|
||||||
<Option value="file">{{$L('文件')}}</Option>
|
<Option value="file">{{$L('文件')}}</Option>
|
||||||
|
<Option value="message">{{$L('消息')}}</Option>
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
@ -85,16 +86,19 @@ export default {
|
|||||||
const typeMap = {
|
const typeMap = {
|
||||||
'task': this.$L('任务'),
|
'task': this.$L('任务'),
|
||||||
'project': this.$L('项目'),
|
'project': this.$L('项目'),
|
||||||
'file': this.$L('文件')
|
'file': this.$L('文件'),
|
||||||
|
'message': this.$L('消息')
|
||||||
};
|
};
|
||||||
const color = {
|
const color = {
|
||||||
'task': 'primary',
|
'task': 'success',
|
||||||
'project': 'success',
|
'project': '#f87cbd',
|
||||||
'file': 'warning'
|
'file': 'warning',
|
||||||
|
'message': 'primary'
|
||||||
};
|
};
|
||||||
return h('Tag', {
|
return h('Tag', {
|
||||||
|
class: 'favorite-type-tag',
|
||||||
props: {
|
props: {
|
||||||
color: color[row.type] || 'default'
|
color: color[row.type] || 'primary'
|
||||||
}
|
}
|
||||||
}, typeMap[row.type] || row.type);
|
}, 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.total = data.total || this.allData.length;
|
||||||
this.filterData();
|
this.filterData();
|
||||||
this.noText = '没有相关的收藏';
|
this.noText = '没有相关的收藏';
|
||||||
@ -349,6 +368,16 @@ export default {
|
|||||||
}, 600);
|
}, 600);
|
||||||
this.$emit('on-close');
|
this.$emit('on-close');
|
||||||
break;
|
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;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
6
resources/assets/js/store/actions.js
vendored
6
resources/assets/js/store/actions.js
vendored
@ -2851,7 +2851,7 @@ export default {
|
|||||||
/**
|
/**
|
||||||
* 检查收藏状态
|
* 检查收藏状态
|
||||||
* @param dispatch
|
* @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}) {
|
checkFavoriteStatus({dispatch}, {type, id}) {
|
||||||
return dispatch('call', {
|
return dispatch('call', {
|
||||||
@ -2868,7 +2868,7 @@ export default {
|
|||||||
/**
|
/**
|
||||||
* 切换收藏状态
|
* 切换收藏状态
|
||||||
* @param dispatch
|
* @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}) {
|
toggleFavorite({dispatch}, {type, id}) {
|
||||||
return dispatch('call', {
|
return dispatch('call', {
|
||||||
@ -2884,7 +2884,7 @@ export default {
|
|||||||
/**
|
/**
|
||||||
* 批量检查收藏状态
|
* 批量检查收藏状态
|
||||||
* @param dispatch
|
* @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}) {
|
checkFavoritesStatus({dispatch}, {type, items}) {
|
||||||
if (!Array.isArray(items) || items.length === 0) {
|
if (!Array.isArray(items) || items.length === 0) {
|
||||||
|
|||||||
7
resources/assets/sass/dark.scss
vendored
7
resources/assets/sass/dark.scss
vendored
@ -758,4 +758,11 @@ body.dark-mode-reverse {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.favorite-type-tag {
|
||||||
|
.ivu-tag-text {
|
||||||
|
-webkit-filter: invert(100%);
|
||||||
|
filter: invert(100%);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user