129 lines
3.8 KiB
Vue
129 lines
3.8 KiB
Vue
<template>
|
||
<view>
|
||
<node-list v-for="(g, idx) in taskShowList" :key="g.id" :index="idx + 1" :last="idx === taskShowList.length - 1">
|
||
<view class="group-head">
|
||
<view class="group-title">{{ g.title }}</view>
|
||
<view class="group-toggle" @click.stop="toggle(g.id)">
|
||
<view class="toggle-text">{{ shrinkMap[g.id] ? '展开' : '收起' }}</view>
|
||
<uni-icons class="toggle-icon" size="12" :type="shrinkMap[g.id] ? 'arrowup' : 'arrowdown'" color="#6b7280" />
|
||
</view>
|
||
</view>
|
||
<view v-if="!shrinkMap[g.id]" class="group-body">
|
||
<plan-node-item v-for="(t, tIdx) in g.list" :key="String(t.taskId || tIdx)" :item="t" :classnames="tIdx ? 'mt-20' : ''" />
|
||
</view>
|
||
</node-list>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { computed, ref } from 'vue';
|
||
import dayjs from 'dayjs';
|
||
import nodeList from './node-list.vue';
|
||
import planNodeItem from './plan-node-item.vue';
|
||
|
||
const props = defineProps({
|
||
taskList: { type: Array, default: () => [] },
|
||
planExecutionTime: { type: String, default: '' },
|
||
});
|
||
|
||
const shrinkMap = ref({});
|
||
|
||
function toggle(id) {
|
||
shrinkMap.value[id] = !shrinkMap.value[id];
|
||
}
|
||
|
||
function timeTypeTransform(timeType) {
|
||
if (!timeType || timeType === 'day') return '天';
|
||
if (timeType === 'week') return '周';
|
||
if (timeType === 'month') return '月';
|
||
if (timeType === 'year') return '年';
|
||
return String(timeType);
|
||
}
|
||
|
||
function normalizedDays(taskTime, timeType) {
|
||
const t = typeof taskTime === 'number' ? taskTime : 0;
|
||
if (timeType === 'week') return t * 7;
|
||
if (timeType === 'month') return t * 30;
|
||
if (timeType === 'year') return t * 365;
|
||
return t;
|
||
}
|
||
|
||
function dayTransform(baseDate, taskTime, timeType) {
|
||
if (!baseDate) return '';
|
||
if (!dayjs(baseDate).isValid()) return '';
|
||
const t = typeof taskTime === 'number' ? taskTime : 0;
|
||
const unit = ['day', 'week', 'month', 'year'].includes(String(timeType || '')) ? String(timeType) : 'day';
|
||
return dayjs(baseDate).add(t, unit).format('YYYY-MM-DD');
|
||
}
|
||
|
||
const taskShowList = computed(() => {
|
||
if (!Array.isArray(props.taskList) || props.taskList.length === 0) return [];
|
||
|
||
const grouped = props.taskList.reduce((acc, task) => {
|
||
const obj = task && typeof task === 'object' ? task : {};
|
||
const timeKey = normalizedDays(obj.taskTime, obj.timeType);
|
||
const key = String(timeKey);
|
||
if (!acc[key]) acc[key] = [];
|
||
acc[key].push(obj);
|
||
return acc;
|
||
}, {});
|
||
|
||
const baseDate = props.planExecutionTime && dayjs(props.planExecutionTime).isValid() ? props.planExecutionTime : '';
|
||
return Object.entries(grouped)
|
||
.sort((a, b) => Number(a[0]) - Number(b[0]))
|
||
.map(([key, list]) => {
|
||
const first = list[0] || {};
|
||
const taskTime = typeof first.taskTime === 'number' ? first.taskTime : 0;
|
||
const unit = timeTypeTransform(first.timeType);
|
||
const isToday = (first.timeType === 'day' || !first.timeType) && taskTime === 0;
|
||
const offsetText = isToday ? '当天' : `${taskTime}${unit}`;
|
||
const dateText = baseDate ? dayTransform(baseDate, taskTime, first.timeType) : '';
|
||
return {
|
||
id: key,
|
||
title: `计划开始后: ${offsetText}${dateText ? `(${dateText})` : ''}`,
|
||
list,
|
||
};
|
||
});
|
||
});
|
||
</script>
|
||
|
||
<style scoped>
|
||
.group-head {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding: 24rpx 0 20rpx;
|
||
}
|
||
.group-title {
|
||
flex: 1;
|
||
min-width: 0;
|
||
padding-left: 20rpx;
|
||
font-size: 28rpx;
|
||
font-weight: 600;
|
||
color: #111827;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
.group-toggle {
|
||
flex-shrink: 0;
|
||
display: flex;
|
||
align-items: center;
|
||
margin-left: 20rpx;
|
||
}
|
||
.toggle-text {
|
||
font-size: 28rpx;
|
||
color: #6b7280;
|
||
}
|
||
.toggle-icon {
|
||
margin-left: 10rpx;
|
||
}
|
||
.group-body {
|
||
padding-bottom: 20rpx;
|
||
}
|
||
.mt-20 {
|
||
margin-top: 20rpx;
|
||
}
|
||
</style>
|
||
|