perf: 优化全局任务操作菜单

This commit is contained in:
kuaifan 2022-06-07 10:17:41 +08:00
parent 242932735a
commit c9de0c2eba
13 changed files with 591 additions and 432 deletions

View File

@ -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,

View File

@ -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)
}
},

View File

@ -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() {

View File

@ -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 ? '&#xe61d;' : '&#xe61c;'"></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();
});
}
});
},
},
}

View 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 ? '&#xe61d;' : '&#xe61c;'"></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>

View File

@ -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)
}
},

View File

@ -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 ? '&#xe627;' : '&#xe625;'"></i>
</div>
</TaskMenu>
<div class="item-select" @click.stop="openMenu($event, item)">
<i class="taskfont" v-html="item.complete_at ? '&#xe627;' : '&#xe625;'"></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) {

View File

@ -84,6 +84,7 @@ const stateData = {
taskContents: [],
taskFiles: [],
taskLogs: [],
taskOperation: {},
// 任务等待状态
taskLoading: [],

View File

@ -17,5 +17,6 @@
@import "task-deleted";
@import "task-detail";
@import "task-menu";
@import "task-operation";
@import "task-priority";
@import "team-management";

View File

@ -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;

View File

@ -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;
}
}
}
}
}
}
}
}

View 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;
}
}
}
}
}
}
}
}

View File

@ -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;