mirror of
https://github.com/kuaifan/dootask.git
synced 2025-12-12 11:19:56 +00:00
feat: 添加收藏备注功能
- 在 UsersController 中新增 favorite__remark 方法,支持用户修改收藏的备注 - 在 UserFavorite 模型中添加更新备注的逻辑 - 新增数据库迁移以添加备注字段 - 更新前端组件以支持备注的显示和编辑 - 优化收藏操作的用户体验
This commit is contained in:
parent
89bdd86f14
commit
a268391e68
@ -3188,6 +3188,58 @@ class UsersController extends AbstractController
|
|||||||
return Base::retSuccess($message, $result);
|
return Base::retSuccess($message, $result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @api {post} api/users/favorite/remark 47-1. 修改收藏备注
|
||||||
|
*
|
||||||
|
* @apiDescription 需要token身份
|
||||||
|
* @apiVersion 1.0.0
|
||||||
|
* @apiGroup users
|
||||||
|
* @apiName favorite__remark
|
||||||
|
*
|
||||||
|
* @apiParam {String} type 收藏类型 (task/project/file/message)
|
||||||
|
* @apiParam {Number} id 收藏对象ID
|
||||||
|
* @apiParam {String} remark 收藏备注(<=255个字符)
|
||||||
|
*
|
||||||
|
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||||
|
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||||
|
* @apiSuccess {Object} data 返回数据
|
||||||
|
*/
|
||||||
|
public function favorite__remark()
|
||||||
|
{
|
||||||
|
$user = User::auth();
|
||||||
|
//
|
||||||
|
$type = trim(Request::input('type'));
|
||||||
|
$id = intval(Request::input('id'));
|
||||||
|
$remark = trim(Request::input('remark', ''));
|
||||||
|
|
||||||
|
if (!$type || $id <= 0) {
|
||||||
|
return Base::retError('参数错误');
|
||||||
|
}
|
||||||
|
|
||||||
|
$allowedTypes = [UserFavorite::TYPE_TASK, UserFavorite::TYPE_PROJECT, UserFavorite::TYPE_FILE, UserFavorite::TYPE_MESSAGE];
|
||||||
|
if (!in_array($type, $allowedTypes)) {
|
||||||
|
return Base::retError('无效的收藏类型');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($remark === '') {
|
||||||
|
return Base::retError('请输入修改备注');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mb_strlen($remark) > 255) {
|
||||||
|
return Base::retError('备注最多支持255个字符');
|
||||||
|
}
|
||||||
|
|
||||||
|
$favorite = UserFavorite::updateRemark($user->userid, $type, $id, $remark);
|
||||||
|
|
||||||
|
if (!$favorite) {
|
||||||
|
return Base::retError('收藏记录不存在');
|
||||||
|
}
|
||||||
|
|
||||||
|
return Base::retSuccess('修改备注成功', [
|
||||||
|
'remark' => $favorite->remark,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @api {post} api/users/favorites/clean 48. 清理用户收藏
|
* @api {post} api/users/favorites/clean 48. 清理用户收藏
|
||||||
*
|
*
|
||||||
|
|||||||
@ -44,6 +44,7 @@ class UserFavorite extends AbstractModel
|
|||||||
'userid',
|
'userid',
|
||||||
'favoritable_type',
|
'favoritable_type',
|
||||||
'favoritable_id',
|
'favoritable_id',
|
||||||
|
'remark',
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -79,16 +80,42 @@ class UserFavorite extends AbstractModel
|
|||||||
if ($favorite) {
|
if ($favorite) {
|
||||||
// 取消收藏
|
// 取消收藏
|
||||||
$favorite->delete();
|
$favorite->delete();
|
||||||
return ['favorited' => false, 'action' => 'removed'];
|
return ['favorited' => false, 'action' => 'removed', 'remark' => ''];
|
||||||
} else {
|
}
|
||||||
|
|
||||||
// 添加收藏
|
// 添加收藏
|
||||||
self::create([
|
$favorite = self::create([
|
||||||
'userid' => $userid,
|
'userid' => $userid,
|
||||||
'favoritable_type' => $type,
|
'favoritable_type' => $type,
|
||||||
'favoritable_id' => $id,
|
'favoritable_id' => $id,
|
||||||
]);
|
]);
|
||||||
return ['favorited' => true, 'action' => 'added'];
|
|
||||||
|
return ['favorited' => true, 'action' => 'added', 'remark' => $favorite->remark ?? ''];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新收藏备注
|
||||||
|
* @param int $userid
|
||||||
|
* @param string $type
|
||||||
|
* @param int $id
|
||||||
|
* @param string $remark
|
||||||
|
* @return static|null
|
||||||
|
*/
|
||||||
|
public static function updateRemark($userid, $type, $id, $remark)
|
||||||
|
{
|
||||||
|
$favorite = self::whereUserid($userid)
|
||||||
|
->whereFavoritableType($type)
|
||||||
|
->whereFavoritableId($id)
|
||||||
|
->first();
|
||||||
|
|
||||||
|
if (!$favorite) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$favorite->remark = $remark;
|
||||||
|
$favorite->save();
|
||||||
|
|
||||||
|
return $favorite;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -192,6 +219,7 @@ class UserFavorite extends AbstractModel
|
|||||||
'flow_item_status' => $flowItemStatus,
|
'flow_item_status' => $flowItemStatus,
|
||||||
'flow_item_color' => $flowItemColor,
|
'flow_item_color' => $flowItemColor,
|
||||||
'favorited_at' => Carbon::parse($favorite->created_at)->format('Y-m-d H:i:s'),
|
'favorited_at' => Carbon::parse($favorite->created_at)->format('Y-m-d H:i:s'),
|
||||||
|
'remark' => $favorite->remark,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -211,6 +239,7 @@ class UserFavorite extends AbstractModel
|
|||||||
'desc' => $project->desc,
|
'desc' => $project->desc,
|
||||||
'archived_at' => $project->archived_at,
|
'archived_at' => $project->archived_at,
|
||||||
'favorited_at' => Carbon::parse($favorite->created_at)->format('Y-m-d H:i:s'),
|
'favorited_at' => Carbon::parse($favorite->created_at)->format('Y-m-d H:i:s'),
|
||||||
|
'remark' => $favorite->remark,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -231,6 +260,7 @@ class UserFavorite extends AbstractModel
|
|||||||
'size' => $file->size,
|
'size' => $file->size,
|
||||||
'pid' => $file->pid,
|
'pid' => $file->pid,
|
||||||
'favorited_at' => Carbon::parse($favorite->created_at)->format('Y-m-d H:i:s'),
|
'favorited_at' => Carbon::parse($favorite->created_at)->format('Y-m-d H:i:s'),
|
||||||
|
'remark' => $favorite->remark,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -263,6 +293,7 @@ class UserFavorite extends AbstractModel
|
|||||||
'userid' => $message->userid,
|
'userid' => $message->userid,
|
||||||
'type' => $message->type,
|
'type' => $message->type,
|
||||||
'favorited_at' => Carbon::parse($favorite->created_at)->format('Y-m-d H:i:s'),
|
'favorited_at' => Carbon::parse($favorite->created_at)->format('Y-m-d H:i:s'),
|
||||||
|
'remark' => $favorite->remark,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
class AddRemarkToUserFavoritesTable extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::table('user_favorites', function (Blueprint $table) {
|
||||||
|
if (!Schema::hasColumn('user_favorites', 'remark')) {
|
||||||
|
$table->string('remark', 255)->default('')->after('favoritable_id')->comment('收藏备注');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Schema::table('user_favorites', function (Blueprint $table) {
|
||||||
|
if (Schema::hasColumn('user_favorites', 'remark')) {
|
||||||
|
$table->dropColumn('remark');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
5
resources/assets/js/app.js
vendored
5
resources/assets/js/app.js
vendored
@ -108,6 +108,11 @@ router.afterEach(() => {
|
|||||||
store.commit('route/loading', false);
|
store.commit('route/loading', false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 消息配置
|
||||||
|
ViewUI.Message.config({
|
||||||
|
duration: 2.5
|
||||||
|
});
|
||||||
|
|
||||||
// 加载路由
|
// 加载路由
|
||||||
Vue.prototype.goForward = function(route, isReplace, autoBroadcast = true) {
|
Vue.prototype.goForward = function(route, isReplace, autoBroadcast = true) {
|
||||||
if ($A.Ready && $A.isSubElectron && autoBroadcast) {
|
if ($A.Ready && $A.isSubElectron && autoBroadcast) {
|
||||||
|
|||||||
@ -4083,15 +4083,12 @@ export default {
|
|||||||
this.$store.dispatch("toggleFavorite", {
|
this.$store.dispatch("toggleFavorite", {
|
||||||
type: 'message',
|
type: 'message',
|
||||||
id: this.operateItem.id
|
id: this.operateItem.id
|
||||||
}).then(({data, msg}) => {
|
}).then(({data}) => {
|
||||||
this.$set(this.operateItem, 'favorited', data.favorited);
|
this.$set(this.operateItem, 'favorited', data.favorited);
|
||||||
const message = this.dialogMsgs.find(msg => msg.id === this.operateItem.id);
|
const message = this.dialogMsgs.find(msg => msg.id === this.operateItem.id);
|
||||||
if (message) {
|
if (message) {
|
||||||
this.$set(message, 'favorited', data.favorited);
|
this.$set(message, 'favorited', data.favorited);
|
||||||
}
|
}
|
||||||
this.$Message.success(msg);
|
|
||||||
}).catch(({msg}) => {
|
|
||||||
$A.messageError(msg);
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@ -66,10 +66,11 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import SearchButton from "../../../components/SearchButton.vue";
|
import SearchButton from "../../../components/SearchButton.vue";
|
||||||
|
import QuickEdit from "../../../components/QuickEdit.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "FavoriteManagement",
|
name: "FavoriteManagement",
|
||||||
components: {SearchButton},
|
components: {SearchButton, QuickEdit},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
loadIng: 0,
|
loadIng: 0,
|
||||||
@ -118,6 +119,53 @@ export default {
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: this.$L('备注'),
|
||||||
|
key: 'remark',
|
||||||
|
minWidth: 160,
|
||||||
|
render: (h, {row}) => {
|
||||||
|
return h('QuickEdit', {
|
||||||
|
props: {
|
||||||
|
value: row.remark || '',
|
||||||
|
attrTitle: row.remark || '',
|
||||||
|
alwaysIcon: true,
|
||||||
|
},
|
||||||
|
on: {
|
||||||
|
'on-update': (val, cb) => {
|
||||||
|
const remark = (val || '').trim();
|
||||||
|
if (!remark) {
|
||||||
|
$A.messageWarning(this.$L('请输入修改备注'));
|
||||||
|
cb();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.$store.dispatch('call', {
|
||||||
|
url: 'users/favorite/remark',
|
||||||
|
data: {
|
||||||
|
type: row.type,
|
||||||
|
id: row.id,
|
||||||
|
remark,
|
||||||
|
},
|
||||||
|
method: 'post',
|
||||||
|
}).then(({data, msg}) => {
|
||||||
|
const newRemark = data && typeof data.remark !== 'undefined' ? data.remark : remark;
|
||||||
|
row.remark = newRemark;
|
||||||
|
const target = this.allData.find(item => item.id === row.id && item.type === row.type);
|
||||||
|
if (target) {
|
||||||
|
target.remark = newRemark;
|
||||||
|
}
|
||||||
|
$A.messageSuccess(msg || this.$L('操作成功'));
|
||||||
|
cb();
|
||||||
|
}).catch(({msg}) => {
|
||||||
|
$A.modalError(msg || this.$L('操作失败'));
|
||||||
|
cb();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [
|
||||||
|
h('AutoTip', row.remark || '-')
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: this.$L('所属项目'),
|
title: this.$L('所属项目'),
|
||||||
key: 'project_name',
|
key: 'project_name',
|
||||||
@ -259,6 +307,7 @@ export default {
|
|||||||
flow_item_status: task.flow_item_status,
|
flow_item_status: task.flow_item_status,
|
||||||
flow_item_color: task.flow_item_color,
|
flow_item_color: task.flow_item_color,
|
||||||
favorited_at: task.favorited_at,
|
favorited_at: task.favorited_at,
|
||||||
|
remark: task.remark || '',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -273,6 +322,7 @@ export default {
|
|||||||
desc: project.desc,
|
desc: project.desc,
|
||||||
archived_at: project.archived_at,
|
archived_at: project.archived_at,
|
||||||
favorited_at: project.favorited_at,
|
favorited_at: project.favorited_at,
|
||||||
|
remark: project.remark || '',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -288,6 +338,7 @@ export default {
|
|||||||
size: file.size,
|
size: file.size,
|
||||||
pid: file.pid,
|
pid: file.pid,
|
||||||
favorited_at: file.favorited_at,
|
favorited_at: file.favorited_at,
|
||||||
|
remark: file.remark || '',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -303,6 +354,7 @@ export default {
|
|||||||
userid: message.userid,
|
userid: message.userid,
|
||||||
msg_type: message.type,
|
msg_type: message.type,
|
||||||
favorited_at: message.favorited_at,
|
favorited_at: message.favorited_at,
|
||||||
|
remark: message.remark || '',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -386,10 +438,7 @@ export default {
|
|||||||
type: item.type,
|
type: item.type,
|
||||||
id: item.id
|
id: item.id
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
$A.messageSuccess('取消收藏成功');
|
|
||||||
this.getLists();
|
this.getLists();
|
||||||
}).catch(({msg}) => {
|
|
||||||
$A.modalError(msg);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1896,11 +1896,8 @@ export default {
|
|||||||
this.$store.dispatch("toggleFavorite", {
|
this.$store.dispatch("toggleFavorite", {
|
||||||
type: 'project',
|
type: 'project',
|
||||||
id: this.projectData.id
|
id: this.projectData.id
|
||||||
}).then(({data, msg}) => {
|
}).then(({data}) => {
|
||||||
this.$set(this.projectData, 'favorited', data.favorited);
|
this.$set(this.projectData, 'favorited', data.favorited);
|
||||||
$A.messageSuccess(msg);
|
|
||||||
}).catch(({msg}) => {
|
|
||||||
$A.modalError(msg || this.$L('操作失败'));
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@ -525,12 +525,9 @@ export default {
|
|||||||
this.$store.dispatch("toggleFavorite", {
|
this.$store.dispatch("toggleFavorite", {
|
||||||
type: 'task',
|
type: 'task',
|
||||||
id: this.task.id
|
id: this.task.id
|
||||||
}).then(({data, msg}) => {
|
}).then(({data}) => {
|
||||||
this.isFavorited = data.favorited;
|
this.isFavorited = data.favorited;
|
||||||
this.hide();
|
this.hide();
|
||||||
$A.messageSuccess(msg);
|
|
||||||
}).catch(({msg}) => {
|
|
||||||
$A.messageError(msg || '操作失败');
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@ -2091,7 +2091,7 @@ export default {
|
|||||||
this.$store.dispatch("toggleFavorite", {
|
this.$store.dispatch("toggleFavorite", {
|
||||||
type: 'file',
|
type: 'file',
|
||||||
id: item.id
|
id: item.id
|
||||||
}).then(({data, msg}) => {
|
}).then(({data}) => {
|
||||||
const fileIndex = this.fileList.findIndex(file => file.id === item.id);
|
const fileIndex = this.fileList.findIndex(file => file.id === item.id);
|
||||||
if (fileIndex > -1) {
|
if (fileIndex > -1) {
|
||||||
this.$set(this.fileList[fileIndex], 'favorited', data.favorited);
|
this.$set(this.fileList[fileIndex], 'favorited', data.favorited);
|
||||||
@ -2099,9 +2099,6 @@ export default {
|
|||||||
if (this.contextMenuItem.id === item.id) {
|
if (this.contextMenuItem.id === item.id) {
|
||||||
this.$set(this.contextMenuItem, 'favorited', data.favorited);
|
this.$set(this.contextMenuItem, 'favorited', data.favorited);
|
||||||
}
|
}
|
||||||
$A.messageSuccess(msg);
|
|
||||||
}).catch(({msg}) => {
|
|
||||||
$A.modalError(msg || this.$L('操作失败'));
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
61
resources/assets/js/store/actions.js
vendored
61
resources/assets/js/store/actions.js
vendored
@ -2898,13 +2898,72 @@ export default {
|
|||||||
* @param {object} params {type: 'task|project|file|message', id: number}
|
* @param {object} params {type: 'task|project|file|message', id: number}
|
||||||
*/
|
*/
|
||||||
toggleFavorite({dispatch}, {type, id}) {
|
toggleFavorite({dispatch}, {type, id}) {
|
||||||
return dispatch('call', {
|
return new Promise((resolve, reject) => {
|
||||||
|
dispatch('call', {
|
||||||
url: 'users/favorite/toggle',
|
url: 'users/favorite/toggle',
|
||||||
data: {
|
data: {
|
||||||
type: type,
|
type: type,
|
||||||
id: id
|
id: id
|
||||||
},
|
},
|
||||||
method: 'post',
|
method: 'post',
|
||||||
|
}).then(result => {
|
||||||
|
resolve(result)
|
||||||
|
//
|
||||||
|
const {data, msg} = result
|
||||||
|
if (!data.favorited) {
|
||||||
|
$A.messageSuccess(msg);
|
||||||
|
return
|
||||||
|
}
|
||||||
|
$A.Message.success({
|
||||||
|
duration: 5,
|
||||||
|
render: h => {
|
||||||
|
return h('span', [
|
||||||
|
h('span', $A.L(msg)),
|
||||||
|
h('a', {
|
||||||
|
style: {
|
||||||
|
marginLeft: '8px'
|
||||||
|
},
|
||||||
|
on: {
|
||||||
|
click: () => {
|
||||||
|
const currentRemark = data && typeof data.remark === 'string' ? data.remark : '';
|
||||||
|
$A.modalInput({
|
||||||
|
title: $A.L('修改备注'),
|
||||||
|
placeholder: $A.L('请输入修改备注'),
|
||||||
|
okText: $A.L('保存'),
|
||||||
|
value: currentRemark,
|
||||||
|
onOk: (inputValue) => {
|
||||||
|
const remark = typeof inputValue === 'string' ? inputValue.trim() : '';
|
||||||
|
if (!remark) {
|
||||||
|
return $A.L('请输入修改备注');
|
||||||
|
}
|
||||||
|
return new Promise((resolveRemark, rejectRemark) => {
|
||||||
|
dispatch('call', {
|
||||||
|
url: 'users/favorite/remark',
|
||||||
|
data: {
|
||||||
|
type,
|
||||||
|
id,
|
||||||
|
remark,
|
||||||
|
},
|
||||||
|
method: 'post',
|
||||||
|
}).then(({msg}) => {
|
||||||
|
$A.messageSuccess(msg || $A.L('操作成功'));
|
||||||
|
resolveRemark();
|
||||||
|
}).catch(({msg}) => {
|
||||||
|
rejectRemark(msg || $A.L('操作失败'));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, $A.L('修改备注')),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}).catch(({msg}) => {
|
||||||
|
$A.modalError(msg || this.$L('操作失败'));
|
||||||
|
reject()
|
||||||
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user