mirror of
https://github.com/kuaifan/dootask.git
synced 2025-12-12 19:35:50 +00:00
perf: 支持项目调整排序
This commit is contained in:
parent
788cae3efe
commit
02275bb417
@ -169,7 +169,11 @@ class ProjectController extends AbstractController
|
|||||||
$builder->where('projects.updated_at', '>', $timerange->updated);
|
$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) {
|
$list->transform(function (Project $project) use ($getstatistics, $getuserid, $user) {
|
||||||
$array = $project->toArray();
|
$array = $project->toArray();
|
||||||
if ($getuserid == 'yes') {
|
if ($getuserid == 'yes') {
|
||||||
@ -643,6 +647,39 @@ class ProjectController extends AbstractController
|
|||||||
return Base::retSuccess('调整成功');
|
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. 退出项目
|
* @api {get} api/project/exit 11. 退出项目
|
||||||
*
|
*
|
||||||
|
|||||||
@ -129,6 +129,7 @@ class Project extends AbstractModel
|
|||||||
'projects.*',
|
'projects.*',
|
||||||
'project_users.owner',
|
'project_users.owner',
|
||||||
'project_users.top_at',
|
'project_users.top_at',
|
||||||
|
'project_users.sort',
|
||||||
])
|
])
|
||||||
->leftJoin('project_users', function ($leftJoin) use ($userid) {
|
->leftJoin('project_users', function ($leftJoin) use ($userid) {
|
||||||
$leftJoin
|
$leftJoin
|
||||||
@ -153,6 +154,7 @@ class Project extends AbstractModel
|
|||||||
'projects.*',
|
'projects.*',
|
||||||
'project_users.owner',
|
'project_users.owner',
|
||||||
'project_users.top_at',
|
'project_users.top_at',
|
||||||
|
'project_users.sort',
|
||||||
])
|
])
|
||||||
->join('project_users', 'projects.id', '=', 'project_users.project_id')
|
->join('project_users', 'projects.id', '=', 'project_users.project_id')
|
||||||
->where('project_users.userid', $userid);
|
->where('project_users.userid', $userid);
|
||||||
|
|||||||
@ -128,12 +128,22 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div ref="menuProject" class="menu-project">
|
<div ref="menuProject" class="menu-project">
|
||||||
<ul v-longpress="handleLongpress">
|
<Draggable
|
||||||
|
:list="projectDraggableList"
|
||||||
|
:animation="150"
|
||||||
|
:disabled="$isEEUIApp || windowTouch || !!projectKeyValue"
|
||||||
|
tag="ul"
|
||||||
|
item-key="id"
|
||||||
|
draggable="li:not(.pinned)"
|
||||||
|
handle=".project-h1"
|
||||||
|
v-longpress="handleLongpress"
|
||||||
|
@start="projectDragging = true"
|
||||||
|
@end="onProjectSortEnd">
|
||||||
<li
|
<li
|
||||||
v-for="(item, key) in projectLists"
|
v-for="item in projectDraggableList"
|
||||||
:ref="`project_${item.id}`"
|
:ref="`project_${item.id}`"
|
||||||
:key="key"
|
:key="item.id"
|
||||||
:class="classNameProject(item)"
|
:class="[classNameProject(item), item.top_at ? 'pinned' : '']"
|
||||||
:data-id="item.id"
|
:data-id="item.id"
|
||||||
@pointerdown="handleOperation"
|
@pointerdown="handleOperation"
|
||||||
@click="toggleRoute('project', {projectId: item.id})">
|
@click="toggleRoute('project', {projectId: item.id})">
|
||||||
@ -157,7 +167,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<li v-if="projectKeyLoading > 0" class="loading"><Loading/></li>
|
<li v-if="projectKeyLoading > 0" class="loading"><Loading/></li>
|
||||||
</ul>
|
</Draggable>
|
||||||
</div>
|
</div>
|
||||||
</Scrollbar>
|
</Scrollbar>
|
||||||
<div
|
<div
|
||||||
@ -393,6 +403,7 @@ import emitter from "../store/events";
|
|||||||
import SearchBox from "../components/SearchBox.vue";
|
import SearchBox from "../components/SearchBox.vue";
|
||||||
import transformEmojiToHtml from "../utils/emoji";
|
import transformEmojiToHtml from "../utils/emoji";
|
||||||
import {languageName} from "../language";
|
import {languageName} from "../language";
|
||||||
|
import Draggable from 'vuedraggable'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
@ -414,7 +425,8 @@ export default {
|
|||||||
TeamManagement,
|
TeamManagement,
|
||||||
ProjectArchived,
|
ProjectArchived,
|
||||||
MicroApps,
|
MicroApps,
|
||||||
ComplaintManagement
|
ComplaintManagement,
|
||||||
|
Draggable
|
||||||
},
|
},
|
||||||
directives: {longpress, TransferDom},
|
directives: {longpress, TransferDom},
|
||||||
data() {
|
data() {
|
||||||
@ -450,6 +462,9 @@ export default {
|
|||||||
projectKeyLoading: 0,
|
projectKeyLoading: 0,
|
||||||
projectSearchShow: false,
|
projectSearchShow: false,
|
||||||
|
|
||||||
|
projectDraggableList: [],
|
||||||
|
projectDragging: false,
|
||||||
|
|
||||||
openMenu: {},
|
openMenu: {},
|
||||||
visibleMenu: false,
|
visibleMenu: false,
|
||||||
|
|
||||||
@ -675,9 +690,15 @@ export default {
|
|||||||
projectLists() {
|
projectLists() {
|
||||||
const {projectKeyValue, cacheProjects} = this;
|
const {projectKeyValue, cacheProjects} = this;
|
||||||
const data = $A.cloneJSON(cacheProjects).sort((a, b) => {
|
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);
|
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;
|
return b.id - a.id;
|
||||||
});
|
});
|
||||||
if (projectKeyValue) {
|
if (projectKeyValue) {
|
||||||
@ -761,6 +782,15 @@ export default {
|
|||||||
immediate: true
|
immediate: true
|
||||||
},
|
},
|
||||||
|
|
||||||
|
projectLists: {
|
||||||
|
handler(val) {
|
||||||
|
if (!this.projectDragging) {
|
||||||
|
this.projectDraggableList = val
|
||||||
|
}
|
||||||
|
},
|
||||||
|
immediate: true
|
||||||
|
},
|
||||||
|
|
||||||
unreadAndOverdue: {
|
unreadAndOverdue: {
|
||||||
handler(val) {
|
handler(val) {
|
||||||
if (this.$Electron) {
|
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) {
|
onAddTask(params) {
|
||||||
this.addTaskShow = true
|
this.addTaskShow = true
|
||||||
this.$nextTick(_ => {
|
this.$nextTick(_ => {
|
||||||
@ -1194,6 +1246,7 @@ export default {
|
|||||||
},
|
},
|
||||||
}).then(({data}) => {
|
}).then(({data}) => {
|
||||||
this.$store.dispatch("saveProject", data);
|
this.$store.dispatch("saveProject", data);
|
||||||
|
this.projectDraggableList = this.projectLists
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
const active = this.$refs.menuProject.querySelector(".active")
|
const active = this.$refs.menuProject.querySelector(".active")
|
||||||
if (active) {
|
if (active) {
|
||||||
|
|||||||
@ -12,21 +12,24 @@
|
|||||||
</Form>
|
</Form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ul
|
<Draggable
|
||||||
@scroll="onScroll"
|
:list="projectDraggableList"
|
||||||
@touchstart="onTouchStart"
|
:animation="150"
|
||||||
v-longpress="handleLongpress">
|
:disabled="!!projectKeyValue"
|
||||||
<template v-if="projectLists.length === 0">
|
tag="ul"
|
||||||
<li v-if="projectKeyLoading > 0" class="loading"><Loading/></li>
|
item-key="id"
|
||||||
<li v-else class="nothing">
|
draggable="li:not(.pinned)"
|
||||||
{{$L(projectKeyValue ? `没有任何与"${projectKeyValue}"相关的结果` : `没有任何项目`)}}
|
handle=".project-h1"
|
||||||
</li>
|
@scroll.native="onScroll"
|
||||||
</template>
|
@touchstart.native="onTouchStart"
|
||||||
|
v-longpress="handleLongpress"
|
||||||
|
@start="projectDragging = true"
|
||||||
|
@end="onProjectSortEnd">
|
||||||
<li
|
<li
|
||||||
v-for="(item, key) in projectLists"
|
v-for="item in projectDraggableList"
|
||||||
:key="key"
|
:key="item.id"
|
||||||
:data-id="item.id"
|
:data-id="item.id"
|
||||||
:class="{operate: item.id == operateItem.id && operateVisible}"
|
:class="[{operate: item.id == operateItem.id && operateVisible}, item.top_at ? 'pinned' : '']"
|
||||||
@pointerdown="handleOperation"
|
@pointerdown="handleOperation"
|
||||||
@click="toggleRoute('project', {projectId: item.id})">
|
@click="toggleRoute('project', {projectId: item.id})">
|
||||||
<div class="project-item">
|
<div class="project-item">
|
||||||
@ -55,7 +58,13 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
<template v-if="projectLists.length === 0">
|
||||||
|
<li v-if="projectKeyLoading > 0" class="loading"><Loading/></li>
|
||||||
|
<li v-else class="nothing">
|
||||||
|
{{$L(projectKeyValue ? `没有任何与"${projectKeyValue}"相关的结果` : `没有任何项目`)}}
|
||||||
|
</li>
|
||||||
|
</template>
|
||||||
|
</Draggable>
|
||||||
<div
|
<div
|
||||||
v-transfer-dom
|
v-transfer-dom
|
||||||
:data-transfer="true"
|
:data-transfer="true"
|
||||||
@ -84,12 +93,14 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import {mapState} from "vuex";
|
import {mapState} from "vuex";
|
||||||
|
import Draggable from 'vuedraggable'
|
||||||
import longpress from "../../../directives/longpress";
|
import longpress from "../../../directives/longpress";
|
||||||
import TransferDom from "../../../directives/transfer-dom";
|
import TransferDom from "../../../directives/transfer-dom";
|
||||||
import transformEmojiToHtml from "../../../utils/emoji";
|
import transformEmojiToHtml from "../../../utils/emoji";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "ProjectList",
|
name: "ProjectList",
|
||||||
|
components: {Draggable},
|
||||||
directives: {longpress, TransferDom},
|
directives: {longpress, TransferDom},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
@ -99,6 +110,9 @@ export default {
|
|||||||
operateStyles: {},
|
operateStyles: {},
|
||||||
operateVisible: false,
|
operateVisible: false,
|
||||||
operateItem: {},
|
operateItem: {},
|
||||||
|
|
||||||
|
projectDraggableList: [],
|
||||||
|
projectDragging: false,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -108,9 +122,15 @@ export default {
|
|||||||
projectLists() {
|
projectLists() {
|
||||||
const {projectKeyValue, cacheProjects} = this;
|
const {projectKeyValue, cacheProjects} = this;
|
||||||
const data = $A.cloneJSON(cacheProjects).sort((a, b) => {
|
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);
|
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;
|
return b.id - a.id;
|
||||||
});
|
});
|
||||||
if (projectKeyValue) {
|
if (projectKeyValue) {
|
||||||
@ -121,6 +141,14 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
watch: {
|
watch: {
|
||||||
|
projectLists: {
|
||||||
|
handler(val) {
|
||||||
|
if (!this.projectDragging) {
|
||||||
|
this.projectDraggableList = val
|
||||||
|
}
|
||||||
|
},
|
||||||
|
immediate: true
|
||||||
|
},
|
||||||
projectKeyValue(val) {
|
projectKeyValue(val) {
|
||||||
if (val == '') {
|
if (val == '') {
|
||||||
return;
|
return;
|
||||||
@ -141,6 +169,27 @@ export default {
|
|||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
transformEmojiToHtml,
|
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() {
|
searchProject() {
|
||||||
this.projectKeyLoading++;
|
this.projectKeyLoading++;
|
||||||
this.$store.dispatch("getProjects", {
|
this.$store.dispatch("getProjects", {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user