feat: 动画优化

master
xjs 2025-05-27 12:01:37 +08:00
parent 21da1b661a
commit 545731d6d5
22 changed files with 422 additions and 505 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

View File

@ -86,6 +86,7 @@ const percentage = computed(() => {
align-items: center;
justify-content: center;
box-shadow: 0 0 10px 1px var(--progress-start-color);
z-index: auto;
//
&::before {
@ -98,7 +99,7 @@ const percentage = computed(() => {
background-image: v-bind('props.striped ? "linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent)" : "none"');
background-size: 40px 40px;
opacity: 0.6;
z-index: 1;
z-index: auto;
animation: v-bind('props.animated && props.striped ? "progress-animation 2s linear infinite" : "none"');
}
}

View File

@ -213,7 +213,7 @@ onUnmounted(() => {
position: absolute;
background-color: var(--color-led-off);
border-radius: var(--segment-spacing);
z-index: auto;
&-on {
background-color: var(--color-led);
// box-shadow: 0 0 var(--glow-radius) var(--color-glow);

View File

@ -1,3 +1,15 @@
.text-color{
background: linear-gradient(90deg, #8FC8FF 0%, #FFFFFF 100%);
position: relative;
color: #8FC8FF;
&::after {
content: attr(data-text);
position: absolute;
top: 0;
left: 0;
z-index: auto;
color: #fff;
-webkit-mask: linear-gradient(to bottom, transparent, #000);
white-space: nowrap;
padding-right: 4px;
}
}

View File

@ -1,62 +1,62 @@
<template>
<div class="h-[358px] relative bg-[#082059]">
<div class="flex h-full custom-border absolute top-0 left-0">
<div class="relative h-[36px]">
<div class="absolute top-[50%] translate-y-[-50%] left-[15px] flex items-center">
<SvgComponent :content="arrowLeftSvg" class="w-[15px] h-[18px]" />
<div class="bg-gradient-to-b from-[#8FC8FF] to-white bg-clip-text text-transparent text-[20px] ml-[9px] font-700">咨询学段<span class="text-[14px]">(已标记)</span></div>
</div>
<SvgComponent :content="headerLeftSvg" class="w-[255px] h-[36px]" />
</div>
<SvgComponent :content="headerRightSvg" class="w-[669px] h-[36px]" />
</div>
<div class="w-full h-[calc(100%-36px)] mt-[36px]">
<AskSectionChart :chart-data="askSectionData.stages || []" />
<div class="h-[358px] relative bg-[#082059]">
<div class="flex h-full custom-border absolute top-0 left-0">
<SvgComponent :content="headerLeftSvg" class="w-[255px] h-[36px]" />
<SvgComponent :content="headerRightSvg" class="w-[669px] h-[36px]" />
<div class="absolute top-[2px] left-[15px] flex items-center">
<SvgComponent :content="arrowLeftSvg" class="w-[15px] h-[18px]" />
<div class="ml-[9px] font-700 flex items-baseline">
<span class="text-[20px] text-color" data-text="">咨询学段</span>
<span class="text-[14px] text-color" data-text="()">(已标记)</span>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import SvgComponent from "@/components/SvgComponent.vue";
import AskSectionChart from "@/views/components/chartsComponents/AskSectionChart.vue";
const headerLeftSvg = ref("");
const headerRightSvg = ref("");
const getHeaderLeftSvg = async () => {
const { default: svg } = await import("/src/assets/svg-img/header-bg-left.svg?raw");
headerLeftSvg.value = svg;
};
const getHeaderRightSvg = async () => {
const { default: svg } = await import("/src/assets/svg-img/header-bg-right.svg?raw");
headerRightSvg.value = svg;
};
const arrowLeftSvg = ref("");
const getArrowLeftSvg = async () => {
const { default: svg } = await import("/src/assets/svg-img/arrow-left.svg?raw");
arrowLeftSvg.value = svg;
};
<div class="w-full h-[calc(100%-36px)] mt-[36px]">
<AskSectionChart :chart-data="askSectionData.stages || []" />
</div>
</div>
</template>
const askSectionData = inject("askSectionData",ref({stages:[]}))
<script setup lang="ts">
import SvgComponent from "@/components/SvgComponent.vue";
import AskSectionChart from "@/views/components/chartsComponents/AskSectionChart.vue";
onBeforeMount(() => {
getHeaderLeftSvg();
getHeaderRightSvg();
getArrowLeftSvg();
});
</script>
<style scoped lang="scss">
.custom-border {
border-image-slice: 27 27 27 27;
border-image-width: 1px 1px 2px 1px;
border-image-outset: 0px 0px 0px 0px;
border-image-repeat: stretch stretch;
border-image-source: url("src/assets/svg-img/border-image.png");
border-style: solid;
}
</style>
const headerLeftSvg = ref("");
const headerRightSvg = ref("");
const getHeaderLeftSvg = async () => {
const { default: svg } = await import("/src/assets/svg-img/header-bg-left.svg?raw");
headerLeftSvg.value = svg;
};
const getHeaderRightSvg = async () => {
const { default: svg } = await import("/src/assets/svg-img/header-bg-right.svg?raw");
headerRightSvg.value = svg;
};
const arrowLeftSvg = ref("");
const getArrowLeftSvg = async () => {
const { default: svg } = await import("/src/assets/svg-img/arrow-left.svg?raw");
arrowLeftSvg.value = svg;
};
const askSectionData = inject("askSectionData", ref({ stages: [] }));
onBeforeMount(() => {
getHeaderLeftSvg();
getHeaderRightSvg();
getArrowLeftSvg();
});
</script>
<style scoped lang="scss">
@use "@/styles/text-color.scss";
.custom-border {
border-image-slice: 27 27 27 27;
border-image-width: 1px 1px 2px 1px;
border-image-outset: 0px 0px 0px 0px;
border-image-repeat: stretch stretch;
border-image-source: url("src/assets/svg-img/border-image.png");
border-style: solid;
}
</style>

View File

@ -1,108 +0,0 @@
<template>
<div class="border-container">
<div class="border-glow"></div>
<div class="border-inner p-[2px]">
<slot></slot>
</div>
</div>
</template>
<script>
export default {
name: "BackgroundColor",
data() {
return {
animationFrame: null,
};
},
mounted() {
this.startAnimation();
},
beforeUnmount() {
if (this.animationFrame) {
cancelAnimationFrame(this.animationFrame);
}
},
methods: {
startAnimation() {
const borderGlow = document.querySelector(".border-glow");
let angle = 0;
const animate = () => {
angle = (angle + 1) % 360;
if (borderGlow) {
borderGlow.style.background = `conic-gradient(
from ${angle}deg,
hsla(0, 0%, 0%, 0.25),
hsla(214, 60%, 34%, 0.2),
hsla(220, 74%, 35%, 0.1),
hsla(219, 100%, 58%, 0.1),
hsla(219, 100%, 58%, 0.2),
hsla(219, 100%, 58%, 0.5),
hsla(219, 100%, 58%, 0)
)`;
}
this.animationFrame = requestAnimationFrame(animate);
};
animate();
},
},
};
</script>
<style scoped>
.border-container {
position: relative;
border-radius: 4px;
overflow: hidden;
}
.border-glow {
position: absolute;
top: -2px;
left: -2px;
right: -2px;
bottom: -2px;
width: auto;
height: auto;
background: conic-gradient(
from 0deg,
hsla(0, 0%, 0%, 0.25),
hsla(214, 60%, 34%, 0.2),
hsla(220, 74%, 35%, 0.1),
hsla(219, 100%, 58%, 0.1),
hsla(219, 100%, 58%, 0.2),
hsla(219, 100%, 58%, 0.5),
hsla(219, 100%, 58%, 0)
);
border-radius: inherit;
filter: blur(2px);
pointer-events: none;
mix-blend-mode: plus-lighter;
z-index: 0;
}
.border-inner {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
width: 100%;
height: 100%;
border: 0.5px solid hsla(236, 100%, 66%, 0.48);
border-radius: inherit;
pointer-events: none;
box-sizing: border-box;
}
/* 内容区域样式 */
.border-container > :not(.border-glow):not(.border-inner) {
position: relative;
z-index: 1;
height: 100%;
width: 100%;
box-sizing: border-box;
}
</style>

View File

@ -1,17 +1,15 @@
<template>
<div class="w-[296px] h-[320px] relative bg-[#082059]">
<div class="h-[320px] relative bg-[#082059]">
<div class="flex h-full custom-border absolute top-0 left-0">
<div class="relative h-[36px]">
<div class="absolute top-[50%] translate-y-[-50%] left-[15px] flex items-center">
<SvgComponent :content="arrowLeftSvg" class="w-[15px] h-[18px]" />
<div class="bg-gradient-to-b from-[#8FC8FF] to-white bg-clip-text text-transparent text-[20px] ml-[9px] font-700">收费排行榜</div>
</div>
<SvgComponent :content="headerLeftSvg" class="w-[188px] h-[36px]" />
</div>
<SvgComponent :content="headerLeftSvg" class="w-[188px] h-[36px]" />
<SvgComponent :content="headerRightSvg" class="w-[108px] h-[36px]" />
<div class="absolute top-[2px] left-[15px] flex items-center">
<SvgComponent :content="arrowLeftSvg" class="w-[15px] h-[18px]" />
<div class="text-color text-[20px] ml-[9px] font-700" data-text="">收费排行榜</div>
</div>
</div>
<div class="w-full h-[calc(100%-36px)] mt-[36px] flex flex-col relative">
<div class="absolute right-[0] flex items-center justify-end mr-[13px] transform-translate-y-[-50%]">
<div class="w-[296px] h-[calc(100%-36px)] mt-[36px] flex flex-col">
<div class="flex items-center justify-end mr-[13px] transform-translate-y-[-50%] h-0">
<div class="text-[15px] text-[#84E8FF]">更多</div>
<SvgComponent :content="moreArrowSvg" class="w-[14px] h-[22px]" />
</div>
@ -55,15 +53,15 @@
watch(
() => chargingRankingData.value,
() => {
initData()
initData();
},
);
const initData = () => {
if(chargingRankingData.value.paymentRanks){
products.value = chargingRankingData.value.paymentRanks
}
}
if (chargingRankingData.value.paymentRanks) {
products.value = chargingRankingData.value.paymentRanks;
}
};
const headerLeftSvg = ref("");
const headerRightSvg = ref("");
@ -124,6 +122,7 @@
</script>
<style scoped lang="scss">
@use "@/styles/text-color.scss";
.custom-border {
border-image-slice: 27 27 27 27;
border-image-width: 1px 1px 2px 1px;

View File

@ -5,9 +5,9 @@
<div class="w-[144px]">
<div class="relative w-[144px] h-[113px] flex items-center flex-col">
<div class="text-[#44C1EF] italic text-[20px] font-700">今日获客</div>
<div class="bg-gradient-to-b from-[#8FC8FF] to-white bg-clip-text text-transparent mb-[15px] z-10 font-700">
<span class="text-[34px] italic">{{ gainData.toDayAcq || 0 }}</span>
<span class="text-[18px]"></span>
<div class="leading-[1] flex items-baseline mb-[15px] z-10 font-700">
<span class="text-[34px] italic text-color" :data-text="gainData.toDayAcq || 0">{{ gainData.toDayAcq || 0 }}</span>
<span class="text-[18px] text-color" data-text=""></span>
</div>
<SvgComponent :content="hexagonalBoxSvg" class="box-light absolute" />
</div>

View File

@ -5,9 +5,9 @@
<div class="w-[144px]">
<div class="relative w-[144px] h-[113px] flex items-center flex-col">
<div class="text-[#44C1EF] italic text-[20px] font-700">总获客</div>
<div class="bg-gradient-to-b from-[#8FC8FF] to-white bg-clip-text text-transparent mb-[15px] z-10 font-700">
<span class="text-[34px] italic">{{gainData.acqTotal}}</span>
<span class="text-[18px]"></span>
<div class="leading-[1] flex items-baseline mb-[15px] z-10 font-700">
<span class="text-[34px] italic text-color" :data-text="gainData.acqTotal || 0">{{gainData.acqTotal || 0}}</span>
<span class="text-[18px] text-color" data-text=""></span>
</div>
<SvgComponent :content="hexagonalBoxSvg" class="box-light absolute" />
</div>

View File

@ -5,9 +5,9 @@
<div class="w-[144px]">
<div class="relative w-[144px] h-[113px] flex items-center flex-col">
<div class="text-[#44C1EF] italic text-[20px] font-700">今日流失</div>
<div class="bg-gradient-to-b from-[#8FC8FF] to-white bg-clip-text text-transparent mb-[15px] z-10 font-700">
<span class="text-[34px] italic">{{ gainData.toDayOutFlow || 0 }}</span>
<span class="text-[18px]"></span>
<div class="leading-[1] flex items-baseline mb-[15px] z-10 font-700">
<span class="text-[34px] italic text-color" :data-text="gainData.toDayOutFlow || 0">{{ gainData.toDayOutFlow || 0 }}</span>
<span class="text-[18px] text-color" data-text=""></span>
</div>
<SvgComponent :content="hexagonalBoxSvg" class="box-light absolute" />
</div>

View File

@ -1,25 +1,20 @@
<template>
<div class="w-[296px] h-[320px] relative bg-[#082059]">
<div class="flex h-full custom-border absolute top-0 left-0">
<div class="relative h-[36px]">
<div class="absolute top-[50%] translate-y-[-50%] left-[15px] flex items-center">
<SvgComponent :content="arrowLeftSvg" class="w-[15px] h-[18px]" />
<div class="bg-gradient-to-b from-[#8FC8FF] to-white bg-clip-text text-transparent text-[20px] ml-[9px] font-700">线下详情</div>
</div>
<SvgComponent :content="headerLeftSvg" class="w-[188px] h-[36px]" />
</div>
<SvgComponent :content="headerLeftSvg" class="w-[188px] h-[36px]" />
<SvgComponent :content="headerRightSvg" class="w-[108px] h-[36px]" />
<div class="absolute top-[2px] left-[15px] flex items-center">
<SvgComponent :content="arrowLeftSvg" class="w-[15px] h-[18px]" />
<div class="text-[20px] ml-[9px] font-700 text-color" data-text="线">线下详情</div>
</div>
</div>
<div class="w-full h-[calc(100%-36px)] mt-[36px] flex flex-col">
<div class="ml-[22px] relative mt-[13px]">
<div class="flex items-center justify-center">
<ProportionCharts :chart-data="chartData" class="z-2 relative" />
<SvgComponent :content="paymentChartSvg" class="w-[143px] h-[143px] absolute top-0 left-[50%] z-1 transform-translate-x-[-50%]" />
</div>
<div class="ml-[22px] relative mt-[13px] flex items-center justify-center">
<ProportionCharts :chart-data="chartData" class="" />
<div class="leading-[1] absolute top-[50%] left-[50%] translate-x-[-50%] translate-y-[-50%] z-3 flex items-center flex-col font-700 italic">
<div class="bg-gradient-to-b from-[#8FC8FF] to-white bg-clip-text text-transparent flex items-baseline">
<div class="text-[25px]">{{ offlineTotal }}</div>
<div class="text-[20px]"></div>
<div class="flex items-baseline">
<div class="text-[25px] text-color" :data-text="offlineTotal">{{ offlineTotal }}</div>
<div class="text-[20px] text-color" data-text=""></div>
</div>
<div class="text-[#FFFFFF] text-[16px] text-shadow-[0px_2px_2px_rgba(12,32,72,0.42)] mt-[7px]">线下</div>
</div>
@ -57,11 +52,6 @@
headerRightSvg.value = svg;
};
const paymentChartSvg = ref("");
const getPaymentChartSvg = async () => {
const { default: svg } = await import("/src/assets/svg-img/payment-chart.svg?raw");
paymentChartSvg.value = svg;
};
const arrowLeftSvg = ref("");
const getArrowLeftSvg = async () => {
@ -98,13 +88,13 @@
getHeaderLeftSvg();
getHeaderRightSvg();
getArrowLeftSvg();
getPaymentChartSvg();
initData();
});
</script>
<style scoped lang="scss">
@use "@/styles/custom-border.scss";
@use "@/styles/text-color.scss";
.custom-border {
border-image-slice: 27 27 27 27;

View File

@ -1,25 +1,20 @@
<template>
<div class="w-[296px] h-[320px] relative bg-[#082059]">
<div class="flex h-full custom-border absolute top-0 left-0">
<div class="relative h-[36px]">
<div class="absolute top-[50%] translate-y-[-50%] left-[15px] flex items-center">
<SvgComponent :content="arrowLeftSvg" class="w-[15px] h-[18px]" />
<div class="bg-gradient-to-b from-[#8FC8FF] to-white bg-clip-text text-transparent text-[20px] ml-[9px] font-700">线上详情</div>
</div>
<SvgComponent :content="headerLeftSvg" class="w-[188px] h-[36px]" />
<SvgComponent :content="headerLeftSvg" class="w-[188px] h-[36px]" />
<div class="absolute top-[2px] left-[15px] flex items-center">
<SvgComponent :content="arrowLeftSvg" class="w-[15px] h-[18px]" />
<div class="text-color text-[20px] ml-[9px] font-700" data-text="线">线上详情</div>
</div>
<SvgComponent :content="headerRightSvg" class="w-[108px] h-[36px]" />
</div>
<div class="w-full h-[calc(100%-36px)] mt-[36px] flex flex-col">
<div class="ml-[22px] relative mt-[13px]">
<div class="flex items-center justify-center">
<ProportionCharts :chart-data="chartData" class="z-2 relative" />
<SvgComponent :content="paymentChartSvg" class="w-[143px] h-[143px] absolute top-0 left-[50%] z-1 transform-translate-x-[-50%]" />
</div>
<div class="ml-[22px] relative mt-[13px] flex items-center justify-center">
<ProportionCharts :chart-data="chartData" class="" />
<div class="leading-[1] absolute top-[50%] left-[50%] translate-x-[-50%] translate-y-[-50%] z-3 flex items-center flex-col font-700 italic">
<div class="bg-gradient-to-b from-[#8FC8FF] to-white bg-clip-text text-transparent flex items-baseline">
<div class="text-[25px]">{{ onlineTotal }}</div>
<div class="text-[20px]"></div>
<div class="flex items-baseline">
<div class="text-[25px] text-color" :data-text="onlineTotal">{{ onlineTotal }}</div>
<div class="text-[20px] text-color" data-text=""></div>
</div>
<div class="text-[#FFFFFF] text-[16px] text-shadow-[0px_2px_2px_rgba(12,32,72,0.42)] mt-[7px]">线上</div>
</div>
@ -57,11 +52,6 @@
headerRightSvg.value = svg;
};
const paymentChartSvg = ref("");
const getPaymentChartSvg = async () => {
const { default: svg } = await import("/src/assets/svg-img/payment-chart.svg?raw");
paymentChartSvg.value = svg;
};
const arrowLeftSvg = ref("");
const getArrowLeftSvg = async () => {
@ -98,7 +88,6 @@
getHeaderLeftSvg();
getHeaderRightSvg();
getArrowLeftSvg();
getPaymentChartSvg();
initData();
});
@ -106,6 +95,7 @@
<style scoped lang="scss">
@use "@/styles/custom-border.scss";
@use "@/styles/text-color.scss";
.custom-border {
border-image-slice: 27 27 27 27;

View File

@ -1,14 +1,12 @@
<template>
<div class="h-[358px] relative bg-[#082059]">
<div class="flex h-full custom-border absolute top-0 left-0">
<div class="relative h-[36px]">
<div class="absolute top-[50%] translate-y-[-50%] left-[15px] flex items-center">
<SvgComponent :content="arrowLeftSvg" class="w-[15px] h-[18px]" />
<div class="bg-gradient-to-b from-[#8FC8FF] to-white bg-clip-text text-transparent text-[20px] ml-[9px] font-700">经营趋势</div>
</div>
<SvgComponent :content="headerLeftSvg" class="w-[255px] h-[36px]" />
</div>
<SvgComponent :content="headerLeftSvg" class="w-[255px] h-[36px]" />
<SvgComponent :content="headerRightSvg" class="w-[669px] h-[36px]" />
<div class="absolute top-[2px] left-[15px] flex items-center flex leading-[30px] items-baseline">
<SvgComponent :content="arrowLeftSvg" class="w-[15px] h-[18px]" />
<div class="text-[20px] ml-[9px] font-700 text-color" data-text="">经营趋势</div>
</div>
</div>
<div class="w-full h-[calc(100%-36px)] mt-[36px]">
<OperatingTrendsChart :chartDataArray="acqTrend" @date-change="handleDateChange" />
@ -70,6 +68,8 @@
</script>
<style scoped lang="scss">
@use "@/styles/text-color.scss";
.custom-border {
border-image-slice: 27 27 27 27;
border-image-width: 1px 1px 2px 1px;

View File

@ -5,9 +5,9 @@
<div class="w-[216px]">
<div class="relative w-[154px] h-[124px] flex items-center flex-col">
<div class="text-[#44C1EF] italic text-[20px] font-700">总缴费</div>
<div class="bg-gradient-to-b from-[#8FC8FF] to-white bg-clip-text text-transparent mb-[15px] z-10 font-700">
<span class="text-[40px] italic">{{paymentData.chargeTotal}}</span>
<span class="text-[20px]"></span>
<div class="mb-[15px] z-10 font-700 leading-[1] flex items-baseline">
<span class="text-[40px] italic text-color" :data-text="`${paymentData.chargeTotal || 0}`">{{paymentData.chargeTotal ||0}}</span>
<span class="text-[20px] text-color" data-text=""></span>
</div>
<SvgComponent :content="lightningBoxSvg" class="box-light absolute" />
</div>
@ -24,8 +24,7 @@
<YProgress :percentage="Math.round(paymentData.chargeTotal/paymentData.estimatedTotal * 100)" height="12px" class="mt-[7px]" />
</div>
<div class="ml-[4px] relative w-[143px] h-[143px]">
<ProportionCharts :chart-data="chartData" class="z-2 relative" />
<SvgComponent :content="paymentChartSvg" class="w-[143px] h-[143px] absolute top-0 left-0 z-1" />
<ProportionCharts :chart-data="chartData" class="" />
<div
class="text-[18px] text-[#fff] font-700 text-shadow-[0_0_10px_rgba(12,32,72,0.42)] italic absolute top-[50%] left-[50%] translate-x-[-50%] translate-y-[-50%] z-3">
缴费
@ -78,16 +77,10 @@
lightningBoxSvg.value = svg;
};
const paymentChartSvg = ref("");
const getPaymentChartSvg = async () => {
const { default: svg } = await import("/src/assets/svg-img/payment-chart.svg?raw");
paymentChartSvg.value = svg;
};
onBeforeMount(() => {
getGroupBackgroundSvg();
getLightningBoxSvg();
getPaymentChartSvg();
});
</script>

View File

@ -1,27 +1,25 @@
<template>
<div class="w-[296px] h-[320px] relative bg-[#082059]">
<div class="flex h-full custom-border absolute top-0 left-0">
<div class="relative h-[36px]">
<div class="absolute top-[50%] translate-y-[-50%] left-[15px] flex items-center">
<SvgComponent :content="arrowLeftSvg" class="w-[15px] h-[18px]" />
<div class="bg-gradient-to-b from-[#8FC8FF] to-white bg-clip-text text-transparent text-[20px] ml-[9px] font-700">六纬志愿</div>
</div>
<SvgComponent :content="headerLeftSvg" class="w-[188px] h-[36px]" />
</div>
<SvgComponent :content="headerLeftSvg" class="w-[188px] h-[36px]" />
<SvgComponent :content="headerRightSvg" class="w-[108px] h-[36px]" />
<div class="absolute top-[2px] left-[15px] flex items-center">
<SvgComponent :content="arrowLeftSvg" class="w-[15px] h-[18px]" />
<div class="text-[20px] ml-[9px] font-700 text-color" data-text="">六纬志愿</div>
</div>
</div>
<div class="w-full h-[calc(100%-36px)] mt-[36px] flex flex-col relative">
<div class="absolute right-[0] flex items-center justify-end mr-[13px] transform-translate-y-[-50%]">
<div class="w-full h-[calc(100%-36px)] mt-[36px] flex flex-col">
<div class="flex items-center justify-end mr-[13px] transform-translate-y-[-50%] h-0">
<div class="text-[15px] text-[#84E8FF]">更多</div>
<SvgComponent :content="moreArrowSvg" class="w-[14px] h-[22px]"/>
<SvgComponent :content="moreArrowSvg" class="w-[14px] h-[22px]" />
</div>
<div class="flex items-baseline leading-[1] mt-[29px] mx-[27px]">
<div class="text-[#44C1EF] italic text-[20px] font-700">总获客</div>
<div class="bg-gradient-to-b from-[#8FC8FF] to-white bg-clip-text text-transparent text-[40px] font-700 italic pr-[4px]">{{ sixStatisticsData.total || 0 }}</div>
<div class="bg-gradient-to-b from-[#8FC8FF] to-white bg-clip-text text-transparent text-[18px] font-700"></div>
<div class="text-[40px] font-700 italic pr-[4px] text-color" :data-text="sixStatisticsData.total || 0">{{ sixStatisticsData.total || 0 }}</div>
<div class="text-[18px] font-700 text-color" data-text=""></div>
<SvgIcon name="arrow-up" class="text-[9px] text-[#4AFFA2] ml-[9px]" />
</div>
<SixStatisticsChart :chartData="sixStatisticsData.items ||[]"/>
<SixStatisticsChart :chartData="sixStatisticsData.items || []" />
</div>
</div>
</template>
@ -56,7 +54,7 @@
moreArrowSvg.value = svg;
};
const sixStatisticsData = inject("sixStatisticsData",ref<{total:number,items:{data:string,total:number}[]}>({total:0,items:[]}))
const sixStatisticsData = inject("sixStatisticsData", ref<{ total: number; items: { data: string; total: number }[] }>({ total: 0, items: [] }));
onBeforeMount(() => {
getHeaderLeftSvg();
@ -68,6 +66,7 @@
</script>
<style scoped lang="scss">
@use "@/styles/text-color.scss";
.custom-border {
border-image-slice: 27 27 27 27;
border-image-width: 1px 1px 2px 1px;

View File

@ -1,41 +1,39 @@
<template>
<div class="w-[296px] h-[320px] relative bg-[#082059]">
<div class="flex h-full custom-border absolute top-0 left-0">
<div class="relative h-[36px]">
<div class="absolute top-[50%] translate-y-[-50%] left-[15px] flex items-center">
<SvgComponent :content="arrowLeftSvg" class="w-[15px] h-[18px]" />
<div class="bg-gradient-to-b from-[#8FC8FF] to-white bg-clip-text text-transparent text-[20px] ml-[9px] font-700">学生来源</div>
</div>
<SvgComponent :content="headerLeftSvg" class="w-[188px] h-[36px]" />
</div>
<SvgComponent :content="headerLeftSvg" class="w-[188px] h-[36px]" />
<SvgComponent :content="headerRightSvg" class="w-[108px] h-[36px]" />
<div class="absolute top-[2px] left-[15px] flex items-center">
<SvgComponent :content="arrowLeftSvg" class="w-[15px] h-[18px]" />
<div class="text-color text-[20px] ml-[9px] font-700" data-text="">学生来源</div>
</div>
</div>
<div class="w-full h-[calc(100%-36px)] mt-[36px] flex flex-col">
<StudentSourceChart class="w-full h-full" :chartData="chartData" :ringSize="0.8" />
<div class="flex items-center justify-between mx-[20px] my-[33px]">
<div class="flex items-center">
<div class="flex flex-col ml-[9px] items-center justify-center">
<div class="bg-gradient-to-b from-[#8FC8FF] to-white bg-clip-text text-transparent font-700">
<span class="text-[18px]">{{ onlineTotal }}</span>
<span class="text-[14px]"></span>
<div class="font-700 flex items-baseline">
<span class="text-[18px] text-color" :data-text="onlineTotal">{{ onlineTotal }}</span>
<span class="text-[14px] text-color" data-text=""></span>
</div>
<span class="text-[#C7F0FF] text-[12px]">线上来源</span>
</div>
</div>
<div class="flex items-center">
<div class="flex flex-col ml-[9px] items-center justify-center">
<div class="bg-gradient-to-b from-[#8FC8FF] to-white bg-clip-text text-transparent font-700">
<span class="text-[18px]">{{ offlineTotal }}</span>
<span class="text-[14px]"></span>
<div class="font-700 flex items-baseline">
<span class="text-[18px] text-color" :data-text="offlineTotal">{{ offlineTotal }}</span>
<span class="text-[14px] text-color" data-text=""></span>
</div>
<span class="text-[#C7F0FF] text-[12px]">线下来源</span>
</div>
</div>
<div class="flex items-center">
<div class="flex flex-col ml-[9px] items-center justify-center">
<div class="bg-gradient-to-b from-[#8FC8FF] to-white bg-clip-text text-transparent font-700">
<span class="text-[18px]">{{ unMarkTotal }}</span>
<span class="text-[14px]"></span>
<div class="font-700 flex items-baseline">
<span class="text-[18px] text-color" :data-text="unMarkTotal">{{ unMarkTotal }}</span>
<span class="text-[14px] text-color" data-text=""></span>
</div>
<span class="text-[#C7F0FF] text-[12px]">未标记</span>
</div>
@ -80,36 +78,35 @@
};
const askSectionData = inject("askSectionData", ref<{ online: any[]; offline: any[] }>({ online: [], offline: [] }));
const gainData = inject("gainData",ref({acqTotal:0}))
const gainData = inject("gainData", ref({ acqTotal: 0 }));
const onlineTotal = ref(0);
const offlineTotal = ref(0);
const unMarkTotal = ref(0)
const unMarkTotal = ref(0);
const chartData = ref<any[]>([]);
watch(
() => askSectionData.value,
() => {
initData()
initData();
},
);
const initData = () => {
if (askSectionData.value.online && askSectionData.value.online.length > 0) {
onlineTotal.value = askSectionData.value.online.reduce((acc, curr) => acc + curr.total, 0);
}
if (askSectionData.value.offline && askSectionData.value.offline.length > 0) {
offlineTotal.value = askSectionData.value.offline.reduce((acc, curr) => acc + curr.total, 0);
}
if(gainData.value.acqTotal && askSectionData.value.offline && askSectionData.value.online){
unMarkTotal.value = gainData.value.acqTotal - offlineTotal.value - onlineTotal.value
}
chartData.value = [
{ name: "线下", value: offlineTotal.value, itemStyle: { color: "rgba(147, 219, 255, 1)" } },
{ name: "线上", value: onlineTotal.value, itemStyle: { color: "rgb(79, 214, 169)" } },
{name:'未标记',value: unMarkTotal.value,itemStyle: { color: "#FF4E4E" }}
];
}
onlineTotal.value = askSectionData.value.online.reduce((acc, curr) => acc + curr.total, 0);
}
if (askSectionData.value.offline && askSectionData.value.offline.length > 0) {
offlineTotal.value = askSectionData.value.offline.reduce((acc, curr) => acc + curr.total, 0);
}
if (gainData.value.acqTotal && askSectionData.value.offline && askSectionData.value.online) {
unMarkTotal.value = gainData.value.acqTotal - offlineTotal.value - onlineTotal.value;
}
chartData.value = [
{ name: "线下", value: offlineTotal.value, itemStyle: { color: "rgba(147, 219, 255, 1)" } },
{ name: "线上", value: onlineTotal.value, itemStyle: { color: "rgb(79, 214, 169)" } },
{ name: "未标记", value: unMarkTotal.value, itemStyle: { color: "#FF4E4E" } },
];
};
onBeforeMount(() => {
getHeaderLeftSvg();
@ -123,6 +120,7 @@
</script>
<style scoped lang="scss">
@use "@/styles/text-color.scss";
.custom-border {
border-image-slice: 27 27 27 27;
border-image-width: 1px 1px 2px 1px;

View File

@ -2,16 +2,13 @@
<div class="w-max h-max relative">
<SvgComponent :content="groupSvg" class="w-[312px] h-[210px]" />
<div class="w-full h-full absolute top-0 left-0 pt-[31px] pb-[21px] pl-[14px] pr-[19px] flex items-center">
<div class="w-[144px]">
<div class="relative w-[144px] h-[113px] flex items-center flex-col">
<div class="text-[#44C1EF] italic text-[20px] font-700">今日缴费</div>
<div class="bg-gradient-to-b from-[#8FC8FF] to-white bg-clip-text text-transparent mb-[15px] z-10 font-700">
<span class="text-[34px] italic">{{paymentData.toDayTotal || 0}}</span>
<span class="text-[18px]"></span>
</div>
<SvgComponent :content="hexagonalBoxSvg" class="box-light absolute" />
<div class="relative w-[144px] h-[113px] flex items-center flex-col">
<div class="text-[#44C1EF] italic text-[20px] font-700">今日缴费</div>
<div class="mb-[15px] z-10 font-700 leading-[1] flex items-baseline">
<span class="text-[34px] italic text-color" :data-text="paymentData.toDayTotal">{{paymentData.toDayTotal || 0}}</span>
<span class="text-[18px] text-color" data-text=""></span>
</div>
<SvgComponent :content="hexagonalBoxSvg" class="box-light absolute" />
</div>
<div class="relative ml-[12px]">
<SvgComponent :content="paymentBorderSvg" class="w-[127px] h-[104px] z-1" />

View File

@ -1,50 +1,46 @@
<template>
<div class="w-[296px] h-[320px] relative bg-[#082059]">
<div class="flex h-full custom-border absolute top-0 left-0">
<div class="relative h-[36px]">
<div class="absolute top-[50%] translate-y-[-50%] left-[15px] flex items-center">
<SvgComponent :content="arrowLeftSvg" class="w-[15px] h-[18px]" />
<div class="bg-gradient-to-b from-[#8FC8FF] to-white bg-clip-text text-transparent text-[20px] ml-[9px] font-700">获客排行榜</div>
</div>
<SvgComponent :content="headerLeftSvg" class="w-[188px] h-[36px]" />
</div>
<SvgComponent :content="headerRightSvg" class="w-[108px] h-[36px]" />
</div>
<div class="w-full h-[calc(100%-36px)] mt-[36px] flex flex-col relative">
<div class="absolute right-[0] flex items-center justify-end mr-[13px] transform-translate-y-[-50%]">
<div class="text-[15px] text-[#84E8FF]">更多</div>
<SvgComponent :content="moreArrowSvg" class="w-[14px] h-[22px]" />
</div>
<div class="mt-[26px] mx-[16px]">
<RankingTable :value="products" :columns="columns" header-class="custom-table-header" body-class="custom-table-body">
<template #rank="{index }">
<SvgComponent :content="goldMedalSvg" class="w-[34px] h-[20px]" v-if="index===0"/>
<SvgComponent :content="silverMedalSvg" class="w-[34px] h-[20px]" v-if="index===1"/>
<SvgComponent :content="bronzeMedalSvg" class="w-[34px] h-[20px]" v-if="index===2"/>
<span class="text-[14px] font-600" v-if="index > 2">{{ index + 1 }}</span>
</template>
<template #name="{data}">
<span class="text-[#C0EEFF] text-[14px]">{{ data.name }}</span>
</template>
</RankingTable>
</div>
<div class="h-[320px] relative bg-[#082059]">
<div class="flex h-full custom-border absolute top-0 left-0">
<SvgComponent :content="headerLeftSvg" class="w-[188px] h-[36px]" />
<SvgComponent :content="headerRightSvg" class="w-[108px] h-[36px]" />
<div class="absolute top-[2px] left-[15px] flex items-center">
<SvgComponent :content="arrowLeftSvg" class="w-[15px] h-[18px]" />
<div class="text-color text-[20px] ml-[9px] font-700" data-text="">获客排行榜</div>
</div>
</div>
</template>
<script setup lang="ts">
import SvgComponent from "@/components/SvgComponent.vue";
import RankingTable from "@/components/table/RankingTable.vue";
const columns = [
{ field: "rank", header: "名次", align: "justify-center",width:'68px' },
{ field: "name", header: "姓名", align: "justify-left",width:'100px' },
{ field: "total", header: "获客人数", align: "justify-center",width:'96px' },
];
let products = ref<any[]>([]);
<div class="w-[296px] h-[calc(100%-36px)] mt-[36px] flex flex-col">
<div class="flex items-center justify-end mr-[13px] transform-translate-y-[-50%] h-0">
<div class="text-[15px] text-[#84E8FF]">更多</div>
<SvgComponent :content="moreArrowSvg" class="w-[14px] h-[22px]" />
</div>
<div class="mt-[26px] mx-[16px]">
<RankingTable :value="products" :columns="columns" header-class="custom-table-header" body-class="custom-table-body">
<template #rank="{ index }">
<SvgComponent :content="goldMedalSvg" class="w-[34px] h-[20px]" v-if="index === 0" />
<SvgComponent :content="silverMedalSvg" class="w-[34px] h-[20px]" v-if="index === 1" />
<SvgComponent :content="bronzeMedalSvg" class="w-[34px] h-[20px]" v-if="index === 2" />
<span class="text-[14px] font-600" v-if="index > 2">{{ index + 1 }}</span>
</template>
<template #name="{ data }">
<span class="text-[#C0EEFF] text-[14px]">{{ data.name }}</span>
</template>
</RankingTable>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import SvgComponent from "@/components/SvgComponent.vue";
import RankingTable from "@/components/table/RankingTable.vue";
const columns = [
{ field: "rank", header: "名次", align: "justify-center", width: "68px" },
{ field: "name", header: "姓名", align: "justify-left", width: "100px" },
{ field: "total", header: "获客人数", align: "justify-center", width: "96px" },
];
let products = ref<any[]>([]);
const chargingRankingData = inject(
"chargingRankingData",
ref<{
@ -56,104 +52,105 @@
watch(
() => chargingRankingData.value,
() => {PageTransitionEvent
initData()
() => {
PageTransitionEvent;
initData();
},
);
const initData = () => {
if(chargingRankingData.value.acqRanks){
products.value = chargingRankingData.value.acqRanks
}
}
const headerLeftSvg = ref("");
const headerRightSvg = ref("");
const getHeaderLeftSvg = async () => {
const { default: svg } = await import("/src/assets/svg-img/header-bg-left-sort.svg?raw");
headerLeftSvg.value = svg;
};
const getHeaderRightSvg = async () => {
const { default: svg } = await import("/src/assets/svg-img/header-bg-right-sort.svg?raw");
headerRightSvg.value = svg;
};
const arrowLeftSvg = ref("");
const getArrowLeftSvg = async () => {
const { default: svg } = await import("/src/assets/svg-img/arrow-left.svg?raw");
arrowLeftSvg.value = svg;
};
const moreArrowSvg = ref("");
const getMoreArrowSvg = async () => {
const { default: svg } = await import("/src/assets/svg-img/more-arrow.svg?raw");
moreArrowSvg.value = svg;
};
const goldMedalSvg = ref("")
const getGoldMedalSvg = async () => {
const {default: svg} = await import("/src/assets/svg-img/gold-medal.svg?raw");
goldMedalSvg.value = svg
if (chargingRankingData.value.acqRanks) {
products.value = chargingRankingData.value.acqRanks;
}
const silverMedalSvg = ref("")
const getSilverMedalSvg = async () => {
const {default: svg} = await import("/src/assets/svg-img/silver-medal.svg?raw");
silverMedalSvg.value = svg
}
const bronzeMedalSvg = ref("")
const getBronzeMedalSvg = async () => {
const {default: svg} = await import("/src/assets/svg-img/bronze-medal.svg?raw");
bronzeMedalSvg.value = svg
}
onBeforeMount(() => {
getHeaderLeftSvg();
getHeaderRightSvg();
getArrowLeftSvg();
getMoreArrowSvg();
getGoldMedalSvg();
getSilverMedalSvg();
getBronzeMedalSvg();
};
initData();
});
</script>
<style scoped lang="scss">
.custom-border {
border-image-slice: 27 27 27 27;
border-image-width: 1px 1px 2px 1px;
border-image-outset: 0px 0px 0px 0px;
border-image-repeat: stretch stretch;
border-image-source: url("src/assets/svg-img/border-image.png");
border-style: solid;
}
:deep(.custom-table-header) {
tr{
background-color: rgb(14, 39, 97);
th{
padding: 13px 10px;
color:#44C1EF;
font-weight: 400;
line-height: 1;
}
const headerLeftSvg = ref("");
const headerRightSvg = ref("");
const getHeaderLeftSvg = async () => {
const { default: svg } = await import("/src/assets/svg-img/header-bg-left-sort.svg?raw");
headerLeftSvg.value = svg;
};
const getHeaderRightSvg = async () => {
const { default: svg } = await import("/src/assets/svg-img/header-bg-right-sort.svg?raw");
headerRightSvg.value = svg;
};
const arrowLeftSvg = ref("");
const getArrowLeftSvg = async () => {
const { default: svg } = await import("/src/assets/svg-img/arrow-left.svg?raw");
arrowLeftSvg.value = svg;
};
const moreArrowSvg = ref("");
const getMoreArrowSvg = async () => {
const { default: svg } = await import("/src/assets/svg-img/more-arrow.svg?raw");
moreArrowSvg.value = svg;
};
const goldMedalSvg = ref("");
const getGoldMedalSvg = async () => {
const { default: svg } = await import("/src/assets/svg-img/gold-medal.svg?raw");
goldMedalSvg.value = svg;
};
const silverMedalSvg = ref("");
const getSilverMedalSvg = async () => {
const { default: svg } = await import("/src/assets/svg-img/silver-medal.svg?raw");
silverMedalSvg.value = svg;
};
const bronzeMedalSvg = ref("");
const getBronzeMedalSvg = async () => {
const { default: svg } = await import("/src/assets/svg-img/bronze-medal.svg?raw");
bronzeMedalSvg.value = svg;
};
onBeforeMount(() => {
getHeaderLeftSvg();
getHeaderRightSvg();
getArrowLeftSvg();
getMoreArrowSvg();
getGoldMedalSvg();
getSilverMedalSvg();
getBronzeMedalSvg();
initData();
});
</script>
<style scoped lang="scss">
@use "@/styles/text-color.scss";
.custom-border {
border-image-slice: 27 27 27 27;
border-image-width: 1px 1px 2px 1px;
border-image-outset: 0px 0px 0px 0px;
border-image-repeat: stretch stretch;
border-image-source: url("src/assets/svg-img/border-image.png");
border-style: solid;
}
:deep(.custom-table-header) {
tr {
background-color: rgb(14, 39, 97);
th {
padding: 13px 10px;
color: #44c1ef;
font-weight: 400;
line-height: 1;
}
}
:deep(.custom-table-body){
tr{
td{
padding: 10px;
line-height: 1;
}
}
:deep(.custom-table-body) {
tr {
td {
padding: 10px;
line-height: 1;
}
}
</style>
}
</style>

View File

@ -277,11 +277,11 @@
type: "group",
right: 97,
top: 0,
z: 100,
// z: 100,
children: [
{
type: "image",
z: 100,
// z: 100,
style: {
image: `data:image/svg+xml;charset=utf-8,${currentMode.value === "week" ? activeButtonSvgBase64 : buttonSvgBase64}`,
width: 72,
@ -292,7 +292,7 @@
},
{
type: "text",
z: 100,
// z: 100,
style: {
text: "按周",
x: 36,
@ -312,11 +312,11 @@
type: "group",
right: 15,
top: 0,
z: 100,
// z: 100,
children: [
{
type: "image",
z: 100,
// z: 100,
style: {
image: `data:image/svg+xml;charset=utf-8,${currentMode.value === "month" ? activeButtonSvgBase64 : buttonSvgBase64}`,
width: 72,
@ -327,7 +327,7 @@
},
{
type: "text",
z: 100,
// z: 100,
style: {
text: "按月",
x: 36,
@ -451,7 +451,7 @@
{
type: "bar",
id: "axisOverlayBar",
zlevel: 1,
// zlevel: 1,
silent: false,
barWidth: "100%",
data: chartData.value.dates.map(() => 500), // 使y
@ -495,7 +495,7 @@
type: "bar",
barWidth: 40,
barGap: "-100%",
zlevel: 10,
// zlevel: 10,
silent: true,
animation: true,
data: chartData.value.dates.map((_: any, index: number) => {

View File

@ -88,6 +88,26 @@
gap: 0,
},
],
graphic: [
{
id: "circle-rotate",
elements: [
{
type: "image",
left: "center",
top: "center",
style: {
image: "/images/rotate-circle.webp",
width: 143,
height: 143,
fill:"transparent"
},
z: 2,
cursor: "point",
},
],
},
],
animation: true,
animationDuration: 500,
};

View File

@ -1,12 +1,14 @@
<template>
<div class="flex flex-col">
<div class="relative flex-1">
<div class="particle-base"></div>
<div ref="chartRef" class="w-full h-full"></div>
<div
class="absolute left-50% top-50% transform-translate-x-[-50%] transform-translate-y-[-50%] bg-gradient-to-b from-[#8FC8FF] to-white bg-clip-text text-transparent flex items-end leading-[1] z-[2]">
<span class="text-[24px] font-700">{{ total }}</span>
<span class="text-[16px] font-700"></span>
class="absolute left-50% top-50% transform-translate-x-[-50%] transform-translate-y-[-50%] flex items-baseline">
<span class="text-[24px] font-700 text-color" :data-text="total">{{ total }}</span>
<span class="text-[16px] font-700 text-color" data-text=""></span>
</div>
</div>
</div>
</template>
@ -236,47 +238,47 @@
},
],
graphic: [
{
id: "backgroundImg",
elements: [
{
type: "image",
left: "center",
top: "33%",
style: {
image: "/images/particleBase.png",
width: 254,
height: 106,
},
z: 0,
cursor: "point",
origin: [127, 53],
keyframeAnimation: [
{
duration: 1600,
loop: true,
keyframes: [
{
percent: 0,
scaleX: 1.1,
scaleY: 1.1,
},
{
percent: 0.3,
scaleX: 1,
scaleY: 1,
},
{
percent: 1,
scaleX: 1.1,
scaleY: 1.1,
},
],
},
],
},
],
},
// {
// id: "backgroundImg",
// elements: [
// {
// type: "image",
// left: "center",
// top: "33%",
// style: {
// image: "/images/particleBase.png",
// width: 254,
// height: 106,
// },
// z: 0,
// cursor: "point",
// origin: [127, 53],
// // keyframeAnimation: [
// // {
// // duration: 1600,
// // loop: true,
// // keyframes: [
// // {
// // percent: 0,
// // scaleX: 1.1,
// // scaleY: 1.1,
// // },
// // {
// // percent: 0.3,
// // scaleX: 1,
// // scaleY: 1,
// // },
// // {
// // percent: 1,
// // scaleX: 1.1,
// // scaleY: 1.1,
// // },
// // ],
// // },
// // ],
// },
// ],
// },
],
};
};
@ -328,4 +330,32 @@
};
</script>
<style scoped></style>
<style scoped lang="scss">
@use "@/styles/text-color.scss";
.particle-base {
position: absolute;
left: 50%;
top: 60%;
transform: translate(-50%, -50%);
width: 254px;
height: 106px;
background-image: url('/images/particleBase.png');
background-size: contain;
background-repeat: no-repeat;
animation: scaleAnimation 1.6s infinite;
will-change: transform;
}
@keyframes scaleAnimation {
0% {
transform: translate(-50%, -50%) scale(1.1);
}
30% {
transform: translate(-50%, -50%) scale(1);
}
100% {
transform: translate(-50%, -50%) scale(1.1);
}
}
</style>

View File

@ -1,5 +1,5 @@
<template>
<div class="main-bg flex flex-col scrollbar-hide h-screen overflow-auto">
<div class="main-bg flex flex-col scrollbar-hide h-screen overflow-y-auto">
<header class="relative flex items-center">
<SvgComponent :content="headerSvg" class="w-full h-[98px]" />
<SvgComponent :content="titleSvg" class="w-[50%] h-[69px] absolute top-0 left-50% translate-x-[-50%]" />
@ -30,13 +30,13 @@
<OperatingTrends class="flex-1" />
<AskSection class="flex-1" />
</div>
<div class="flex items-center px-[24px] overflow-x-auto">
<StudentSource class="min-w-[296px] max-w-[296px]" />
<OnLineStatus class="ml-[20px] min-w-[296px] max-w-[296px]" />
<OfflineStatus class="ml-[20px] min-w-[296px] max-w-[296px]" />
<SixStatistics class="ml-[20px] min-w-[296px] max-w-[296px]" />
<ChargingRanking class="ml-[20px]" />
<WinCustomer class="ml-[20px]" />
<div class="grid grid-cols-6 gap-[20px] items-center px-[24px] ">
<StudentSource class="" />
<OnLineStatus class="" />
<OfflineStatus class="" />
<SixStatistics class="" />
<ChargingRanking class="" />
<WinCustomer class="" />
</div>
</div>
</div>
@ -184,6 +184,5 @@
background-size: 100% 100%;
background-position: center;
background-repeat: no-repeat;
overflow-y: auto;
}
</style>