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 @@
    - + +
    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", {