效果图如下
父组件
<template>
<div class="mainBox">
<div v-for="(item, index) in titleList" :key="index" class="contentBox">
<div class="title">{{ item }}</div>
<div class="childTotalBox">
<childData
v-if="item === '项目人员情况' && personValue"
:item="item"
:contentList="contentList"
@dataClick="dataClick"
:personValue="personValue"
/>
<childData
v-else-if="item === '劳务人员情况' && personValue"
:item="item"
:contentList="contentListTwo"
@dataClick="dataClick"
:personValue="personValue"
/>
<childData
v-else
:item="item"
:contentList="contentListThree"
@dataClick="dataClick"
:personValue="personValue"
/>
</div>
</div>
</div>
<DXDialog
:title="titles"
:visible="visible"
:showBoxShadow="false"
:destroyOnClose="false"
:maskClosed="false"
@close="close"
width="60vw"
height="75vh"
top="12vh"
>
<el-col h-68.8vh pos="relative" class="tableBox">
<Table :columnData="columnData" :list="personList" :person="false" />
</el-col>
</DXDialog>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import Table from '@daxiedongk/views/LodarManagement/compontents/Table.vue';
import DXDialog from '@daxiedongk/components/DXDialog/DXDialog.vue';
import childData from '@daxiedongk/views/LodarManagement/compontents/ProPerson/componemts/childData.vue';
import {
columnData,
contentList,
contentListThree,
contentListTwo,
titleList,
} from '@daxiedongk/views/LodarManagement/hook/mockDataShowData.ts';
import { getContractWorkerList } from '@daxiedongk/api/loaderManagement';
const visible = ref(false);
const titles = ref('');
const personList = reactive({
type: '',
peopleDetailType: '',
});
const dataClick = (item, data) => {
if (data.label === '进场登记') {
titles.value = '登记项目人员数';
} else {
titles.value = `${data.label}人员数`;
}
if (item === '劳务人员情况') {
personList.type = 'service';
} else if (item === '管理人员情况') {
personList.type = 'manage';
} else {
personList.type = 'project';
}
if (data.label === '进场登记') {
personList.peopleDetailType = 'registeredNumber';
} else if (data.label === '今日出勤') {
personList.peopleDetailType = 'attendanceCounts';
} else {
personList.peopleDetailType = 'CurrentNumber';
}
visible.value = true;
};
const personValue = ref({
projectRegisteredNumber: 0,
projectAttendanceCounts: 0,
projectCurrentNumber: 0,
projectPercent: 0,
lwRegisteredNumber: 0,
lwAttendanceCounts: 0,
lwCurrentNumber: 0,
lwPercent: 0,
glRegisteredNumber: 0,
glAttendanceCounts: 0,
glCurrentNumber: 0,
glPercent: 0,
});
const getPersonList = () => {
getContractWorkerList({}).then((res) => {
personValue.value = res.data;
});
};
getPersonList();
const close = () => {
visible.value = false;
};
</script>
<style scoped lang="less">
.mainBox {
background: rgba(79, 190, 249, 0.1);
clip-path: polygon(0 5%, 3% 0, 97% 0, 100% 4.8%, 100% 93%, 96% 98%, 4% 98%, 0% 93%);
padding: 10% 4%;
height: 100%;
width: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
.contentBox {
width: 100%;
margin-bottom: 1.8rem;
padding-left: 2.5rem;
.title {
font-size: 1.2rem;
font-weight: 600;
margin-bottom: 1%;
color: #ffffff;
}
.childTotalBox {
height: 70%;
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
}
}
}
.tableBox {
:deep(.el-table) {
.el-table__cell {
padding: 0;
}
.el-table__body {
.el-table__cell {
padding: 0.5rem 0;
}
}
}
}
.el-pagination {
--el-pagination-bg-color: black !important;
--el-pagination-button-disabled-bg-color: black !important;
--el-pagination-hover-color: #ffffff !important;
}
</style>
子组件
<template>
<div class="childDiv">
<div v-for="(it, indexs) in contentList" :key="indexs" class="childBox">
<div class="iconBox">
<img :src="it.iconBox" style="width: 100%; height: 100%" alt="" />
</div>
<div class="childTextBox">
<div
@click="it.label !== '出勤率' ? dataClick(item, it) : ''"
cursor-pointer
class="data-show"
>{{ it.label !== '出勤率' ? `${it.label}人数` : it.label }}</div
>
<div v-if="personValue && it.label === '出勤率'">
<NumericalIncrement
:duration="2"
:is-decimal="true"
:value="`${personValue[it.value]}`"
class="num"
style="min-width: 0"
></NumericalIncrement>
<span style="font-size: 0.75rem" ml="0.2">%</span>
</div>
<NumericalIncrement
v-if="personValue && it.label !== '出勤率'"
:duration="2"
:is-decimal="true"
:value="personValue[it.value]"
class="num"
></NumericalIncrement>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import NumericalIncrement from '@daxiedongk/views/LodarManagement/compontents/ProPerson/componemts/NumericalIncrement.vue';
import { onMounted, ref } from 'vue';
defineProps<{
contentList: {
iconBox: string;
label: string;
value: number;
}[];
item: string;
personValue: Record<string, any>;
}>();
const emit = defineEmits(['dataClick']);
const val = ref(0);
onMounted(() => {
setTimeout(() => {
val.value = 200;
}, 3000);
});
const dataClick = (item, it) => {
emit('dataClick', item, it);
};
</script>
<style scoped>
.num {
min-width: 40px;
font-size: 1.2rem;
color: #fff;
}
.childDiv {
height: 70%;
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
.childBox {
width: 25%;
height: 100%;
display: flex;
align-items: center;
.iconBox {
width: 2rem;
height: 2rem;
background-color: rgb(59 130 246 / 0.5);
border-radius: 50%;
margin-right: 5%;
}
.childTextBox {
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
.data-show {
font-size: 1rem;
color: #7699b4;
}
.data-show:hover {
color: #ffffff;
}
}
}
}
</style>
孙子组件
第一种写法
<template>
<div>
<span ref="numberDom">0</span>
</div>
</template>
<script setup lang="ts">
import { defineProps, onBeforeUnmount, onBeforeUpdate, onMounted, ref, withDefaults } from 'vue';
/**
* @param value 数值大小
* @param duration 递增动画持续时间;
* @param isDecimal 是否显示为小数
*/
const props = withDefaults(
defineProps<{
value: number | string;
duration: number;
isDecimal: boolean;
}>(),
{
duration: 2,
isDecimal: false,
}
);
let timer: number | null = null;
const timerDelay = 5;
const numberDom = ref<any>(null);
onMounted(() => {
numericalIncrement(numberDom.value);
});
onBeforeUpdate(() => {
if (timer) {
clearInterval(timer!);
timer = null;
}
numericalIncrement(numberDom.value);
});
onBeforeUnmount(() => {
if (timer) {
clearInterval(timer!);
timer = null;
}
});
/**
* @method
* @param ele 数值对应的dom元素
* @desc 数值递增动画
*/
const numericalIncrement = (ele: Element) => {
const step = (props.value * timerDelay) / (props.duration * 1000);
let current: number = 0;
let start: number = 0;
let flag: boolean = false;
timer = setInterval(() => {
start += step;
if (start >= props.value) {
flag = props.isDecimal;
clearInterval(timer!);
start = props.value;
timer = null;
}
current = start;
if (flag) {
ele.innerHTML = current.toString().replace(/(\d)(?=(?:\d{3}[+]?)+$)/g, '$1,');
} else {
ele.innerHTML = current
.toFixed(0)
.toString()
.replace(/(\d)(?=(?:\d{3}[+]?)+$)/g, '$1,');
}
}, timerDelay);
};
</script>
<style scoped>
div {
display: inline-block;
}
</style>
第二种写法
<script setup lang="ts">
const props = defineProps<{}>();
const dataValue = computed(() => props.value ?? 0);
const counter = useTransition(dataValue, {
duration: 1500,
});
</script>
<template>
<el-statistic :value="counter" :value-style="{ color: '#ffffff' }" />
</template>
<style scoped lang="less"></style>