perf: 任务操作菜单组件化

This commit is contained in:
kuaifan 2022-01-09 00:03:00 +08:00
parent 114b792300
commit eb1f5f2632
12 changed files with 394 additions and 491 deletions

View File

@ -116,8 +116,7 @@
<Icon type="md-trash" />{{$L('删除')}}
</div>
</EDropdownItem>
<EDropdownItem divided disabled>{{$L('颜色')}}</EDropdownItem>
<EDropdownItem v-for="(c, k) in $store.state.columnColorList" :key="k" :command="c">
<EDropdownItem v-for="(c, k) in $store.state.columnColorList" :key="k" :divided="k==0" :command="c">
<div class="item">
<i class="taskfont" :style="{color:c.color}" v-html="c.color == column.color ? '&#xe61d;' : '&#xe61c;'"></i>{{$L(c.name)}}
</div>
@ -154,42 +153,7 @@
<div :class="['task-head', item.desc ? 'has-desc' : '']">
<div class="task-title"><pre>{{item.name}}</pre></div>
<div class="task-menu" @click.stop="">
<div v-if="taskLoad[item.id] === true" class="loading"><Loading /></div>
<EDropdown
v-else
trigger="click"
size="small"
@command="dropTask(item, $event)">
<Icon type="ios-more" />
<EDropdownMenu slot="dropdown" class="project-list-more-dropdown-menu">
<EDropdownItem v-if="item.complete_at" command="uncomplete">
<div class="item red">
<Icon type="md-checkmark-circle-outline" />{{$L('标记未完成')}}
</div>
</EDropdownItem>
<EDropdownItem v-else command="complete">
<div class="item">
<Icon type="md-radio-button-off" />{{$L('完成')}}
</div>
</EDropdownItem>
<EDropdownItem command="archived">
<div class="item">
<Icon type="ios-filing" />{{$L('归档')}}
</div>
</EDropdownItem>
<EDropdownItem command="remove">
<div class="item">
<Icon type="md-trash" />{{$L('删除')}}
</div>
</EDropdownItem>
<EDropdownItem divided disabled>{{$L('背景色')}}</EDropdownItem>
<EDropdownItem v-for="(c, k) in $store.state.taskColorList" :key="k" :command="c">
<div class="item">
<i class="taskfont" :style="{color:c.color||'#f9f9f9'}" v-html="c.color == item.color ? '&#xe61d;' : '&#xe61c;'"></i>{{$L(c.name)}}
</div>
</EDropdownItem>
</EDropdownMenu>
</EDropdown>
<TaskMenu :task="item" icon="ios-more"/>
</div>
</div>
<div v-if="item.desc" class="task-desc"><pre v-html="item.desc"></pre></div>
@ -286,7 +250,7 @@
<Col span="3"></Col>
<Col span="3"></Col>
</Row>
<TaskRow v-if="projectParameter('showMy')" :list="transforTasks(myList)" open-key="my" @command="dropTask" @on-priority="addTaskOpen" fast-add-task/>
<TaskRow v-if="projectParameter('showMy')" :list="transforTasks(myList)" open-key="my" @on-priority="addTaskOpen" fast-add-task/>
</div>
<!--协助的任务-->
<div v-if="helpList.length" :class="['project-table-body', !projectParameter('showHelp') ? 'project-table-hide' : '']">
@ -301,7 +265,7 @@
<Col span="3"></Col>
<Col span="3"></Col>
</Row>
<TaskRow v-if="projectParameter('showHelp')" :list="helpList" open-key="help" @command="dropTask" @on-priority="addTaskOpen"/>
<TaskRow v-if="projectParameter('showHelp')" :list="helpList" open-key="help" @on-priority="addTaskOpen"/>
</div>
<!--未完成任务-->
<div v-if="projectData.task_num > 0" :class="['project-table-body', !projectParameter('showUndone') ? 'project-table-hide' : '']">
@ -316,7 +280,7 @@
<Col span="3"></Col>
<Col span="3"></Col>
</Row>
<TaskRow v-if="projectParameter('showUndone')" :list="unList" open-key="undone" @command="dropTask" @on-priority="addTaskOpen"/>
<TaskRow v-if="projectParameter('showUndone')" :list="unList" open-key="undone" @on-priority="addTaskOpen"/>
</div>
<!--已完成任务-->
<div v-if="projectData.task_num > 0" :class="['project-table-body', !projectParameter('showCompleted') ? 'project-table-hide' : '']">
@ -331,7 +295,7 @@
<Col span="3"></Col>
<Col span="3">{{projectData.task_num > 0 && projectParameter('showCompleted') ? $L('完成时间') : ''}}</Col>
</Row>
<TaskRow v-if="projectParameter('showCompleted')" :list="completedList" open-key="completed" @command="dropTask" @on-priority="addTaskOpen" showCompleteAt/>
<TaskRow v-if="projectParameter('showCompleted')" :list="completedList" open-key="completed" @on-priority="addTaskOpen" showCompleteAt/>
</div>
</div>
@ -475,10 +439,12 @@ import TaskArchived from "./TaskArchived";
import ProjectLog from "./ProjectLog";
import DrawerOverlay from "../../../components/DrawerOverlay";
import ProjectWorkflow from "./ProjectWorkflow";
import TaskMenu from "./TaskMenu";
export default {
name: "ProjectList",
components: {
TaskMenu,
ProjectWorkflow,
DrawerOverlay,
ProjectLog, TaskArchived, TaskRow, Draggable, TaskAddSimple, UserInput, TaskAdd, TaskPriority},
@ -489,9 +455,6 @@ export default {
columnLoad: {},
columnTopShow: {},
taskLoad: {},
tempShowTasks: [],
sortField: 'end_at',
sortType: 'desc',
@ -639,13 +602,13 @@ export default {
},
myList() {
const {projectId, cacheTasks, searchText, tempShowTasks, sortField, sortType} = this;
const {projectId, cacheTasks, searchText, sortField, sortType} = this;
const array = cacheTasks.filter((task) => {
if (task.project_id != projectId) {
return false;
}
if (!this.projectParameter('completedTask')) {
if (task.complete_at && !tempShowTasks.find(({id}) => id == task.id)) {
if (task.complete_at) {
return false;
}
}
@ -672,13 +635,13 @@ export default {
},
helpList() {
const {projectId, cacheTasks, searchText, tempShowTasks, userId, sortField, sortType} = this;
const {projectId, cacheTasks, searchText, userId, sortField, sortType} = this;
const array = cacheTasks.filter((task) => {
if (task.project_id != projectId || task.parent_id > 0) {
return false;
}
if (!this.projectParameter('completedTask')) {
if (task.complete_at && !tempShowTasks.find(({id}) => id == task.id)) {
if (task.complete_at) {
return false;
}
}
@ -767,9 +730,6 @@ export default {
projectData() {
this.sortData = this.getSort();
},
'$route'() {
this.tempShowTasks = [];
},
searchShow(val) {
if (val) {
this.$nextTick(() => {
@ -965,112 +925,6 @@ export default {
});
},
dropTask(task, command) {
if ($A.isJson(command)) {
if (command.name) {
//
this.updateTask(task, {
color: command.color
})
}
return;
}
if ($A.leftExists(command, 'column::')) {
//
this.updateTask(task, {
column_id: $A.leftDelete(command, 'column::')
})
return;
}
if ($A.leftExists(command, 'priority::')) {
//
let data = this.taskPriority[parseInt($A.leftDelete(command, 'priority::'))];
if (data) {
this.updateTask(task, {
p_level: data.priority,
p_name: data.name,
p_color: data.color,
})
}
return;
}
switch (command) {
case 'complete':
if (task.complete_at) return;
this.updateTask(task, {
complete_at: $A.formatDate("Y-m-d H:i:s")
}).then(() => {
this.tempShowTasks.push(task)
})
break;
case 'uncomplete':
if (!task.complete_at) return;
this.updateTask(task, {
complete_at: false
}).then(() => {
this.tempShowTasks = this.tempShowTasks.filter(({id}) => id != task.id)
})
break;
case 'archived':
case 'remove':
this.archivedOrRemoveTask(task, command);
break;
}
},
updateTask(task, updata) {
return new Promise((resolve, reject) => {
if (this.taskLoad[task.id] === true) {
reject()
return;
}
this.$set(this.taskLoad, task.id, true);
//
Object.keys(updata).forEach(key => this.$set(task, key, updata[key]));
//
this.$store.dispatch("taskUpdate", Object.assign(updata, {
task_id: task.id,
})).then(() => {
this.$set(this.taskLoad, task.id, false);
resolve()
}).catch(({msg}) => {
$A.modalError(msg);
this.$set(this.taskLoad, task.id, false);
this.$store.dispatch("getTaskOne", task.id);
reject()
});
});
},
archivedOrRemoveTask(task, type) {
let typeDispatch = type == 'remove' ? 'removeTask' : 'archivedTask';
let typeName = type == 'remove' ? '删除' : '归档';
let typeTask = task.parent_id > 0 ? '子任务' : '任务';
$A.modalConfirm({
title: typeName + typeTask,
content: '你确定要' + typeName + typeTask + '【' + task.name + '】吗?',
loading: true,
onOk: () => {
if (this.taskLoad[task.id] === true) {
this.$Modal.remove();
return;
}
this.$set(this.taskLoad, task.id, true);
this.$store.dispatch(typeDispatch, task.id).then(({msg}) => {
$A.messageSuccess(msg);
this.$Modal.remove();
this.$set(this.taskLoad, task.id, false);
}).catch(({msg}) => {
$A.modalError(msg, 301);
this.$Modal.remove();
this.$set(this.taskLoad, task.id, false);
});
}
});
},
onSort(field) {
this.sortField = field;
this.sortType = this.sortType == 'desc' ? 'asc' : 'desc';
@ -1311,7 +1165,6 @@ export default {
toggleCompleted() {
this.$store.dispatch('toggleProjectParameter', 'completedTask');
this.tempShowTasks = [];
},
expiresFormat(date) {

View File

@ -97,6 +97,13 @@ export default {
title: this.$L('完成时间'),
key: 'complete_at',
width: 168,
render: (h, {row}) => {
return h('div', {
style: {
color: row.complete_at ? '' : '#f00'
}
}, row.complete_at || this.$L('未完成'));
}
},
{
title: this.$L('归档时间'),

View File

@ -2,40 +2,7 @@
<!--子任务-->
<li v-if="ready && taskDetail.parent_id > 0">
<div class="subtask-icon">
<div v-if="taskDetail.loading === true" class="loading"><Loading /></div>
<EDropdown
v-else
trigger="click"
placement="bottom"
size="small"
@command="dropTask">
<div>
<Icon v-if="taskDetail.complete_at" class="completed" type="md-checkmark-circle" />
<Icon v-else type="md-radio-button-off" />
</div>
<EDropdownMenu slot="dropdown" class="project-list-more-dropdown-menu">
<EDropdownItem v-if="taskDetail.complete_at" command="uncomplete">
<div class="item red">
<Icon type="md-checkmark-circle-outline" />{{$L('标记未完成')}}
</div>
</EDropdownItem>
<EDropdownItem v-else command="complete">
<div class="item">
<Icon type="md-radio-button-off" />{{$L('完成')}}
</div>
</EDropdownItem>
<EDropdownItem command="times">
<div class="item">
<Icon type="md-time" />{{$L('时间')}}
</div>
</EDropdownItem>
<EDropdownItem command="remove">
<div class="item">
<Icon type="md-trash" />{{$L('删除')}}
</div>
</EDropdownItem>
</EDropdownMenu>
</EDropdown>
<TaskMenu :task="taskDetail" :load-status="taskDetail.loading === true"/>
</div>
<div class="subtask-name">
<Input
@ -58,9 +25,10 @@
@on-clear="timeClear"
@on-ok="timeOk"
transfer>
<div @click="openTime" :class="['time', taskDetail.today ? 'today' : '', taskDetail.overdue ? 'overdue' : '']">
{{taskDetail.end_at && taskDetail.end_at != mainEndAt ? expiresFormat(taskDetail.end_at) : ' '}}
<div v-if="taskDetail.end_at && taskDetail.end_at != mainEndAt" @click="openTime" :class="['time', taskDetail.today ? 'today' : '', taskDetail.overdue ? 'overdue' : '']">
{{expiresFormat(taskDetail.end_at)}}
</div>
<Icon v-else class="clock" type="ios-clock-outline" @click="openTime" />
</DatePicker>
<Poptip
ref="owner"
@ -93,8 +61,7 @@
<div v-else-if="ready" v-show="taskDetail.id > 0" :class="{'task-detail':true, 'open-dialog': hasOpenDialog, 'completed': taskDetail.complete_at}">
<div class="task-info">
<div class="head">
<Icon v-if="taskDetail.complete_at" class="icon completed" type="md-checkmark-circle" @click="updateData('uncomplete')"/>
<Icon v-else class="icon" type="md-radio-button-off" @click="updateData('complete')"/>
<TaskMenu :task="taskDetail" class="icon" size="medium" :color-show="false" quick-completed/>
<div class="nav">
<p v-if="projectName"><span>{{projectName}}</span></p>
<p v-if="columnName"><span>{{columnName}}</span></p>
@ -130,34 +97,9 @@
<ETooltip v-if="$Electron" :content="$L('新窗口打开')">
<i class="taskfont open" @click="openNewWin">&#xe776;</i>
</ETooltip>
<EDropdown
trigger="click"
placement="bottom"
@command="dropTask">
<Icon class="menu" type="ios-more"/>
<EDropdownMenu slot="dropdown">
<EDropdownItem v-if="taskDetail.complete_at" command="uncomplete">
<div class="item red">
<Icon type="md-checkmark-circle-outline" />{{$L('标记未完成')}}
<div class="menu">
<TaskMenu :task="taskDetail" icon="ios-more" completed-icon="ios-more" size="medium" :color-show="false"/>
</div>
</EDropdownItem>
<EDropdownItem v-else command="complete">
<div class="item">
<Icon type="md-radio-button-off" />{{$L('完成')}}
</div>
</EDropdownItem>
<EDropdownItem command="archived">
<div class="item">
<Icon type="ios-filing" />{{$L('归档')}}
</div>
</EDropdownItem>
<EDropdownItem command="remove">
<div class="item">
<Icon type="md-trash" />{{$L('删除')}}
</div>
</EDropdownItem>
</EDropdownMenu>
</EDropdown>
</div>
</div>
<div class="scroller overlay-y">
@ -428,10 +370,11 @@ import TaskUpload from "./TaskUpload";
import DialogWrapper from "./DialogWrapper";
import ProjectLog from "./ProjectLog";
import {Store} from "le5le-store";
import TaskMenu from "./TaskMenu";
export default {
name: "TaskDetail",
components: {ProjectLog, DialogWrapper, TaskUpload, UserInput, TaskPriority, TEditor},
components: {TaskMenu, ProjectLog, DialogWrapper, TaskUpload, UserInput, TaskPriority, TEditor},
props: {
taskId: {
type: Number,
@ -794,34 +737,8 @@ export default {
}
},
dropTask(command) {
switch (command) {
case 'complete':
this.updateData('complete')
break;
case 'uncomplete':
this.updateData('uncomplete')
break;
case 'times':
this.openTime()
break;
case 'archived':
case 'remove':
this.archivedOrRemoveTask(command);
break;
}
},
updateData(action, params) {
switch (action) {
case 'complete':
this.$set(this.taskDetail, 'complete_at', $A.formatDate());
action = 'complete_at';
break;
case 'uncomplete':
this.$set(this.taskDetail, 'complete_at', false);
action = 'complete_at';
break;
case 'priority':
this.$set(this.taskDetail, 'p_level', params.priority)
this.$set(this.taskDetail, 'p_name', params.name)

View File

@ -0,0 +1,184 @@
<template>
<EDropdown
trigger="click"
:size="size"
placement="bottom"
@command="dropTask">
<slot name="icon">
<div class="task-menu-icon">
<div v-if="loadIng" class="loading"><Loading /></div>
<template v-else>
<Icon v-if="task.complete_at" class="completed" :type="completedIcon" />
<Icon v-else :type="icon" class="uncomplete"/>
</template>
</div>
</slot>
<EDropdownMenu slot="dropdown" class="task-menu-more-dropdown">
<EDropdownItem v-if="task.complete_at" command="uncomplete">
<div class="item red">
<Icon type="md-checkmark-circle-outline" />{{$L('标记未完成')}}
</div>
</EDropdownItem>
<EDropdownItem v-else command="complete">
<div class="item">
<Icon type="md-radio-button-off" />{{$L('完成')}}
</div>
</EDropdownItem>
<EDropdownItem v-if="task.parent_id === 0" command="archived">
<div class="item">
<Icon type="ios-filing" />{{$L('归档')}}
</div>
</EDropdownItem>
<EDropdownItem command="remove">
<div class="item">
<Icon type="md-trash" />{{$L('删除')}}
</div>
</EDropdownItem>
<template v-if="task.parent_id === 0 && colorShow">
<EDropdownItem v-for="(c, k) in $store.state.taskColorList" :key="k" :divided="k==0" :command="c">
<div class="item">
<i class="taskfont" :style="{color:c.color||'#f9f9f9'}" v-html="c.color == task.color ? '&#xe61d;' : '&#xe61c;'"></i>{{$L(c.name)}}
</div>
</EDropdownItem>
</template>
</EDropdownMenu>
</EDropdown>
</template>
<script>
import {mapState} from "vuex";
export default {
name: "TaskMenu",
props: {
task: {
type: Object,
default: () => {
return {};
}
},
loadStatus: {
type: Boolean,
default: false
},
colorShow: {
type: Boolean,
default: true
},
quickCompleted: { //
type: Boolean,
default: false
},
size: {
type: String,
default: 'small'
},
icon: {
type: String,
default: 'md-radio-button-off'
},
completedIcon: {
type: String,
default: 'md-checkmark-circle'
},
},
data() {
return {
}
},
computed: {
...mapState(['taskLoading']),
loadIng() {
if (this.loadStatus) {
return true;
}
const load = this.taskLoading.find(({id}) => id == this.task.id);
return load && load.num > 0
}
},
methods: {
dropTask(command) {
if ($A.isJson(command)) {
if (command.name) {
//
this.updateTask({
color: command.color
})
}
return;
}
switch (command) {
case 'complete':
if (this.task.complete_at) return;
this.updateTask({
complete_at: $A.formatDate("Y-m-d H:i:s")
}).then(() => {
//
})
break;
case 'uncomplete':
if (!this.task.complete_at) return;
this.updateTask({
complete_at: false
}).then(() => {
//
})
break;
case 'archived':
case 'remove':
this.archivedOrRemoveTask(command);
break;
}
},
updateTask(updata) {
return new Promise((resolve, reject) => {
if (this.loadIng) {
reject()
return;
}
//
Object.keys(updata).forEach(key => this.$set(this.task, key, updata[key]));
//
this.$store.dispatch("taskUpdate", Object.assign(updata, {
task_id: this.task.id,
})).then(() => {
resolve()
}).catch(({msg}) => {
$A.modalError(msg);
this.$store.dispatch("getTaskOne", this.task.id);
reject()
});
});
},
archivedOrRemoveTask(type) {
let typeDispatch = type == 'remove' ? 'removeTask' : 'archivedTask';
let typeName = type == 'remove' ? '删除' : '归档';
let typeTask = this.task.parent_id > 0 ? '子任务' : '任务';
$A.modalConfirm({
title: typeName + typeTask,
content: '你确定要' + typeName + typeTask + '【' + this.task.name + '】吗?',
loading: true,
onOk: () => {
if (this.loadIng) {
this.$Modal.remove();
return;
}
this.$store.dispatch(typeDispatch, this.task.id).then(({msg}) => {
$A.messageSuccess(msg);
this.$Modal.remove();
}).catch(({msg}) => {
$A.modalError(msg, 301);
this.$Modal.remove();
});
}
});
},
},
}
</script>

View File

@ -9,47 +9,7 @@
:class="['sub-icon', taskOpen[item.id] ? 'active' : '']"
type="ios-arrow-forward"
@click="getSublist(item)"/>
<EDropdown
trigger="click"
size="small"
placement="bottom"
@command="dropTask(item, $event)">
<div class="drop-icon">
<Icon v-if="item.complete_at" class="completed" type="md-checkmark-circle" />
<Icon v-else type="md-radio-button-off" />
<div v-if="taskLoad[item.id] === true" class="loading"><Loading /></div>
</div>
<EDropdownMenu slot="dropdown" class="project-list-more-dropdown-menu">
<EDropdownItem v-if="item.complete_at" command="uncomplete">
<div class="item red">
<Icon type="md-checkmark-circle-outline" />{{$L('标记未完成')}}
</div>
</EDropdownItem>
<EDropdownItem v-else command="complete">
<div class="item">
<Icon type="md-radio-button-off" />{{$L('完成')}}
</div>
</EDropdownItem>
<EDropdownItem v-if="item.parent_id === 0" command="archived">
<div class="item">
<Icon type="ios-filing" />{{$L('归档')}}
</div>
</EDropdownItem>
<EDropdownItem command="remove">
<div class="item">
<Icon type="md-trash" />{{$L('删除')}}
</div>
</EDropdownItem>
<template v-if="item.parent_id === 0">
<EDropdownItem divided disabled>{{$L('背景色')}}</EDropdownItem>
<EDropdownItem v-for="(c, k) in $store.state.taskColorList" :key="k" :command="c">
<div class="item">
<i class="taskfont" :style="{color:c.color||'#f9f9f9'}" v-html="c.color == item.color ? '&#xe61d;' : '&#xe61c;'"></i>{{$L(c.name)}}
</div>
</EDropdownItem>
</template>
</EDropdownMenu>
</EDropdown>
<TaskMenu :ref="`taskMenu_${item.id}`" :task="item"/>
<div class="item-title" @click="openTask(item)">
<span v-if="item.sub_top === true">{{$L('子任务')}}</span>
<span v-if="item.sub_my && item.sub_my.length > 0">+{{item.sub_my.length}}</span>
@ -145,10 +105,11 @@ import TaskPriority from "./TaskPriority";
import TaskAddSimple from "./TaskAddSimple";
import {mapState} from "vuex";
import {Store} from "le5le-store";
import TaskMenu from "./TaskMenu";
export default {
name: "TaskRow",
components: {TaskAddSimple, TaskPriority},
components: {TaskMenu, TaskAddSimple, TaskPriority},
props: {
list: {
type: Array,
@ -211,8 +172,31 @@ export default {
return column ? column.name : '';
},
dropTask(task, command) {
this.$emit("command", task, command)
const el = this.$refs[`taskMenu_${task.id}`];
if (!el) {
return;
}
//
if ($A.leftExists(command, 'column::')) {
//
el[0].updateTask({
column_id: $A.leftDelete(command, 'column::')
})
return;
}
if ($A.leftExists(command, 'priority::')) {
//
let data = this.taskPriority[parseInt($A.leftDelete(command, 'priority::'))];
if (data) {
el[0].updateTask({
p_level: data.priority,
p_name: data.name,
p_color: data.color,
})
}
}
},
onPriority(data) {

View File

@ -40,45 +40,11 @@
v-if="item.p_name"
class="priority-color"
:style="{backgroundColor:item.p_color}"></em>
<EDropdown
trigger="click"
size="small"
placement="bottom"
@command="dropTask(item, $event)">
<div class="drop-icon" @click.stop="">
<TaskMenu :task="item">
<div slot="icon" class="drop-icon" @click.stop="">
<i class="taskfont" v-html="item.complete_at ? '&#xe627;' : '&#xe625;'"></i>
</div>
<EDropdownMenu slot="dropdown" class="project-list-more-dropdown-menu">
<EDropdownItem v-if="item.complete_at" command="uncomplete">
<div class="item red">
<Icon type="md-checkmark-circle-outline" />{{$L('标记未完成')}}
</div>
</EDropdownItem>
<EDropdownItem v-else command="complete">
<div class="item">
<Icon type="md-radio-button-off" />{{$L('完成')}}
</div>
</EDropdownItem>
<EDropdownItem v-if="item.parent_id === 0" command="archived">
<div class="item">
<Icon type="ios-filing" />{{$L('归档')}}
</div>
</EDropdownItem>
<EDropdownItem command="remove">
<div class="item">
<Icon type="md-trash" />{{$L('删除')}}
</div>
</EDropdownItem>
<template v-if="item.parent_id === 0">
<EDropdownItem divided disabled>{{$L('背景色')}}</EDropdownItem>
<EDropdownItem v-for="(c, k) in $store.state.taskColorList" :key="k" :command="c">
<div class="item">
<i class="taskfont" :style="{color:c.color||'#f9f9f9'}" v-html="c.color == item.color ? '&#xe61d;' : '&#xe61c;'"></i>{{$L(c.name)}}
</div>
</EDropdownItem>
</template>
</EDropdownMenu>
</EDropdown>
</TaskMenu>
<div class="item-title">
<span v-if="item.sub_top === true">{{$L('子任务')}}</span>
<span v-if="item.sub_my && item.sub_my.length > 0">+{{item.sub_my.length}}</span>
@ -107,9 +73,10 @@
import {mapGetters, mapState} from "vuex";
import AppDown from "../../components/AppDown";
import {Store} from "le5le-store";
import TaskMenu from "./components/TaskMenu";
export default {
components: {AppDown},
components: {TaskMenu, AppDown},
data() {
return {
nowTime: $A.Time(),
@ -117,10 +84,6 @@ export default {
loadIng: 0,
dashboard: 'today',
taskLoad: {},
tempShowTasks: [],
}
},
@ -156,7 +119,7 @@ export default {
},
list() {
const {dashboard, tempShowTasks} = this;
const {dashboard} = this;
let data = [];
switch (dashboard) {
case 'today':
@ -166,112 +129,17 @@ export default {
data = this.transforTasks(this.dashboardTask.overdue);
break
}
if (tempShowTasks.length > 0) {
data.push(...tempShowTasks);
}
return data.sort((a, b) => {
return $A.Date(a.end_at) - $A.Date(b.end_at);
});
},
},
watch: {
'$route'() {
this.tempShowTasks = [];
},
dashboard() {
this.tempShowTasks = [];
}
},
methods: {
dropTask(task, command) {
switch (command) {
case 'complete':
if (task.complete_at) return;
this.updateTask(task, {
complete_at: $A.formatDate("Y-m-d H:i:s")
}).then(() => {
this.tempShowTasks.push(task)
})
break;
case 'uncomplete':
if (!task.complete_at) return;
this.updateTask(task, {
complete_at: false
}).then(() => {
this.tempShowTasks = this.tempShowTasks.filter(({id}) => id != task.id)
})
break;
case 'archived':
case 'remove':
this.archivedOrRemoveTask(task, command);
break;
default:
if (command.name) {
this.updateTask(task, {
color: command.color
})
}
break;
}
},
openTask(task) {
this.$store.dispatch("openTask", task)
},
updateTask(task, updata) {
return new Promise((resolve, reject) => {
if (this.taskLoad[task.id] === true) {
reject()
return;
}
this.$set(this.taskLoad, task.id, true);
//
Object.keys(updata).forEach(key => this.$set(task, key, updata[key]));
//
this.$store.dispatch("taskUpdate", Object.assign(updata, {
task_id: task.id,
})).then(() => {
this.$set(this.taskLoad, task.id, false);
resolve()
}).catch(({msg}) => {
$A.modalError(msg);
this.$set(this.taskLoad, task.id, false);
this.$store.dispatch("getTaskOne", task.id);
reject();
});
})
},
archivedOrRemoveTask(task, type) {
let typeDispatch = type == 'remove' ? 'removeTask' : 'archivedTask';
let typeName = type == 'remove' ? '删除' : '归档';
let typeTask = task.parent_id > 0 ? '子任务' : '任务';
$A.modalConfirm({
title: typeName + typeTask,
content: '你确定要' + typeName + typeTask + '【' + task.name + '】吗?',
loading: true,
onOk: () => {
if (this.taskLoad[task.id] === true) {
this.$Modal.remove();
return;
}
this.$set(this.taskLoad, task.id, true);
this.$store.dispatch(typeDispatch, task.id).then(({msg}) => {
$A.messageSuccess(msg);
this.$Modal.remove();
this.$set(this.taskLoad, task.id, false);
}).catch(({msg}) => {
$A.modalError(msg, 301);
this.$Modal.remove();
this.$set(this.taskLoad, task.id, false);
});
}
});
},
expiresFormat(date) {
return $A.countDownFormat(date, this.nowTime)
},

View File

@ -1135,6 +1135,7 @@ export default {
reject({msg: 'Parameter error'});
return;
}
dispatch("taskLoadAdd", task_id)
dispatch("call", {
url: 'project/task/remove',
data: {
@ -1142,10 +1143,12 @@ export default {
},
}).then(result => {
dispatch("forgetTask", task_id)
dispatch("taskLoadSub", task_id)
resolve(result)
}).catch(e => {
console.error(e);
dispatch("getTaskOne", task_id);
dispatch("taskLoadSub", task_id)
reject(e)
});
});
@ -1164,6 +1167,7 @@ export default {
reject({msg: 'Parameter error'});
return;
}
dispatch("taskLoadAdd", task_id)
dispatch("call", {
url: 'project/task/archived',
data: {
@ -1171,10 +1175,12 @@ export default {
},
}).then(result => {
dispatch("forgetTask", task_id)
dispatch("taskLoadSub", task_id)
resolve(result)
}).catch(e => {
console.error(e);
dispatch("getTaskOne", task_id);
dispatch("getTaskOne", task_id)
dispatch("taskLoadSub", task_id)
reject(e)
});
});
@ -1365,28 +1371,67 @@ export default {
* 更新任务
* @param state
* @param dispatch
* @param data
* @param data {task_id, ?}
* @returns {Promise<unknown>}
*/
taskUpdate({state, dispatch}, data) {
return new Promise(function (resolve, reject) {
const post = $A.cloneJSON($A.date2string(data));
//
dispatch("taskLoadAdd", post.task_id)
dispatch("call", {
url: 'project/task/update',
data: post,
method: 'post',
}).then(result => {
dispatch("taskLoadSub", post.task_id)
dispatch("saveTask", result.data)
resolve(result)
}).catch(e => {
console.error(e);
dispatch("taskLoadSub", post.task_id)
dispatch("getTaskOne", post.task_id);
reject(e)
});
});
},
/**
* 任务增加等待
* @param state
* @param task_id
*/
taskLoadAdd({state}, task_id) {
setTimeout(() => {
const load = state.taskLoading.find(({id}) => id == task_id)
if (!load) {
state.taskLoading.push({
id: task_id,
num: 1
})
} else {
load.num++;
}
}, 300)
},
/**
* 任务减少等待
* @param state
* @param task_id
*/
taskLoadSub({state}, task_id) {
const load = state.taskLoading.find(({id}) => id == task_id)
if (!load) {
state.taskLoading.push({
id: task_id,
num: -1
})
} else {
load.num--;
}
},
/**
* 获取任务优先级预设数据
* @param state

View File

@ -67,6 +67,7 @@ state.taskId = 0;
state.taskContents = [];
state.taskFiles = [];
state.taskLogs = [];
state.taskLoading = [];
// 任务优先级
state.taskPriority = [];

View File

@ -10,5 +10,6 @@
@import "task-add-simple";
@import "task-archived";
@import "task-detail";
@import "task-menu";
@import "task-priority";
@import "team-management";

View File

@ -380,13 +380,14 @@
}
.ivu-icon {
font-size: 22px;
&.uncomplete {
color: #777777;
cursor: pointer;
&:hover {
color: #555555;
}
}
}
}
&.has-desc {
.task-title {
font-weight: 600;
@ -651,31 +652,12 @@
align-items: flex-start;
padding: 12px 12px 12px 34px;
line-height: 24px;
.drop-icon {
position: relative;
}
.loading {
position: absolute;
top: 0;
left: 0;
bottom: 0;
width: 16px;
display: flex;
align-items: center;
justify-content: center;
.common-loading {
margin: 0;
width: 14px;
height: 14px;
}
height: 24px;
}
.ivu-icon {
cursor: pointer;
font-size: 16px;
color: #cccccc;
&.completed {
color: $primary-color;
}
&.sub-icon {
font-size: 16px;
width: 16px;
@ -689,6 +671,9 @@
transform: rotate(90deg);
}
}
&.uncomplete {
color: #cccccc;
}
}
.item-title {
flex: 1;

View File

@ -26,11 +26,23 @@
background-color: #f4f5f5;
}
.icon {
width: 18px;
.task-menu-icon {
display: flex;
align-items: center;
.ivu-icon {
font-size: 18px;
cursor: pointer;
&.completed {
color: $primary-color;
}
.loading {
width: 18px;
height: 18px;
.common-loading {
width: 16px;
height: 16px;
}
}
.uncomplete {
color: #888888;
}
}
}
.nav {
@ -78,8 +90,12 @@
}
.menu {
margin-left: 12px;
display: flex;
align-items: center;
.ivu-icon {
font-size: 22px;
cursor: pointer;
color: #606266;
}
}
}
}
@ -243,37 +259,22 @@
&:last-child {
margin-bottom: -6px;
}
&:hover {
.subtask-time {
.clock {
transform: translateX(0);
opacity: 0.8;
}
}
}
.subtask-icon {
width: 16px;
height: 26px;
line-height: 26px;
margin-right: 6px;
display: flex;
align-items: center;
cursor: pointer;
.loading {
width: 16px;
height: 16px;
margin: 0;
padding: 2px;
}
.ivu-icon {
font-size: 16px;
color: #cccccc;
&.completed {
color: $primary-color;
}
}
&.sub-icon {
font-size: 16px;
width: 16px;
height: 16px;
margin-left: -20px;
margin-right: 4px;
color: #cfcfcf;
transition: transform 0.2s;
&.active {
transform: rotate(90deg);
}
}
}
.subtask-name {
flex: 1;
@ -309,6 +310,13 @@
color: #ff9900;
}
}
.clock {
margin: 3px 2px;
font-size: 20px;
transition: all 0.2s;
transform: translateX(50%);
opacity: 0;
}
}
.subtask-avatar {
height: 20px;

View File

@ -0,0 +1,50 @@
.task-menu-icon {
position: relative;
.loading {
width: 16px;
height: 16px;
display: flex;
align-items: center;
justify-content: center;
.common-loading {
margin: 0;
width: 14px;
height: 14px;
}
}
.ivu-icon {
cursor: pointer;
font-size: 16px;
color: #cccccc;
&.completed {
color: $primary-color;
}
}
}
.task-menu-more-dropdown {
> li {
.item {
display: flex;
align-items: center;
> i {
width: 18px;
height: 18px;
line-height: 18px;
font-size: 18px;
margin-right: 8px;
padding: 0;
color: #bbbbbb;
&.ivu-icon {
font-size: 16px;
}
}
}
}
}