mirror of
https://github.com/kuaifan/dootask.git
synced 2026-02-28 04:40:37 +00:00
perf: 优化全局任务操作菜单
This commit is contained in:
parent
242932735a
commit
c9de0c2eba
@ -310,6 +310,9 @@
|
||||
</div>
|
||||
</Modal>
|
||||
|
||||
<!--任务操作-->
|
||||
<TaskOperation/>
|
||||
|
||||
<!--任务详情-->
|
||||
<TaskModal ref="taskModal"/>
|
||||
|
||||
@ -379,9 +382,11 @@ import TaskModal from "./manage/components/TaskModal";
|
||||
import notificationKoro from "notification-koro1";
|
||||
import {Store} from "le5le-store";
|
||||
import PageLoading from "../components/PageLoading";
|
||||
import TaskOperation from "./manage/components/TaskOperation";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
TaskOperation,
|
||||
PageLoading,
|
||||
TaskModal,
|
||||
DialogModal,
|
||||
|
||||
@ -166,7 +166,7 @@
|
||||
<div :class="['task-head', item.desc ? 'has-desc' : '']">
|
||||
<div class="task-title">
|
||||
<!--工作流状态-->
|
||||
<span v-if="item.flow_item_name" :class="item.flow_item_status" @click.stop="openMenu(item)">{{item.flow_item_name}}</span>
|
||||
<span v-if="item.flow_item_name" :class="item.flow_item_status" @click.stop="openMenu($event, item)">{{item.flow_item_name}}</span>
|
||||
<!--任务描述-->
|
||||
<pre>{{item.name}}</pre>
|
||||
</div>
|
||||
@ -1245,10 +1245,10 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
openMenu(task) {
|
||||
openMenu(event, task) {
|
||||
const el = this.$refs[`taskMenu_${task.id}`];
|
||||
if (el) {
|
||||
el[0].handleClick()
|
||||
el[0].handleClick(event)
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
@on-update="getLogLists"/>
|
||||
</div>
|
||||
<div v-if="taskDetail.flow_item_name" class="subtask-flow">
|
||||
<span :class="taskDetail.flow_item_status" @click.stop="openMenu(taskDetail)">{{taskDetail.flow_item_name}}</span>
|
||||
<span :class="taskDetail.flow_item_status" @click.stop="openMenu($event, taskDetail)">{{taskDetail.flow_item_name}}</span>
|
||||
</div>
|
||||
<div class="subtask-name">
|
||||
<Input
|
||||
@ -83,10 +83,10 @@
|
||||
:color-show="false"
|
||||
@on-update="getLogLists"/>
|
||||
<div v-if="taskDetail.flow_item_name" class="flow">
|
||||
<span :class="taskDetail.flow_item_status" @click.stop="openMenu(taskDetail)">{{taskDetail.flow_item_name}}</span>
|
||||
<span :class="taskDetail.flow_item_status" @click.stop="openMenu($event, taskDetail)">{{taskDetail.flow_item_name}}</span>
|
||||
</div>
|
||||
<div v-if="taskDetail.archived_at" class="flow">
|
||||
<span class="archived" @click.stop="openMenu(taskDetail)">{{$L('已归档')}}</span>
|
||||
<span class="archived" @click.stop="openMenu($event, taskDetail)">{{$L('已归档')}}</span>
|
||||
</div>
|
||||
<div class="nav">
|
||||
<p v-if="projectName"><span>{{projectName}}</span></p>
|
||||
@ -1203,9 +1203,9 @@ export default {
|
||||
});
|
||||
},
|
||||
|
||||
openMenu(task) {
|
||||
openMenu(event, task) {
|
||||
const el = this.$refs[`taskMenu_${task.id}`];
|
||||
el && el.handleClick()
|
||||
el && el.handleClick(event)
|
||||
},
|
||||
|
||||
openNewWin() {
|
||||
|
||||
@ -1,79 +1,11 @@
|
||||
<template>
|
||||
<EDropdown
|
||||
ref="dropdown"
|
||||
trigger="click"
|
||||
:disabled="disabled"
|
||||
:size="size"
|
||||
placement="bottom"
|
||||
@command="dropTask"
|
||||
@visible-change="visibleChange">
|
||||
<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 ref="dropdownMenu" slot="dropdown" class="task-menu-more-dropdown">
|
||||
<li class="task-menu-more-warp" :class="size">
|
||||
<ul>
|
||||
<EDropdownItem v-if="!flow" class="load-flow" disabled>
|
||||
<div class="load-flow-warp">
|
||||
<Loading/>
|
||||
</div>
|
||||
</EDropdownItem>
|
||||
<template v-else-if="turns.length > 0">
|
||||
<EDropdownItem v-for="item in turns" :key="item.id" :command="`turn::${item.id}`">
|
||||
<div class="item flow">
|
||||
<Icon v-if="item.id == task.flow_item_id && flow.auto_assign !== true" class="check" type="md-checkmark-circle-outline" />
|
||||
<Icon v-else type="md-radio-button-off" />
|
||||
<div class="flow-name" :class="item.status">{{item.name}}</div>
|
||||
</div>
|
||||
</EDropdownItem>
|
||||
</template>
|
||||
<template v-else>
|
||||
<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>
|
||||
</template>
|
||||
|
||||
<template v-if="task.parent_id === 0">
|
||||
<EDropdownItem :divided="turns.length > 0" command="archived">
|
||||
<div class="item">
|
||||
<Icon type="ios-filing" />{{$L(task.archived_at ? '还原归档' : '归档')}}
|
||||
</div>
|
||||
</EDropdownItem>
|
||||
<EDropdownItem command="remove">
|
||||
<div class="item hover-del">
|
||||
<Icon type="md-trash" />{{$L('删除')}}
|
||||
</div>
|
||||
</EDropdownItem>
|
||||
<template v-if="colorShow">
|
||||
<EDropdownItem v-for="(c, k) in taskColorList" :key="'c_' + k" :divided="k==0" :command="c">
|
||||
<div class="item">
|
||||
<i class="taskfont" :style="{color:c.color||'#f9f9f9'}" v-html="c.color == task.color ? '' : ''"></i>{{$L(c.name)}}
|
||||
</div>
|
||||
</EDropdownItem>
|
||||
</template>
|
||||
</template>
|
||||
<EDropdownItem v-else command="remove" :divided="turns.length > 0">
|
||||
<div class="item">
|
||||
<Icon type="md-trash" />{{$L('删除')}}
|
||||
</div>
|
||||
</EDropdownItem>
|
||||
</ul>
|
||||
</li>
|
||||
</EDropdownMenu>
|
||||
</EDropdown>
|
||||
<div class="task-menu-icon" @click="handleClick">
|
||||
<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>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@ -117,13 +49,8 @@ export default {
|
||||
default: 'md-checkmark-circle'
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState(['taskColorList', 'taskLoading', 'taskFlows', 'taskFlowItems']),
|
||||
...mapState(['taskLoading', 'taskFlows']),
|
||||
|
||||
loadIng() {
|
||||
if (this.loadStatus) {
|
||||
@ -132,195 +59,21 @@ export default {
|
||||
const load = this.taskLoading.find(({id}) => id == this.task.id);
|
||||
return load && load.num > 0
|
||||
},
|
||||
|
||||
flow() {
|
||||
return this.taskFlows.find(({task_id}) => task_id == this.task.id);
|
||||
},
|
||||
|
||||
turns() {
|
||||
if (!this.flow) {
|
||||
return [];
|
||||
}
|
||||
let item = this.taskFlowItems.find(({id}) => id == this.flow.flow_item_id);
|
||||
if (!item) {
|
||||
return [];
|
||||
}
|
||||
return this.taskFlowItems.filter(({id}) => item.turns.includes(id))
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
show() {
|
||||
this.$refs.dropdown.show()
|
||||
},
|
||||
|
||||
hide() {
|
||||
this.$refs.dropdown.hide()
|
||||
},
|
||||
|
||||
handleClick() {
|
||||
this.$refs.dropdown.handleClick()
|
||||
},
|
||||
|
||||
dropTask(command) {
|
||||
const cacheTask = this.task;
|
||||
const completeTemp = (save) => {
|
||||
if (save) {
|
||||
this.$store.dispatch("saveTaskCompleteTemp", cacheTask.id)
|
||||
} else {
|
||||
this.$store.dispatch("forgetTaskCompleteTemp", cacheTask.id)
|
||||
}
|
||||
}
|
||||
// 修改背景色
|
||||
if ($A.isJson(command)) {
|
||||
if (command.name) {
|
||||
this.updateTask({
|
||||
color: command.color
|
||||
}).catch(() => {})
|
||||
}
|
||||
return;
|
||||
}
|
||||
// 修改工作流状态
|
||||
if ($A.leftExists(command, 'turn::')) {
|
||||
let flow_item_id = $A.leftDelete(command, 'turn::');
|
||||
if (flow_item_id == this.task.flow_item_id) return;
|
||||
//
|
||||
let currentFlow = this.taskFlowItems.find(({id}) => id == this.flow.flow_item_id) || {};
|
||||
let updateFlow = this.taskFlowItems.find(({id}) => id == flow_item_id) || {};
|
||||
let isComplete = currentFlow.status !== 'end' && updateFlow.status === 'end';
|
||||
let isUnComplete = currentFlow.status === 'end' && updateFlow.status !== 'end';
|
||||
if (this.updateBefore) {
|
||||
if (isComplete) {
|
||||
completeTemp(true)
|
||||
} else if (isUnComplete) {
|
||||
completeTemp(false)
|
||||
}
|
||||
}
|
||||
this.updateTask({
|
||||
flow_item_id
|
||||
}).then(() => {
|
||||
if (isComplete) {
|
||||
completeTemp(true)
|
||||
} else if (isUnComplete) {
|
||||
completeTemp(false)
|
||||
}
|
||||
}).catch(() => {
|
||||
if (isComplete) {
|
||||
completeTemp(false)
|
||||
} else if (isUnComplete) {
|
||||
completeTemp(true)
|
||||
}
|
||||
})
|
||||
return;
|
||||
}
|
||||
// 其他操作
|
||||
switch (command) {
|
||||
case 'complete':
|
||||
if (this.task.complete_at) {
|
||||
return;
|
||||
}
|
||||
if (this.updateBefore) {
|
||||
completeTemp(true)
|
||||
}
|
||||
this.updateTask({
|
||||
complete_at: $A.formatDate("Y-m-d H:i:s")
|
||||
}).then(() => {
|
||||
completeTemp(true)
|
||||
}).catch(() => {
|
||||
completeTemp(false)
|
||||
})
|
||||
break;
|
||||
|
||||
case 'uncomplete':
|
||||
if (!this.task.complete_at) {
|
||||
return;
|
||||
}
|
||||
if (this.updateBefore) {
|
||||
completeTemp(false)
|
||||
}
|
||||
this.updateTask({
|
||||
complete_at: false
|
||||
}).then(() => {
|
||||
completeTemp(false)
|
||||
}).catch(() => {
|
||||
completeTemp(true)
|
||||
})
|
||||
break;
|
||||
|
||||
case 'archived':
|
||||
case 'remove':
|
||||
this.archivedOrRemoveTask(command);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
visibleChange(visible) {
|
||||
if (visible) {
|
||||
this.$store.dispatch("getTaskFlow", this.task.id)
|
||||
.then(this.$refs.dropdownMenu.updatePopper)
|
||||
.catch(this.$refs.dropdownMenu.updatePopper)
|
||||
}
|
||||
},
|
||||
|
||||
updateTask(updata) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (this.loadIng) {
|
||||
reject()
|
||||
return;
|
||||
}
|
||||
//
|
||||
Object.keys(updata).forEach(key => this.$set(this.task, key, updata[key]));
|
||||
//
|
||||
const updateData = Object.assign(updata, {
|
||||
task_id: this.task.id,
|
||||
});
|
||||
this.$store.dispatch("taskUpdate", updateData).then(({data, msg}) => {
|
||||
$A.messageSuccess(msg);
|
||||
resolve()
|
||||
this.$store.dispatch("saveTaskBrowse", updateData.task_id);
|
||||
handleClick(event) {
|
||||
this.$store.state.taskOperation = {
|
||||
event,
|
||||
task: this.task,
|
||||
loadStatus: this.loadStatus,
|
||||
colorShow: this.colorShow,
|
||||
updateBefore: this.updateBefore,
|
||||
disabled: this.disabled,
|
||||
size: this.size,
|
||||
onUpdate: data => {
|
||||
this.$emit("on-update", data)
|
||||
}).catch(({msg}) => {
|
||||
$A.modalError(msg);
|
||||
this.$store.dispatch("getTaskOne", updateData.task_id).catch(() => {})
|
||||
reject()
|
||||
});
|
||||
})
|
||||
},
|
||||
|
||||
archivedOrRemoveTask(type) {
|
||||
let typeDispatch = 'removeTask';
|
||||
let typeName = '删除';
|
||||
let typeData = {task_id: this.task.id};
|
||||
let typeTask = this.task.parent_id > 0 ? '子任务' : '任务';
|
||||
if (type == 'archived') {
|
||||
typeDispatch = 'archivedTask'
|
||||
typeName = '归档'
|
||||
if (this.task.archived_at) {
|
||||
typeName = '还原归档'
|
||||
typeData = {
|
||||
task_id: this.task.id,
|
||||
type: 'recovery'
|
||||
}
|
||||
}
|
||||
}
|
||||
$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, typeData).then(({msg}) => {
|
||||
$A.messageSuccess(msg);
|
||||
this.$store.dispatch("saveTaskBrowse", typeData.task_id);
|
||||
}).catch(({msg}) => {
|
||||
$A.modalError(msg, 301);
|
||||
}).finally(_ => {
|
||||
this.$Modal.remove();
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
388
resources/assets/js/pages/manage/components/TaskOperation.vue
Normal file
388
resources/assets/js/pages/manage/components/TaskOperation.vue
Normal file
@ -0,0 +1,388 @@
|
||||
<template>
|
||||
<EDropdown
|
||||
ref="dropdown"
|
||||
trigger="click"
|
||||
:disabled="disabled"
|
||||
:size="size"
|
||||
:style="styles"
|
||||
class="task-operation-dropdown"
|
||||
placement="bottom"
|
||||
@command="dropTask"
|
||||
@visible-change="visibleChange">
|
||||
<div ref="icon" class="task-operation-icon"></div>
|
||||
<EDropdownMenu ref="dropdownMenu" slot="dropdown" class="task-operation-more-dropdown">
|
||||
<li class="task-operation-more-warp" :class="size">
|
||||
<ul>
|
||||
<EDropdownItem v-if="!flow" class="load-flow" disabled>
|
||||
<div class="load-flow-warp">
|
||||
<Loading/>
|
||||
</div>
|
||||
</EDropdownItem>
|
||||
<template v-else-if="turns.length > 0">
|
||||
<EDropdownItem v-for="item in turns" :key="item.id" :command="`turn::${item.id}`">
|
||||
<div class="item flow">
|
||||
<Icon v-if="item.id == task.flow_item_id && flow.auto_assign !== true" class="check" type="md-checkmark-circle-outline" />
|
||||
<Icon v-else type="md-radio-button-off" />
|
||||
<div class="flow-name" :class="item.status">{{item.name}}</div>
|
||||
</div>
|
||||
</EDropdownItem>
|
||||
</template>
|
||||
<template v-else>
|
||||
<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>
|
||||
</template>
|
||||
|
||||
<template v-if="task.parent_id === 0">
|
||||
<EDropdownItem :divided="turns.length > 0" command="archived">
|
||||
<div class="item">
|
||||
<Icon type="ios-filing" />{{$L(task.archived_at ? '还原归档' : '归档')}}
|
||||
</div>
|
||||
</EDropdownItem>
|
||||
<EDropdownItem command="remove">
|
||||
<div class="item hover-del">
|
||||
<Icon type="md-trash" />{{$L('删除')}}
|
||||
</div>
|
||||
</EDropdownItem>
|
||||
<template v-if="colorShow">
|
||||
<EDropdownItem v-for="(c, k) in taskColorList" :key="'c_' + k" :divided="k==0" :command="c">
|
||||
<div class="item">
|
||||
<i class="taskfont" :style="{color:c.color||'#f9f9f9'}" v-html="c.color == task.color ? '' : ''"></i>{{$L(c.name)}}
|
||||
</div>
|
||||
</EDropdownItem>
|
||||
</template>
|
||||
</template>
|
||||
<EDropdownItem v-else command="remove" :divided="turns.length > 0">
|
||||
<div class="item">
|
||||
<Icon type="md-trash" />{{$L('删除')}}
|
||||
</div>
|
||||
</EDropdownItem>
|
||||
</ul>
|
||||
</li>
|
||||
</EDropdownMenu>
|
||||
</EDropdown>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {mapState} from "vuex";
|
||||
|
||||
export default {
|
||||
name: "TaskOperation",
|
||||
data() {
|
||||
return {
|
||||
task: {},
|
||||
loadStatus: false,
|
||||
colorShow: true,
|
||||
updateBefore: false,
|
||||
disabled: false,
|
||||
size: 'small',
|
||||
onUpdate: null,
|
||||
|
||||
element: null,
|
||||
target: null,
|
||||
styles: {},
|
||||
}
|
||||
},
|
||||
beforeDestroy() {
|
||||
if (this.target) {
|
||||
this.target.removeEventListener('scroll', this.handlerEventListeners);
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState(['taskOperation', 'taskColorList', 'taskLoading', 'taskFlows', 'taskFlowItems']),
|
||||
|
||||
loadIng() {
|
||||
if (this.loadStatus) {
|
||||
return true;
|
||||
}
|
||||
const load = this.taskLoading.find(({id}) => id == this.task.id);
|
||||
return load && load.num > 0
|
||||
},
|
||||
|
||||
flow() {
|
||||
return this.taskFlows.find(({task_id}) => task_id == this.task.id);
|
||||
},
|
||||
|
||||
turns() {
|
||||
if (!this.flow) {
|
||||
return [];
|
||||
}
|
||||
let item = this.taskFlowItems.find(({id}) => id == this.flow.flow_item_id);
|
||||
if (!item) {
|
||||
return [];
|
||||
}
|
||||
return this.taskFlowItems.filter(({id}) => item.turns.includes(id))
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
taskOperation(data) {
|
||||
if (data.event && data.task) {
|
||||
const eventRect = data.event.target.getBoundingClientRect();
|
||||
this.styles = {
|
||||
left: `${eventRect.left}px`,
|
||||
top: `${eventRect.top}px`,
|
||||
width: `${eventRect.width}px`,
|
||||
height: `${eventRect.height}px`,
|
||||
}
|
||||
this.task = data.task;
|
||||
this.loadStatus = typeof data.loadStatus === "undefined" ? false : data.loadStatus;
|
||||
this.colorShow = typeof data.colorShow === "undefined" ? true : data.colorShow;
|
||||
this.updateBefore = typeof data.updateBefore === "undefined" ? false : data.updateBefore;
|
||||
this.disabled = typeof data.disabled === "undefined" ? false : data.disabled;
|
||||
this.size = typeof data.size === "undefined" ? "small" : data.size;
|
||||
this.onUpdate = typeof data.onUpdate === "function" ? data.onUpdate : null;
|
||||
//
|
||||
this.$refs.icon.focus();
|
||||
this.updatePopper();
|
||||
this.show();
|
||||
this.$store.dispatch("getTaskFlow", this.task.id).finally(this.updatePopper)
|
||||
this.setupEventListeners(data.event)
|
||||
} else {
|
||||
this.hide();
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
show() {
|
||||
this.$refs.dropdown.show()
|
||||
},
|
||||
|
||||
hide() {
|
||||
this.$refs.dropdown.hide()
|
||||
},
|
||||
|
||||
dropTask(command) {
|
||||
const cacheTask = this.task;
|
||||
const completeTemp = (save) => {
|
||||
if (save) {
|
||||
this.$store.dispatch("saveTaskCompleteTemp", cacheTask.id)
|
||||
} else {
|
||||
this.$store.dispatch("forgetTaskCompleteTemp", cacheTask.id)
|
||||
}
|
||||
}
|
||||
// 修改背景色
|
||||
if ($A.isJson(command)) {
|
||||
if (command.name) {
|
||||
this.updateTask({
|
||||
color: command.color
|
||||
}).catch(() => {})
|
||||
}
|
||||
return;
|
||||
}
|
||||
// 修改工作流状态
|
||||
if ($A.leftExists(command, 'turn::')) {
|
||||
let flow_item_id = $A.leftDelete(command, 'turn::');
|
||||
if (flow_item_id == this.task.flow_item_id) return;
|
||||
//
|
||||
let currentFlow = this.taskFlowItems.find(({id}) => id == this.flow.flow_item_id) || {};
|
||||
let updateFlow = this.taskFlowItems.find(({id}) => id == flow_item_id) || {};
|
||||
let isComplete = currentFlow.status !== 'end' && updateFlow.status === 'end';
|
||||
let isUnComplete = currentFlow.status === 'end' && updateFlow.status !== 'end';
|
||||
if (this.updateBefore) {
|
||||
if (isComplete) {
|
||||
completeTemp(true)
|
||||
} else if (isUnComplete) {
|
||||
completeTemp(false)
|
||||
}
|
||||
}
|
||||
this.updateTask({
|
||||
flow_item_id
|
||||
}).then(() => {
|
||||
if (isComplete) {
|
||||
completeTemp(true)
|
||||
} else if (isUnComplete) {
|
||||
completeTemp(false)
|
||||
}
|
||||
}).catch(() => {
|
||||
if (isComplete) {
|
||||
completeTemp(false)
|
||||
} else if (isUnComplete) {
|
||||
completeTemp(true)
|
||||
}
|
||||
})
|
||||
return;
|
||||
}
|
||||
// 其他操作
|
||||
switch (command) {
|
||||
case 'complete':
|
||||
if (this.task.complete_at) {
|
||||
return;
|
||||
}
|
||||
if (this.updateBefore) {
|
||||
completeTemp(true)
|
||||
}
|
||||
this.updateTask({
|
||||
complete_at: $A.formatDate("Y-m-d H:i:s")
|
||||
}).then(() => {
|
||||
completeTemp(true)
|
||||
}).catch(() => {
|
||||
completeTemp(false)
|
||||
})
|
||||
break;
|
||||
|
||||
case 'uncomplete':
|
||||
if (!this.task.complete_at) {
|
||||
return;
|
||||
}
|
||||
if (this.updateBefore) {
|
||||
completeTemp(false)
|
||||
}
|
||||
this.updateTask({
|
||||
complete_at: false
|
||||
}).then(() => {
|
||||
completeTemp(false)
|
||||
}).catch(() => {
|
||||
completeTemp(true)
|
||||
})
|
||||
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]));
|
||||
//
|
||||
const updateData = Object.assign(updata, {
|
||||
task_id: this.task.id,
|
||||
});
|
||||
this.$store.dispatch("taskUpdate", updateData).then(({data, msg}) => {
|
||||
$A.messageSuccess(msg);
|
||||
resolve()
|
||||
this.$store.dispatch("saveTaskBrowse", updateData.task_id);
|
||||
if (typeof this.onUpdate === "function") {
|
||||
this.onUpdate(data)
|
||||
}
|
||||
}).catch(({msg}) => {
|
||||
$A.modalError(msg);
|
||||
this.$store.dispatch("getTaskOne", updateData.task_id).catch(() => {})
|
||||
reject()
|
||||
});
|
||||
})
|
||||
},
|
||||
|
||||
archivedOrRemoveTask(type) {
|
||||
let typeDispatch = 'removeTask';
|
||||
let typeName = '删除';
|
||||
let typeData = {task_id: this.task.id};
|
||||
let typeTask = this.task.parent_id > 0 ? '子任务' : '任务';
|
||||
if (type == 'archived') {
|
||||
typeDispatch = 'archivedTask'
|
||||
typeName = '归档'
|
||||
if (this.task.archived_at) {
|
||||
typeName = '还原归档'
|
||||
typeData = {
|
||||
task_id: this.task.id,
|
||||
type: 'recovery'
|
||||
}
|
||||
}
|
||||
}
|
||||
$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, typeData).then(({msg}) => {
|
||||
$A.messageSuccess(msg);
|
||||
this.$store.dispatch("saveTaskBrowse", typeData.task_id);
|
||||
}).catch(({msg}) => {
|
||||
$A.modalError(msg, 301);
|
||||
}).finally(_ => {
|
||||
this.$Modal.remove();
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
visibleChange(visible) {
|
||||
this.visible = visible;
|
||||
},
|
||||
|
||||
updatePopper() {
|
||||
this.$nextTick(this.$refs.dropdownMenu.updatePopper)
|
||||
},
|
||||
|
||||
setupEventListeners(event) {
|
||||
this.element = event.target;
|
||||
let target = this.getScrollParent(this.element);
|
||||
if (target === window.document.body || target === window.document.documentElement) {
|
||||
target = window;
|
||||
}
|
||||
if (this.target) {
|
||||
if (this.target === target) {
|
||||
return;
|
||||
}
|
||||
this.target.removeEventListener('scroll', this.handlerEventListeners);
|
||||
}
|
||||
this.target = target;
|
||||
this.target.addEventListener('scroll', this.handlerEventListeners);
|
||||
},
|
||||
|
||||
handlerEventListeners(e) {
|
||||
if (!this.visible || !this.element) {
|
||||
return
|
||||
}
|
||||
const scrollRect = e.target.getBoundingClientRect();
|
||||
const eventRect = this.element.getBoundingClientRect();
|
||||
if (eventRect.top < scrollRect.top || eventRect.top > scrollRect.top + scrollRect.height) {
|
||||
this.hide();
|
||||
return;
|
||||
}
|
||||
this.styles = {
|
||||
left: `${eventRect.left}px`,
|
||||
top: `${eventRect.top}px`,
|
||||
width: `${eventRect.width}px`,
|
||||
height: `${eventRect.height}px`,
|
||||
};
|
||||
this.updatePopper();
|
||||
},
|
||||
|
||||
getScrollParent(element) {
|
||||
const parent = element.parentNode;
|
||||
if (!parent) {
|
||||
return element;
|
||||
}
|
||||
if (parent === window.document) {
|
||||
if (window.document.body.scrollTop || window.document.body.scrollLeft) {
|
||||
return window.document.body;
|
||||
} else {
|
||||
return window.document.documentElement;
|
||||
}
|
||||
}
|
||||
if (
|
||||
['scroll', 'auto'].indexOf(this.getStyleComputedProperty(parent, 'overflow')) !== -1 ||
|
||||
['scroll', 'auto'].indexOf(this.getStyleComputedProperty(parent, 'overflow-x')) !== -1 ||
|
||||
['scroll', 'auto'].indexOf(this.getStyleComputedProperty(parent, 'overflow-y')) !== -1
|
||||
) {
|
||||
return parent;
|
||||
}
|
||||
return this.getScrollParent(element.parentNode);
|
||||
},
|
||||
|
||||
getStyleComputedProperty(element, property) {
|
||||
const css = window.getComputedStyle(element, null);
|
||||
return css[property];
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@ -12,7 +12,7 @@
|
||||
<TaskMenu :ref="`taskMenu_${item.id}`" :task="item"/>
|
||||
<div class="item-title" @click="openTask(item)">
|
||||
<!--工作流状态-->
|
||||
<span v-if="item.flow_item_name" :class="item.flow_item_status" @click.stop="openMenu(item)">{{item.flow_item_name}}</span>
|
||||
<span v-if="item.flow_item_name" :class="item.flow_item_status" @click.stop="openMenu($event, item)">{{item.flow_item_name}}</span>
|
||||
<!--是否子任务-->
|
||||
<span v-if="item.sub_top === true">{{$L('子任务')}}</span>
|
||||
<!--有多少个子任务-->
|
||||
@ -249,10 +249,10 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
openMenu(task) {
|
||||
openMenu(event, task) {
|
||||
const el = this.$refs[`taskMenu_${task.id}`];
|
||||
if (el) {
|
||||
el[0].handleClick()
|
||||
el[0].handleClick(event)
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@ -49,14 +49,12 @@
|
||||
v-if="item.p_name"
|
||||
class="priority-color"
|
||||
:style="{backgroundColor:item.p_color}"></em>
|
||||
<TaskMenu :ref="`taskMenu_${column.type}_${item.id}`" :task="item">
|
||||
<div slot="icon" class="drop-icon" @click.stop="">
|
||||
<i class="taskfont" v-html="item.complete_at ? '' : ''"></i>
|
||||
</div>
|
||||
</TaskMenu>
|
||||
<div class="item-select" @click.stop="openMenu($event, item)">
|
||||
<i class="taskfont" v-html="item.complete_at ? '' : ''"></i>
|
||||
</div>
|
||||
<div class="item-title">
|
||||
<!--工作流状态-->
|
||||
<span v-if="item.flow_item_name" :class="item.flow_item_status" @click.stop="openMenu(column.type, item)">{{item.flow_item_name}}</span>
|
||||
<span v-if="item.flow_item_name" :class="item.flow_item_status" @click.stop="openMenu($event, item)">{{item.flow_item_name}}</span>
|
||||
<!--是否子任务-->
|
||||
<span v-if="item.sub_top === true">{{$L('子任务')}}</span>
|
||||
<!--有多少个子任务-->
|
||||
@ -170,11 +168,8 @@ export default {
|
||||
this.$store.dispatch("openTask", task)
|
||||
},
|
||||
|
||||
openMenu(type, task) {
|
||||
const el = this.$refs[`taskMenu_${type}_${task.id}`];
|
||||
if (el) {
|
||||
el[0].handleClick()
|
||||
}
|
||||
openMenu(event, task) {
|
||||
this.$store.state.taskOperation = {event, task}
|
||||
},
|
||||
|
||||
expiresFormat(date) {
|
||||
|
||||
1
resources/assets/js/store/state.js
vendored
1
resources/assets/js/store/state.js
vendored
@ -84,6 +84,7 @@ const stateData = {
|
||||
taskContents: [],
|
||||
taskFiles: [],
|
||||
taskLogs: [],
|
||||
taskOperation: {},
|
||||
|
||||
// 任务等待状态
|
||||
taskLoading: [],
|
||||
|
||||
@ -17,5 +17,6 @@
|
||||
@import "task-deleted";
|
||||
@import "task-detail";
|
||||
@import "task-menu";
|
||||
@import "task-operation";
|
||||
@import "task-priority";
|
||||
@import "team-management";
|
||||
|
||||
@ -25,26 +25,24 @@
|
||||
height: 1px;
|
||||
background-color: #f4f5f5;
|
||||
}
|
||||
.icon {
|
||||
.task-menu-icon {
|
||||
margin-right: 18px;
|
||||
.task-menu-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.ivu-icon {
|
||||
font-size: 18px;
|
||||
}
|
||||
.loading {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
.common-loading {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
}
|
||||
.uncomplete {
|
||||
color: #888888;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.ivu-icon {
|
||||
font-size: 18px;
|
||||
}
|
||||
.loading {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
.common-loading {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
}
|
||||
.uncomplete {
|
||||
color: #888888;
|
||||
}
|
||||
}
|
||||
.flow {
|
||||
display: flex;
|
||||
|
||||
@ -25,127 +25,3 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.task-menu-more-dropdown {
|
||||
> li {
|
||||
&.task-menu-more-warp {
|
||||
list-style: none;
|
||||
|
||||
> ul {
|
||||
max-height: 320px;
|
||||
overflow: auto;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
display: none
|
||||
}
|
||||
|
||||
> li {
|
||||
.item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
> i {
|
||||
flex-shrink: 0;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
line-height: 18px;
|
||||
font-size: 18px;
|
||||
margin-right: 8px;
|
||||
padding: 0;
|
||||
color: #bbbbbb;
|
||||
|
||||
&.ivu-icon {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.flow {
|
||||
padding: 4px 0;
|
||||
|
||||
> i {
|
||||
margin-right: 3px;
|
||||
|
||||
&.check {
|
||||
color: $primary-color;
|
||||
}
|
||||
}
|
||||
|
||||
.flow-name {
|
||||
border-radius: 4px;
|
||||
white-space: nowrap;
|
||||
padding: 0 5px;
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
font-size: 12px;
|
||||
background: #f4f4f4;
|
||||
color: #595959;
|
||||
|
||||
&.start {
|
||||
background-color: rgba($flow-status-start-color, 0.1);
|
||||
border-color: rgba($flow-status-start-color, 0.1);
|
||||
color: $flow-status-start-color;
|
||||
}
|
||||
&.progress {
|
||||
background-color: rgba($flow-status-progress-color, 0.1);
|
||||
border-color: rgba($flow-status-progress-color, 0.1);
|
||||
color: $flow-status-progress-color;
|
||||
}
|
||||
&.test {
|
||||
background-color: rgba($flow-status-test-color, 0.1);
|
||||
border-color: rgba($flow-status-test-color, 0.1);
|
||||
color: $flow-status-test-color;
|
||||
}
|
||||
&.end {
|
||||
background-color: rgba($flow-status-end-color, 0.1);
|
||||
border-color: rgba($flow-status-end-color, 0.1);
|
||||
color: $flow-status-end-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.load-flow {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 8px;
|
||||
|
||||
.load-flow-warp {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.medium {
|
||||
> ul {
|
||||
> li {
|
||||
.flow {
|
||||
.flow-name {
|
||||
height: 24px;
|
||||
line-height: 24px;
|
||||
padding: 0 7px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.large {
|
||||
> ul {
|
||||
> li {
|
||||
.flow {
|
||||
.flow-name {
|
||||
font-size: 13px;
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
padding: 0 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
143
resources/assets/sass/pages/components/task-operation.scss
vendored
Normal file
143
resources/assets/sass/pages/components/task-operation.scss
vendored
Normal file
@ -0,0 +1,143 @@
|
||||
.task-operation-dropdown {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 0;
|
||||
opacity: 0;
|
||||
z-index: -1;
|
||||
|
||||
.task-operation-icon {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.task-operation-more-dropdown {
|
||||
> li {
|
||||
&.task-operation-more-warp {
|
||||
list-style: none;
|
||||
|
||||
> ul {
|
||||
max-height: 320px;
|
||||
overflow: auto;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
display: none
|
||||
}
|
||||
|
||||
> li {
|
||||
.item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
> i {
|
||||
flex-shrink: 0;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
line-height: 18px;
|
||||
font-size: 18px;
|
||||
margin-right: 8px;
|
||||
padding: 0;
|
||||
color: #bbbbbb;
|
||||
|
||||
&.ivu-icon {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.flow {
|
||||
padding: 4px 0;
|
||||
|
||||
> i {
|
||||
margin-right: 3px;
|
||||
|
||||
&.check {
|
||||
color: $primary-color;
|
||||
}
|
||||
}
|
||||
|
||||
.flow-name {
|
||||
border-radius: 4px;
|
||||
white-space: nowrap;
|
||||
padding: 0 5px;
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
font-size: 12px;
|
||||
background: #f4f4f4;
|
||||
color: #595959;
|
||||
|
||||
&.start {
|
||||
background-color: rgba($flow-status-start-color, 0.1);
|
||||
border-color: rgba($flow-status-start-color, 0.1);
|
||||
color: $flow-status-start-color;
|
||||
}
|
||||
&.progress {
|
||||
background-color: rgba($flow-status-progress-color, 0.1);
|
||||
border-color: rgba($flow-status-progress-color, 0.1);
|
||||
color: $flow-status-progress-color;
|
||||
}
|
||||
&.test {
|
||||
background-color: rgba($flow-status-test-color, 0.1);
|
||||
border-color: rgba($flow-status-test-color, 0.1);
|
||||
color: $flow-status-test-color;
|
||||
}
|
||||
&.end {
|
||||
background-color: rgba($flow-status-end-color, 0.1);
|
||||
border-color: rgba($flow-status-end-color, 0.1);
|
||||
color: $flow-status-end-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.load-flow {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 8px;
|
||||
|
||||
.load-flow-warp {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.medium {
|
||||
> ul {
|
||||
> li {
|
||||
.flow {
|
||||
.flow-name {
|
||||
height: 24px;
|
||||
line-height: 24px;
|
||||
padding: 0 7px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.large {
|
||||
> ul {
|
||||
> li {
|
||||
.flow {
|
||||
.flow-name {
|
||||
font-size: 13px;
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
padding: 0 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -122,7 +122,7 @@
|
||||
}
|
||||
.dashboard-ul {
|
||||
margin: 0 auto 18px;
|
||||
padding: 0 12px;
|
||||
padding: 6px 12px 0;
|
||||
overflow: hidden;
|
||||
> li {
|
||||
position: relative;
|
||||
@ -133,9 +133,8 @@
|
||||
margin-bottom: 8px;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
transition: transform 0.3s ease-out 0s, box-shadow 0.3s ease-out 0s;
|
||||
transition: box-shadow 0.3s ease-out 0s;
|
||||
&:hover {
|
||||
transform: translateX(-2px);
|
||||
box-shadow: 0 0 6px #dfdfdf;
|
||||
}
|
||||
&.complete {
|
||||
@ -155,7 +154,7 @@
|
||||
height: 12px;
|
||||
width: 2px;
|
||||
}
|
||||
.el-dropdown {
|
||||
.item-select {
|
||||
flex-shrink: 0;
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user