mirror of
https://github.com/kuaifan/dootask.git
synced 2025-12-11 02:12:53 +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);
|
||||
}
|
||||
//
|
||||
$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. 退出项目
|
||||
*
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -128,12 +128,22 @@
|
||||
</ul>
|
||||
</div>
|
||||
<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
|
||||
v-for="(item, key) in projectLists"
|
||||
v-for="item in projectDraggableList"
|
||||
:ref="`project_${item.id}`"
|
||||
:key="key"
|
||||
:class="classNameProject(item)"
|
||||
:key="item.id"
|
||||
:class="[classNameProject(item), item.top_at ? 'pinned' : '']"
|
||||
:data-id="item.id"
|
||||
@pointerdown="handleOperation"
|
||||
@click="toggleRoute('project', {projectId: item.id})">
|
||||
@ -157,7 +167,7 @@
|
||||
</div>
|
||||
</li>
|
||||
<li v-if="projectKeyLoading > 0" class="loading"><Loading/></li>
|
||||
</ul>
|
||||
</Draggable>
|
||||
</div>
|
||||
</Scrollbar>
|
||||
<div
|
||||
@ -393,6 +403,7 @@ import emitter from "../store/events";
|
||||
import SearchBox from "../components/SearchBox.vue";
|
||||
import transformEmojiToHtml from "../utils/emoji";
|
||||
import {languageName} from "../language";
|
||||
import Draggable from 'vuedraggable'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@ -414,7 +425,8 @@ export default {
|
||||
TeamManagement,
|
||||
ProjectArchived,
|
||||
MicroApps,
|
||||
ComplaintManagement
|
||||
ComplaintManagement,
|
||||
Draggable
|
||||
},
|
||||
directives: {longpress, TransferDom},
|
||||
data() {
|
||||
@ -450,6 +462,9 @@ export default {
|
||||
projectKeyLoading: 0,
|
||||
projectSearchShow: false,
|
||||
|
||||
projectDraggableList: [],
|
||||
projectDragging: false,
|
||||
|
||||
openMenu: {},
|
||||
visibleMenu: false,
|
||||
|
||||
@ -675,9 +690,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) {
|
||||
@ -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) {
|
||||
|
||||
@ -12,21 +12,24 @@
|
||||
</Form>
|
||||
</div>
|
||||
</div>
|
||||
<ul
|
||||
@scroll="onScroll"
|
||||
@touchstart="onTouchStart"
|
||||
v-longpress="handleLongpress">
|
||||
<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
|
||||
:list="projectDraggableList"
|
||||
:animation="150"
|
||||
:disabled="!!projectKeyValue"
|
||||
tag="ul"
|
||||
item-key="id"
|
||||
draggable="li:not(.pinned)"
|
||||
handle=".project-h1"
|
||||
@scroll.native="onScroll"
|
||||
@touchstart.native="onTouchStart"
|
||||
v-longpress="handleLongpress"
|
||||
@start="projectDragging = true"
|
||||
@end="onProjectSortEnd">
|
||||
<li
|
||||
v-for="(item, key) in projectLists"
|
||||
:key="key"
|
||||
v-for="item in projectDraggableList"
|
||||
:key="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"
|
||||
@click="toggleRoute('project', {projectId: item.id})">
|
||||
<div class="project-item">
|
||||
@ -55,7 +58,13 @@
|
||||
</div>
|
||||
</div>
|
||||
</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
|
||||
v-transfer-dom
|
||||
:data-transfer="true"
|
||||
@ -84,12 +93,14 @@
|
||||
|
||||
<script>
|
||||
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", {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user