|
@@ -1,11 +1,23 @@
|
|
|
<template>
|
|
|
<div class="mind">
|
|
|
<div class="mind-left">
|
|
|
- <div v-for="(li, i) in UpperHalf" :key="`mind-${i}`" :class="['task-item', mindHalfListClass(i, true)]">
|
|
|
- <span :class="['task-item-name']" :data-index="i + 1">
|
|
|
- <span class="nowrap-ellipsis" :title="li.name">{{ li.name }}</span>
|
|
|
- </span>
|
|
|
- <span class="task-item-date">{{ previewDateTransform(li.begin_time, li.duration_second) }}</span>
|
|
|
+ <div v-for="(li, i) in UpperHalf" :key="`mind-${i}`" class="task-wrap">
|
|
|
+ <div class="subtask-list">
|
|
|
+ <div
|
|
|
+ v-for="({ name }, j) in li.child_task_list"
|
|
|
+ :key="`mind-subtask-${j}`"
|
|
|
+ :class="['subtask-item', mindHalfListClass(i, true, j, true)]"
|
|
|
+ >
|
|
|
+ <span class="subtask-name">{{ name }}</span>
|
|
|
+ <span class="subtask-index">子任务 {{ j + 1 }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div :class="['task-item', { 'has-subtask': li.child_task_list.length > 0 }, mindHalfListClass(i, true)]">
|
|
|
+ <span :class="['task-item-name']" :data-index="i + 1">
|
|
|
+ <span class="nowrap-ellipsis" :title="li.name">{{ li.name }}</span>
|
|
|
+ </span>
|
|
|
+ <span class="task-item-date">{{ previewDateTransform(li.begin_time, li.duration_second) }}</span>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</div>
|
|
|
<div class="main-wrap">
|
|
@@ -14,14 +26,24 @@
|
|
|
<span v-if="LowerHalf.length > 0" class="connect-line" :style="{ right: '-35px' }"></span>
|
|
|
</div>
|
|
|
<div class="mind-right">
|
|
|
- <template v-for="(li, i) in LowerHalf">
|
|
|
- <div :key="`mind-${i}`" :class="['task-item', mindHalfListClass(i, false)]">
|
|
|
+ <div v-for="(li, i) in LowerHalf" :key="`mind-${i}`" class="task-wrap">
|
|
|
+ <div :class="['task-item', { 'has-subtask': li.child_task_list.length > 0 }, mindHalfListClass(i, false)]">
|
|
|
<span class="task-item-name" :data-index="i + median + 2">
|
|
|
<span>{{ li.name }}</span>
|
|
|
</span>
|
|
|
<span class="task-item-date">{{ previewDateTransform(li.begin_time, li.duration_second) }}</span>
|
|
|
</div>
|
|
|
- </template>
|
|
|
+ <div class="subtask-list">
|
|
|
+ <div
|
|
|
+ v-for="({ name }, j) in li.child_task_list"
|
|
|
+ :key="`mind-subtask-${j}`"
|
|
|
+ :class="['subtask-item', mindHalfListClass(i, false, j, true)]"
|
|
|
+ >
|
|
|
+ <span class="subtask-name">{{ name }}</span>
|
|
|
+ <span class="subtask-index">子任务 {{ j + 1 }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</template>
|
|
@@ -54,16 +76,20 @@ let LowerHalf = computed(() => {
|
|
|
|
|
|
/**
|
|
|
* 计算上/下半部分每个子项所需的 class
|
|
|
- * @param {Number} i 索引
|
|
|
+ * @param {Number} i 任务索引
|
|
|
* @param {Boolean} isUpper 是否上半部分
|
|
|
+ * @param {Number} j 子任务索引
|
|
|
+ * @param {Boolean} isSubtask 是否子任务
|
|
|
*/
|
|
|
-function mindHalfListClass(i, isUpper) {
|
|
|
- let length = isUpper ? UpperHalf.value.length : LowerHalf.value.length;
|
|
|
+function mindHalfListClass(i, isUpper, j, isSubtask = false) {
|
|
|
+ let list = isUpper ? UpperHalf.value : LowerHalf.value;
|
|
|
+ let length = isSubtask ? list[i].child_task_list.length : list.length;
|
|
|
if (length === 1) return;
|
|
|
let half = Math.ceil(length / 2) - 1;
|
|
|
const isEven = length % 2 === 0;
|
|
|
- if (isEven) return i <= half ? 'mind-down' : 'mind-up';
|
|
|
- return i < half ? 'mind-down' : i === half ? 'mind-center' : 'mind-up';
|
|
|
+ let index = isSubtask ? j : i;
|
|
|
+ if (isEven) return index <= half ? 'mind-down' : 'mind-up';
|
|
|
+ return index < half ? 'mind-down' : index === half ? 'mind-center' : 'mind-up';
|
|
|
}
|
|
|
</script>
|
|
|
|
|
@@ -95,6 +121,7 @@ $bc-color: #2a76e8;
|
|
|
|
|
|
.csitem-name {
|
|
|
display: inline-block;
|
|
|
+ max-width: 300px;
|
|
|
padding: 8px 12px;
|
|
|
color: #fff;
|
|
|
vertical-align: middle;
|
|
@@ -117,6 +144,17 @@ $bc-color: #2a76e8;
|
|
|
&::after {
|
|
|
right: -31px;
|
|
|
}
|
|
|
+
|
|
|
+ &.has-subtask::after {
|
|
|
+ left: -19px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .subtask-item {
|
|
|
+ &::before,
|
|
|
+ &::after {
|
|
|
+ right: -28px;
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -126,29 +164,125 @@ $bc-color: #2a76e8;
|
|
|
&::after {
|
|
|
left: -31px;
|
|
|
}
|
|
|
+
|
|
|
+ &.has-subtask::after {
|
|
|
+ right: -19px;
|
|
|
+ left: auto;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .subtask-item {
|
|
|
+ &::before,
|
|
|
+ &::after {
|
|
|
+ left: -28px;
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- .task-item {
|
|
|
- position: relative;
|
|
|
+ .task-wrap {
|
|
|
display: flex;
|
|
|
- flex-direction: column;
|
|
|
- row-gap: 8px;
|
|
|
- padding: 8px 12px;
|
|
|
- background-color: #fff;
|
|
|
- border-radius: 20px;
|
|
|
+ column-gap: 47px;
|
|
|
+ align-items: center;
|
|
|
|
|
|
- &::before {
|
|
|
- position: absolute;
|
|
|
- top: 50%;
|
|
|
- width: 31px;
|
|
|
- height: 3px;
|
|
|
- content: '';
|
|
|
- background-color: $bc-color;
|
|
|
+ // 子任务列表
|
|
|
+ .subtask-list {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ row-gap: 8px;
|
|
|
+
|
|
|
+ .subtask-item {
|
|
|
+ position: relative;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ row-gap: 8px;
|
|
|
+ padding: 8px 12px;
|
|
|
+ background-color: #fff;
|
|
|
+ border-radius: 20px;
|
|
|
+
|
|
|
+ &::before {
|
|
|
+ position: absolute;
|
|
|
+ top: 50%;
|
|
|
+ width: 28px;
|
|
|
+ height: 3px;
|
|
|
+ content: '';
|
|
|
+ background-color: $bc-color;
|
|
|
+ }
|
|
|
+
|
|
|
+ &.mind-down::after {
|
|
|
+ position: absolute;
|
|
|
+ top: 50%;
|
|
|
+ width: 3px;
|
|
|
+ height: calc(50% + 6px);
|
|
|
+ content: '';
|
|
|
+ background-color: $bc-color;
|
|
|
+ }
|
|
|
+
|
|
|
+ &.mind-center::after {
|
|
|
+ position: absolute;
|
|
|
+ top: -33px;
|
|
|
+ width: 3px;
|
|
|
+ height: calc(100% + 66px);
|
|
|
+ content: '';
|
|
|
+ background-color: $bc-color;
|
|
|
+ }
|
|
|
+
|
|
|
+ &.mind-up::after {
|
|
|
+ position: absolute;
|
|
|
+ bottom: 50%;
|
|
|
+ width: 3px;
|
|
|
+ height: calc(50% + 6px);
|
|
|
+ content: '';
|
|
|
+ background-color: $bc-color;
|
|
|
+ }
|
|
|
+
|
|
|
+ .subtask-name {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ font-size: 16px;
|
|
|
+ font-weight: bold;
|
|
|
+ color: #000;
|
|
|
+ }
|
|
|
+
|
|
|
+ .subtask-index {
|
|
|
+ min-width: 82px;
|
|
|
+ height: 22px;
|
|
|
+ padding: 2px 12px;
|
|
|
+ background-color: #ebebeb;
|
|
|
+ border-radius: 20px;
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- &.mind-down {
|
|
|
- &::after {
|
|
|
+ .task-item {
|
|
|
+ position: relative;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ row-gap: 8px;
|
|
|
+ width: 310px;
|
|
|
+ height: 72px;
|
|
|
+ padding: 8px 12px;
|
|
|
+ background-color: #fff;
|
|
|
+ border-radius: 20px;
|
|
|
+
|
|
|
+ &.has-subtask::after {
|
|
|
+ position: absolute;
|
|
|
+ top: 50%;
|
|
|
+ width: 19px;
|
|
|
+ height: 3px;
|
|
|
+ content: '';
|
|
|
+ background-color: $bc-color;
|
|
|
+ }
|
|
|
+
|
|
|
+ &::before {
|
|
|
+ position: absolute;
|
|
|
+ top: 50%;
|
|
|
+ width: 31px;
|
|
|
+ height: 3px;
|
|
|
+ content: '';
|
|
|
+ background-color: $bc-color;
|
|
|
+ }
|
|
|
+
|
|
|
+ &.mind-down::after {
|
|
|
position: absolute;
|
|
|
top: 50%;
|
|
|
width: 3px;
|
|
@@ -156,10 +290,8 @@ $bc-color: #2a76e8;
|
|
|
content: '';
|
|
|
background-color: $bc-color;
|
|
|
}
|
|
|
- }
|
|
|
|
|
|
- &.mind-center {
|
|
|
- &::after {
|
|
|
+ &.mind-center::after {
|
|
|
position: absolute;
|
|
|
top: -33px;
|
|
|
width: 3px;
|
|
@@ -167,48 +299,46 @@ $bc-color: #2a76e8;
|
|
|
content: '';
|
|
|
background-color: $bc-color;
|
|
|
}
|
|
|
- }
|
|
|
|
|
|
- &.mind-up {
|
|
|
- &::after {
|
|
|
+ &.mind-up::after {
|
|
|
position: absolute;
|
|
|
- top: calc(-50% + 3px);
|
|
|
+ bottom: 50%;
|
|
|
width: 3px;
|
|
|
height: calc(50% + 33px);
|
|
|
content: '';
|
|
|
background-color: $bc-color;
|
|
|
}
|
|
|
- }
|
|
|
|
|
|
- &-name {
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- font-size: 16px;
|
|
|
- font-weight: bold;
|
|
|
- color: #000;
|
|
|
+ &-name {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ font-size: 16px;
|
|
|
+ font-weight: bold;
|
|
|
+ color: #000;
|
|
|
|
|
|
- &::before {
|
|
|
- display: inline-block;
|
|
|
- width: 26px;
|
|
|
- height: 26px;
|
|
|
- margin-right: 8px;
|
|
|
- font-weight: normal;
|
|
|
- line-height: 26px;
|
|
|
- color: #fff;
|
|
|
- text-align: center;
|
|
|
- content: attr(data-index);
|
|
|
- background-color: #5498ff;
|
|
|
- border-radius: 50%;
|
|
|
+ &::before {
|
|
|
+ display: inline-block;
|
|
|
+ width: 26px;
|
|
|
+ height: 26px;
|
|
|
+ margin-right: 8px;
|
|
|
+ font-weight: normal;
|
|
|
+ line-height: 26px;
|
|
|
+ color: #fff;
|
|
|
+ text-align: center;
|
|
|
+ content: attr(data-index);
|
|
|
+ background-color: #5498ff;
|
|
|
+ border-radius: 50%;
|
|
|
+ }
|
|
|
}
|
|
|
- }
|
|
|
|
|
|
- &-date {
|
|
|
- min-width: 242px;
|
|
|
- height: 22px;
|
|
|
- padding: 2px 12px;
|
|
|
- margin-left: 34px;
|
|
|
- background-color: #ebebeb;
|
|
|
- border-radius: 20px;
|
|
|
+ &-date {
|
|
|
+ min-width: 242px;
|
|
|
+ height: 22px;
|
|
|
+ padding: 2px 12px;
|
|
|
+ margin-left: 34px;
|
|
|
+ background-color: #ebebeb;
|
|
|
+ border-radius: 20px;
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
}
|