目录
1. 需求说明
实现只展示月份天数的日历,日历保养计划列表不超出固定高度则不显示展开按钮,否则支持展开收起当行。
1.1 效果图:
默认情况下:
点击展开后的效果图:
1.2 难点
一个保养计划可能占两行且适配情况下也不允许判断保养计划数组的长度来显示展开收起按钮,故而需要获取天格子的保养计划内容的容器的 scrollHeight 是否大于 clientHeight,大于则支持展开收起。我这边是把天的格子封装成 PlanContent 组件。
2. 日历
2.1 日历需要的数据
store/modules/pmPlans.js
export default {
namespaced: true,
state: {
pmPlansArr: [
{
"date": 1,
"hiddenMore": true,
"assetPmPlans": [
{
"categoryCode": "YB-ZN-ZFDH-CPFLMV9bKlEC3DHybcgn",
"categoryName": "环境",
"items": [
{
"assetQty": 1,
"period": 1,
"periodName": "周"
}
]
}
]
},
{
"date": 2,
"hiddenMore": true,
"assetPmPlans": [
{
"categoryCode": "YB-ZN-ZFDH-CPFLMV9bKlEC5Tlvx300",
"categoryName": "空调机组",
"items": [
{
"assetQty": 2,
"period": 1,
"periodName": "周"
}
]
}
]
},
{
"date": 3,
"hiddenMore": true,
"assetPmPlans": []
},
{
"date": 4,
"hiddenMore": true,
"assetPmPlans": []
},
{
"date": 5,
"hiddenMore": true,
"assetPmPlans": []
},
{
"date": 6,
"hiddenMore": true,
"assetPmPlans": []
},
{
"date": 7,
"hiddenMore": true,
"assetPmPlans": []
},
{
"date": 8,
"hiddenMore": true,
"assetPmPlans": [
{
"categoryCode": "YB-ZN-ZFDH-CPFLMV9bKlEC3DHybcgn",
"categoryName": "环境",
"items": [
{
"assetQty": 1,
"period": 1,
"periodName": "周"
}
]
}
]
},
{
"date": 9,
"hiddenMore": true,
"assetPmPlans": [
{
"categoryCode": "YB-ZN-ZFDH-CPFLMV9bKlEC5Tlvx300",
"categoryName": "空调机组",
"items": [
{
"assetQty": 2,
"period": 1,
"periodName": "周"
}
]
},
{
"categoryCode": "YB-ZN-ZFDH-CPFLMV9bKlEC1FOgO8bg",
"categoryName": "UPS",
"items": [
{
"assetQty": 1,
"period": 4,
"periodName": "月度"
}
]
}
]
},
{
"date": 10,
"hiddenMore": true,
"assetPmPlans": []
},
{
"date": 11,
"hiddenMore": true,
"assetPmPlans": []
},
{
"date": 12,
"hiddenMore": true,
"assetPmPlans": []
},
{
"date": 13,
"hiddenMore": true,
"assetPmPlans": []
},
{
"date": 14,
"hiddenMore": true,
"assetPmPlans": []
},
{
"date": 15,
"hiddenMore": true,
"assetPmPlans": [
{
"categoryCode": "YB-ZN-ZFDH-CPFLMV9bKlEC3DHybcgn",
"categoryName": "环境",
"items": [
{
"assetQty": 1,
"period": 1,
"periodName": "周"
}
]
}
]
},
{
"date": 16,
"hiddenMore": true,
"assetPmPlans": [
{
"categoryCode": "YB-ZN-ZFDH-CPFLMV9bKlEC3DHybcgn",
"categoryName": "环境",
"items": [
{
"assetQty": 1,
"period": 3,
"periodName": "半月"
}
]
},
{
"categoryCode": "YB-ZN-ZFDH-CPFLMV9bKlEC5Tlvx300",
"categoryName": "空调机组",
"items": [
{
"assetQty": 2,
"period": 1,
"periodName": "周"
}
]
},
{
"categoryCode": "YB-ZN-ZFDH-CPFLMV9bKlEC1FOgO8bg",
"categoryName": "UPS",
"items": [
{
"assetQty": 2,
"period": 3,
"periodName": "半月"
}
]
},
{
"categoryCode": "YB-ZN-ZFDH-CPFLMV9bKlEC1FOgO8bg",
"categoryName": "UPS",
"items": [
{
"assetQty": 1,
"period": 4,
"periodName": "月度"
},
{
"assetQty": 2,
"period": 3,
"periodName": "半月"
}
]
},
{
"categoryCode": "YB-ZN-ZFDH-CPFLMV9bKlEC3DHybcgn",
"categoryName": "环境",
"items": [
{
"assetQty": 1,
"period": 3,
"periodName": "半月"
}
]
},
{
"categoryCode": "YB-ZN-ZFDH-CPFLMV9bKlEC3DHybcgn",
"categoryName": "环境",
"items": [
{
"assetQty": 1,
"period": 1,
"periodName": "周"
}
]
}
]
},
{
"date": 17,
"hiddenMore": true,
"assetPmPlans": []
},
{
"date": 18,
"hiddenMore": true,
"assetPmPlans": []
},
{
"date": 19,
"hiddenMore": true,
"assetPmPlans": []
},
{
"date": 20,
"hiddenMore": true,
"assetPmPlans": []
},
{
"date": 21,
"hiddenMore": true,
"assetPmPlans": []
},
{
"date": 22,
"hiddenMore": true,
"assetPmPlans": [
{
"categoryCode": "YB-ZN-ZFDH-CPFLMV9bKlEC3DHybcgn",
"categoryName": "环境",
"items": [
{
"assetQty": 1,
"period": 1,
"periodName": "周"
}
]
}
]
},
{
"date": 23,
"hiddenMore": true,
"assetPmPlans": [
{
"categoryCode": "YB-ZN-ZFDH-CPFLMV9bKlEC5Tlvx300",
"categoryName": "空调机组",
"items": [
{
"assetQty": 2,
"period": 1,
"periodName": "周"
}
]
}
]
},
{
"date": 24,
"hiddenMore": true,
"assetPmPlans": []
},
{
"date": 25,
"hiddenMore": true,
"assetPmPlans": []
},
{
"date": 26,
"hiddenMore": true,
"assetPmPlans": []
},
{
"date": 27,
"hiddenMore": true,
"assetPmPlans": []
},
{
"date": 28,
"hiddenMore": true,
"assetPmPlans": []
},
{
"date": 29,
"hiddenMore": true,
"assetPmPlans": [
{
"categoryCode": "YB-ZN-ZFDH-CPFLMV9bKlEC3DHybcgn",
"categoryName": "环境",
"items": [
{
"assetQty": 1,
"period": 1,
"periodName": "周"
}
]
}
]
},
{
"date": 30,
"hiddenMore": true,
"assetPmPlans": [
{
"categoryCode": "YB-ZN-ZFDH-CPFLMV9bKlEC5Tlvx300",
"categoryName": "空调机组",
"items": [
{
"assetQty": 2,
"period": 1,
"periodName": "周"
}
]
}
]
},
{
"date": 31,
"hiddenMore": true,
"assetPmPlans": [
{
"categoryCode": "YB-ZN-ZFDH-CPFLMV9bKlEC1FOgO8bg",
"categoryName": "UPS",
"items": [
{
"assetQty": 1,
"period": 4,
"periodName": "月度"
},
{
"assetQty": 2,
"period": 3,
"periodName": "半月"
}
]
},
{
"categoryCode": "YB-ZN-ZFDH-CPFLMV9bKlEC3DHybcgn",
"categoryName": "环境",
"items": [
{
"assetQty": 1,
"period": 3,
"periodName": "半月"
}
]
}
]
}
],
daysInMonth: 31
},
getters: {},
mutations: {},
actions: {}
}
2.2 日历及展开收起逻辑代码
<template>
<!-- 日历 -->
<div class="parent-table">
<div class="content" v-for="(item, i) in pmPlans" :key="i">
<PlanContent :i="i" :items="item" @handleHiddenMore="handleHiddenMore">
</PlanContent>
</div>
</div>
</template>
<script lang="ts" setup>
import { computed, ref } from "vue"
import PlanContent from "./PlanContent.vue";
import { useStore } from 'vuex'
const store = useStore()
const pmPlans = computed(() => store.state.pmPlans.pmPlansArr)
const monthDays = computed(() => store.state.pmPlans.daysInMonth)
// 展开、收起 逻辑
const handleHiddenMore = (i) => {
let lineIndex = Math.floor(i / 7) + 1
console.log(lineIndex);
for (let x = 0; x < 7; x++) {
console.log((lineIndex - 1) * 7 + x, pmPlans.value[(lineIndex - 1) * 7 + x]);
const y = (lineIndex - 1) * 7 + x
if (y > monthDays.value) {
break
}
pmPlans.value[y].hiddenMore = !pmPlans.value[y].hiddenMore
}
}
</script>
<style lang="less" scoped>
.parent-table {
table-layout: fixed;
width: inherit;
display: grid;
grid-template-columns: repeat(7, 1fr);
border-left: 1px solid #ddd;
padding-top: 1PX;
overflow: auto;
padding-bottom: 1PX;
padding-right: 0.5PX;
margin-top: 10px;
.content {
margin: -1px 0 0 -1px;
padding: 6PX 12PX;
box-sizing: border-box;
font-family: "PingFang SC";
border: 1px solid #ddd;
}
}
</style>
2.3 PlanContent 组件
我这边的实际需求是支持从月度计划和人员安排维度查看保养计划的,在切换的情况下有些小圆点a-badge组件被挤成一半,加个左右外边距可解决此问题。
<template>
<div>
<div class="top-day">
{{ item.date }}
<span class="toggle-btn" v-show="isShow" @click="handleHiddenMore">{{ item.hiddenMore ? '展开' :
'收起' }}</span>
</div>
<div class="plans-father" :style="item.hiddenMore ? 'height: 78PX;' : 'height: auto;'">
<div class="plans" ref="contentRef" :style="item.hiddenMore ? 'overflow: hidden;' : 'overflow: visible;'">
<div class="plan-list" v-for="(elem, j) in item.assetPmPlans" :key="j">
<div class="flex">
<a-badge class="badge" color="#04CB67" />
<div class="inline">
<span>{{ elem.categoryName }}</span>
<div class="inline" v-for="(x, y) in elem.items" :key="y">
<span class="asset-num">{{ x.assetQty
}}</span>
<span>台</span>
<span class="period">({{ x.periodName }})</span>
<span v-show="y != elem.items.length - 1">、</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { defineEmits, onMounted, defineProps, nextTick, ref, computed } from "vue"
const props = defineProps({
id: Number,
items: Object,
i: Number
});
const isShow = ref(false)
const contentRef = ref(null);
const item = computed(() => props.items)
onMounted(() => {
nextTick(() => {
isShow.value = contentRef.value.scrollHeight > contentRef.value.clientHeight
})
});
const emit = defineEmits(["handleHiddenMore"]);
const handleHiddenMore = () => {
emit("handleHiddenMore", props.i)
}
</script>
<style scoped lang="less">
.top-day {
position: relative;
text-align: center;
color: #000;
font-size: 24PX;
font-weight: 500;
line-height: 34PX;
vertical-align: middle;
font-family: "PingFang SC";
}
.plans {
height: inherit;
font-family: "PingFang SC";
.plan-list:last-child {
padding-bottom: 0;
.period {
color: #8C8C8C;
}
}
.plan-list {
text-align: left;
display: flex;
color: #262626;
font-size: 12PX;
line-height: 24PX;
.flex {
display: flex;
}
.pm-plans,
.inline {
display: inline;
}
.asset-num {
padding: 0 2px;
cursor: pointer;
font-weight: 500;
}
}
}
</style>