diff --git a/app/Http/Controllers/Api/ProjectController.php b/app/Http/Controllers/Api/ProjectController.php
index dfde08bee..0f57c7a0a 100755
--- a/app/Http/Controllers/Api/ProjectController.php
+++ b/app/Http/Controllers/Api/ProjectController.php
@@ -169,7 +169,11 @@ class ProjectController extends AbstractController
$builder->where('projects.updated_at', '>', $timerange->updated);
}
//
- $list = $builder->orderByDesc('projects.id')->paginate(Base::getPaginate(100, 50));
+ $list = $builder
+ ->orderByDesc('project_users.top_at')
+ ->orderBy('project_users.sort')
+ ->orderByDesc('projects.id')
+ ->paginate(Base::getPaginate(100, 50));
$list->transform(function (Project $project) use ($getstatistics, $getuserid, $user) {
$array = $project->toArray();
if ($getuserid == 'yes') {
@@ -643,6 +647,39 @@ class ProjectController extends AbstractController
return Base::retSuccess('调整成功');
}
+ /**
+ * @api {post} api/project/user/sort 47. 项目列表排序
+ *
+ * @apiDescription 需要token身份,按当前用户对项目进行拖动排序,仅影响本人
+ * @apiVersion 1.0.0
+ * @apiGroup project
+ * @apiName user__sort
+ *
+ * @apiParam {Array} list 排序后的项目ID列表,如:[12,5,9]
+ *
+ * @apiSuccess {Number} ret 返回状态码(1正确、0错误)
+ * @apiSuccess {String} msg 返回信息(错误描述)
+ * @apiSuccess {Object} data 返回数据
+ */
+ public function user__sort()
+ {
+ $user = User::auth();
+ $list = Base::json2array(Request::input('list'));
+ if (!is_array($list)) {
+ return Base::retError('参数错误');
+ }
+ $index = 0;
+ foreach ($list as $projectId) {
+ $projectId = intval($projectId);
+ if ($projectId <= 0) continue;
+ ProjectUser::whereUserid($user->userid)
+ ->whereProjectId($projectId)
+ ->update(['sort' => $index]);
+ $index++;
+ }
+ return Base::retSuccess('排序已保存');
+ }
+
/**
* @api {get} api/project/exit 11. 退出项目
*
diff --git a/app/Models/Project.php b/app/Models/Project.php
index 97251c17a..10b26503e 100644
--- a/app/Models/Project.php
+++ b/app/Models/Project.php
@@ -129,6 +129,7 @@ class Project extends AbstractModel
'projects.*',
'project_users.owner',
'project_users.top_at',
+ 'project_users.sort',
])
->leftJoin('project_users', function ($leftJoin) use ($userid) {
$leftJoin
@@ -153,6 +154,7 @@ class Project extends AbstractModel
'projects.*',
'project_users.owner',
'project_users.top_at',
+ 'project_users.sort',
])
->join('project_users', 'projects.id', '=', 'project_users.project_id')
->where('project_users.userid', $userid);
diff --git a/resources/assets/js/pages/manage.vue b/resources/assets/js/pages/manage.vue
index addab3747..45f5b4e4b 100644
--- a/resources/assets/js/pages/manage.vue
+++ b/resources/assets/js/pages/manage.vue
@@ -128,12 +128,22 @@
-
+
{
- if (a.top_at || b.top_at) {
+ // 置顶优先
+ if (a.top_at !== b.top_at && (a.top_at || b.top_at)) {
return $A.sortDay(b.top_at, a.top_at);
}
+ // 自定义排序
+ const as = typeof a.sort === 'number' ? a.sort : Number.MAX_SAFE_INTEGER;
+ const bs = typeof b.sort === 'number' ? b.sort : Number.MAX_SAFE_INTEGER;
+ if (as !== bs) return as - bs;
+ // 兜底:按ID倒序
return b.id - a.id;
});
if (projectKeyValue) {
@@ -761,6 +782,15 @@ export default {
immediate: true
},
+ projectLists: {
+ handler(val) {
+ if (!this.projectDragging) {
+ this.projectDraggableList = val
+ }
+ },
+ immediate: true
+ },
+
unreadAndOverdue: {
handler(val) {
if (this.$Electron) {
@@ -1030,6 +1060,28 @@ export default {
}
},
+ onProjectSortEnd() {
+ // 只对非置顶项进行排序更新
+ const nonPinnedItems = this.projectDraggableList.filter(item => !item.top_at)
+ nonPinnedItems.forEach((item, index) => {
+ this.$store.dispatch("saveProject", {id: item.id, sort: index})
+ })
+ // 提交服务端保存
+ this.$store.dispatch("call", {
+ url: 'project/user/sort',
+ data: {
+ list: nonPinnedItems.map(item => item.id)
+ },
+ method: 'post',
+ }).then(({msg}) => {
+ $A.messageSuccess(msg)
+ }).catch(({msg}) => {
+ $A.modalError(msg)
+ }).finally(() => {
+ this.projectDragging = false
+ })
+ },
+
onAddTask(params) {
this.addTaskShow = true
this.$nextTick(_ => {
@@ -1194,6 +1246,7 @@ export default {
},
}).then(({data}) => {
this.$store.dispatch("saveProject", data);
+ this.projectDraggableList = this.projectLists
this.$nextTick(() => {
const active = this.$refs.menuProject.querySelector(".active")
if (active) {
diff --git a/resources/assets/js/pages/manage/components/ProjectList.vue b/resources/assets/js/pages/manage/components/ProjectList.vue
index 838d08f08..875a5f51f 100644
--- a/resources/assets/js/pages/manage/components/ProjectList.vue
+++ b/resources/assets/js/pages/manage/components/ProjectList.vue
@@ -12,21 +12,24 @@
-
-
-
- -
- {{$L(projectKeyValue ? `没有任何与"${projectKeyValue}"相关的结果` : `没有任何项目`)}}
-
-
+
-
@@ -55,7 +58,13 @@
-
+
+
+
+ {{$L(projectKeyValue ? `没有任何与"${projectKeyValue}"相关的结果` : `没有任何项目`)}}
+
+
+
import {mapState} from "vuex";
+import Draggable from 'vuedraggable'
import longpress from "../../../directives/longpress";
import TransferDom from "../../../directives/transfer-dom";
import transformEmojiToHtml from "../../../utils/emoji";
export default {
name: "ProjectList",
+ components: {Draggable},
directives: {longpress, TransferDom},
data() {
return {
@@ -99,6 +110,9 @@ export default {
operateStyles: {},
operateVisible: false,
operateItem: {},
+
+ projectDraggableList: [],
+ projectDragging: false,
}
},
@@ -108,9 +122,15 @@ export default {
projectLists() {
const {projectKeyValue, cacheProjects} = this;
const data = $A.cloneJSON(cacheProjects).sort((a, b) => {
- if (a.top_at || b.top_at) {
+ // 置顶优先
+ if (a.top_at !== b.top_at && (a.top_at || b.top_at)) {
return $A.sortDay(b.top_at, a.top_at);
}
+ // 自定义排序
+ const as = typeof a.sort === 'number' ? a.sort : Number.MAX_SAFE_INTEGER;
+ const bs = typeof b.sort === 'number' ? b.sort : Number.MAX_SAFE_INTEGER;
+ if (as !== bs) return as - bs;
+ // 兜底:按ID倒序
return b.id - a.id;
});
if (projectKeyValue) {
@@ -121,6 +141,14 @@ export default {
},
watch: {
+ projectLists: {
+ handler(val) {
+ if (!this.projectDragging) {
+ this.projectDraggableList = val
+ }
+ },
+ immediate: true
+ },
projectKeyValue(val) {
if (val == '') {
return;
@@ -141,6 +169,27 @@ export default {
methods: {
transformEmojiToHtml,
+ onProjectSortEnd() {
+ // 只对非置顶项进行排序更新
+ const nonPinnedItems = this.projectDraggableList.filter(item => !item.top_at)
+ nonPinnedItems.forEach((item, index) => {
+ this.$store.dispatch("saveProject", {id: item.id, sort: index})
+ })
+ // 提交服务端保存
+ this.$store.dispatch("call", {
+ url: 'project/user/sort',
+ data: {
+ list: nonPinnedItems.map(item => item.id)
+ },
+ method: 'post',
+ }).then(({msg}) => {
+ $A.messageSuccess(msg)
+ }).catch(({msg}) => {
+ $A.modalError(msg)
+ }).finally(() => {
+ this.projectDragging = false
+ })
+ },
searchProject() {
this.projectKeyLoading++;
this.$store.dispatch("getProjects", {