|
|
@@ -1,146 +1,301 @@
|
|
|
<template>
|
|
|
<div class="app-container home">
|
|
|
- <!-- <el-divider /> -->
|
|
|
- <div class="index-style">
|
|
|
- <div class="typewriter-container">
|
|
|
- <span v-for="(char, index) in 'welcome!'" :key="index" :style="{ animationDelay: `${index * 0.5}s` }" class="typewriter-char">{{
|
|
|
- char
|
|
|
- }}</span>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
+ <el-row :gutter="20" class="panel-group">
|
|
|
+ <el-col :span="6" class="card-panel-col">
|
|
|
+ <el-card shadow="hover" class="box-card">
|
|
|
+ <div class="card-panel-icon-wrapper icon-blue">
|
|
|
+ <el-icon :size="45"><User /></el-icon>
|
|
|
+ </div>
|
|
|
+ <div class="card-panel-description">
|
|
|
+ <div class="card-panel-text">供应商总数</div>
|
|
|
+ <div class="card-panel-num">1,245</div>
|
|
|
+ </div>
|
|
|
+ </el-card>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="6" class="card-panel-col">
|
|
|
+ <el-card shadow="hover" class="box-card">
|
|
|
+ <div class="card-panel-icon-wrapper icon-green">
|
|
|
+ <el-icon :size="45"><Location /></el-icon>
|
|
|
+ </div>
|
|
|
+ <div class="card-panel-description">
|
|
|
+ <div class="card-panel-text">地址总数</div>
|
|
|
+ <div class="card-panel-num">3,892</div>
|
|
|
+ </div>
|
|
|
+ </el-card>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="6" class="card-panel-col">
|
|
|
+ <el-card shadow="hover" class="box-card">
|
|
|
+ <div class="card-panel-icon-wrapper icon-orange">
|
|
|
+ <el-icon :size="45"><Document /></el-icon>
|
|
|
+ </div>
|
|
|
+ <div class="card-panel-description">
|
|
|
+ <div class="card-panel-text">采购订单</div>
|
|
|
+ <div class="card-panel-num">8,460</div>
|
|
|
+ </div>
|
|
|
+ </el-card>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="6" class="card-panel-col">
|
|
|
+ <el-card shadow="hover" class="box-card">
|
|
|
+ <div class="card-panel-icon-wrapper icon-red">
|
|
|
+ <el-icon :size="45"><WarnTriangleFilled /></el-icon>
|
|
|
+ </div>
|
|
|
+ <div class="card-panel-description">
|
|
|
+ <div class="card-panel-text">售后退货单</div>
|
|
|
+ <div class="card-panel-num">125</div>
|
|
|
+ </div>
|
|
|
+ </el-card>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+
|
|
|
+ <el-row :gutter="20" style="margin-top: 20px;">
|
|
|
+ <el-col :span="16">
|
|
|
+ <el-card shadow="hover">
|
|
|
+ <template #header>
|
|
|
+ <div class="clearfix">
|
|
|
+ <span>采购订单趋势 (2025-2026)</span>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ <div ref="lineChartRef" style="height: 350px; width: 100%;"></div>
|
|
|
+ </el-card>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="8">
|
|
|
+ <el-card shadow="hover">
|
|
|
+ <template #header>
|
|
|
+ <div class="clearfix">
|
|
|
+ <span>供应商企业类型分布</span>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ <div ref="pieChartRef" style="height: 350px; width: 100%;"></div>
|
|
|
+ </el-card>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<script setup name="Index" lang="ts">
|
|
|
-const goTarget = (url: string) => {
|
|
|
- window.open(url, '__blank');
|
|
|
-};
|
|
|
-</script>
|
|
|
+import { ref, onMounted, onBeforeUnmount, nextTick } from 'vue';
|
|
|
+import * as echarts from 'echarts';
|
|
|
+import { User, Location, Document, WarnTriangleFilled } from '@element-plus/icons-vue';
|
|
|
|
|
|
-<style lang="scss" scoped>
|
|
|
-.home {
|
|
|
- blockquote {
|
|
|
- padding: 10px 20px;
|
|
|
- margin: 0 0 20px;
|
|
|
- font-size: 17.5px;
|
|
|
- border-left: 5px solid #eee;
|
|
|
- }
|
|
|
- hr {
|
|
|
- margin-top: 20px;
|
|
|
- margin-bottom: 20px;
|
|
|
- border: 0;
|
|
|
- border-top: 1px solid #eee;
|
|
|
+const lineChartRef = ref<HTMLElement | null>(null);
|
|
|
+const pieChartRef = ref<HTMLElement | null>(null);
|
|
|
+
|
|
|
+let lineChart: echarts.ECharts | null = null;
|
|
|
+let pieChart: echarts.ECharts | null = null;
|
|
|
+
|
|
|
+const initLineChart = () => {
|
|
|
+ if (lineChartRef.value) {
|
|
|
+ lineChart = echarts.init(lineChartRef.value);
|
|
|
+ const option = {
|
|
|
+ tooltip: {
|
|
|
+ trigger: 'axis'
|
|
|
+ },
|
|
|
+ legend: {
|
|
|
+ data: ['采购订单量', '售后退货量']
|
|
|
+ },
|
|
|
+ grid: {
|
|
|
+ left: '3%',
|
|
|
+ right: '4%',
|
|
|
+ bottom: '3%',
|
|
|
+ containLabel: true
|
|
|
+ },
|
|
|
+ xAxis: {
|
|
|
+ type: 'category',
|
|
|
+ boundaryGap: false,
|
|
|
+ data: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月']
|
|
|
+ },
|
|
|
+ yAxis: {
|
|
|
+ type: 'value'
|
|
|
+ },
|
|
|
+ series: [
|
|
|
+ {
|
|
|
+ name: '采购订单量',
|
|
|
+ type: 'line',
|
|
|
+ smooth: true,
|
|
|
+ itemStyle: { color: '#409EFF' },
|
|
|
+ areaStyle: {
|
|
|
+ color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
|
|
+ { offset: 0, color: 'rgba(64,158,255,0.5)' },
|
|
|
+ { offset: 1, color: 'rgba(64,158,255,0.1)' }
|
|
|
+ ])
|
|
|
+ },
|
|
|
+ data: [120, 180, 250, 230, 300, 450, 420, 500, 600, 550, 650, 800]
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: '售后退货量',
|
|
|
+ type: 'line',
|
|
|
+ smooth: true,
|
|
|
+ itemStyle: { color: '#F56C6C' },
|
|
|
+ areaStyle: {
|
|
|
+ color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
|
|
+ { offset: 0, color: 'rgba(245,108,108,0.5)' },
|
|
|
+ { offset: 1, color: 'rgba(245,108,108,0.1)' }
|
|
|
+ ])
|
|
|
+ },
|
|
|
+ data: [5, 12, 11, 15, 10, 22, 18, 25, 30, 28, 35, 40]
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ };
|
|
|
+ lineChart.setOption(option);
|
|
|
}
|
|
|
- .col-item {
|
|
|
- margin-bottom: 20px;
|
|
|
+};
|
|
|
+
|
|
|
+const initPieChart = () => {
|
|
|
+ if (pieChartRef.value) {
|
|
|
+ pieChart = echarts.init(pieChartRef.value);
|
|
|
+ const option = {
|
|
|
+ tooltip: {
|
|
|
+ trigger: 'item'
|
|
|
+ },
|
|
|
+ legend: {
|
|
|
+ bottom: '0%',
|
|
|
+ left: 'center'
|
|
|
+ },
|
|
|
+ series: [
|
|
|
+ {
|
|
|
+ name: '供应商类型',
|
|
|
+ type: 'pie',
|
|
|
+ radius: ['40%', '70%'],
|
|
|
+ avoidLabelOverlap: false,
|
|
|
+ itemStyle: {
|
|
|
+ borderRadius: 10,
|
|
|
+ borderColor: '#fff',
|
|
|
+ borderWidth: 2
|
|
|
+ },
|
|
|
+ label: {
|
|
|
+ show: false,
|
|
|
+ position: 'center'
|
|
|
+ },
|
|
|
+ emphasis: {
|
|
|
+ label: {
|
|
|
+ show: true,
|
|
|
+ fontSize: 20,
|
|
|
+ fontWeight: 'bold'
|
|
|
+ }
|
|
|
+ },
|
|
|
+ labelLine: {
|
|
|
+ show: false
|
|
|
+ },
|
|
|
+ data: [
|
|
|
+ { value: 1048, name: '国有企业' },
|
|
|
+ { value: 735, name: '民营企业' },
|
|
|
+ { value: 580, name: '外资企业' },
|
|
|
+ { value: 484, name: '合资企业' },
|
|
|
+ { value: 300, name: '个体工商户' }
|
|
|
+ ]
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ };
|
|
|
+ pieChart.setOption(option);
|
|
|
}
|
|
|
+};
|
|
|
|
|
|
- ul {
|
|
|
- padding: 0;
|
|
|
- margin: 0;
|
|
|
+const handleResize = () => {
|
|
|
+ if (lineChart) {
|
|
|
+ lineChart.resize();
|
|
|
+ }
|
|
|
+ if (pieChart) {
|
|
|
+ pieChart.resize();
|
|
|
}
|
|
|
+};
|
|
|
|
|
|
- font-family: 'open sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
|
|
- font-size: 13px;
|
|
|
- color: #676a6c;
|
|
|
- overflow-x: hidden;
|
|
|
+onMounted(() => {
|
|
|
+ nextTick(() => {
|
|
|
+ initLineChart();
|
|
|
+ initPieChart();
|
|
|
+ window.addEventListener('resize', handleResize);
|
|
|
+ });
|
|
|
+});
|
|
|
|
|
|
- ul {
|
|
|
- list-style-type: none;
|
|
|
+onBeforeUnmount(() => {
|
|
|
+ window.removeEventListener('resize', handleResize);
|
|
|
+ if (lineChart) {
|
|
|
+ lineChart.dispose();
|
|
|
}
|
|
|
-
|
|
|
- h4 {
|
|
|
- margin-top: 0px;
|
|
|
+ if (pieChart) {
|
|
|
+ pieChart.dispose();
|
|
|
}
|
|
|
+});
|
|
|
+</script>
|
|
|
|
|
|
- h2 {
|
|
|
- margin-top: 10px;
|
|
|
- font-size: 26px;
|
|
|
- font-weight: 100;
|
|
|
- }
|
|
|
+<style lang="scss" scoped>
|
|
|
+.home {
|
|
|
+ padding: 20px;
|
|
|
+ background-color: #f0f2f5;
|
|
|
+ min-height: calc(100vh - 84px);
|
|
|
|
|
|
- p {
|
|
|
- margin-top: 10px;
|
|
|
+ .panel-group {
|
|
|
+ margin-bottom: 20px;
|
|
|
|
|
|
- b {
|
|
|
- font-weight: 700;
|
|
|
+ .card-panel-col {
|
|
|
+ margin-bottom: 10px;
|
|
|
}
|
|
|
- }
|
|
|
|
|
|
- .update-log {
|
|
|
- ol {
|
|
|
- display: block;
|
|
|
- list-style-type: decimal;
|
|
|
- margin-block-start: 1em;
|
|
|
- margin-block-end: 1em;
|
|
|
- margin-inline-start: 0;
|
|
|
- margin-inline-end: 0;
|
|
|
- padding-inline-start: 40px;
|
|
|
- }
|
|
|
- }
|
|
|
- .index-style {
|
|
|
- font-size: 48px;
|
|
|
- font-weight: bold;
|
|
|
- letter-spacing: 15px;
|
|
|
- display: flex;
|
|
|
- justify-content: center;
|
|
|
- align-items: center;
|
|
|
- height: 300px;
|
|
|
- background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
|
|
|
- border-radius: 10px;
|
|
|
- box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1);
|
|
|
-
|
|
|
- .typewriter-container {
|
|
|
- display: flex;
|
|
|
- }
|
|
|
+ .box-card {
|
|
|
+ :deep(.el-card__body) {
|
|
|
+ padding: 20px;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: space-between;
|
|
|
+ }
|
|
|
|
|
|
- .typewriter-char {
|
|
|
- opacity: 0;
|
|
|
- transform: translateY(20px) rotate(-5deg);
|
|
|
- animation:
|
|
|
- typewriter-animation 0.8s ease forwards,
|
|
|
- pulse 2s ease-in-out infinite 1s;
|
|
|
- color: #2d8cf0;
|
|
|
- text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.2);
|
|
|
- transition: color 0.3s ease;
|
|
|
+ .card-panel-icon-wrapper {
|
|
|
+ padding: 16px;
|
|
|
+ border-radius: 12px;
|
|
|
+ transition: all 0.38s ease-out;
|
|
|
+ }
|
|
|
+
|
|
|
+ .icon-blue {
|
|
|
+ color: #40c9c6;
|
|
|
+ background: #eaffff;
|
|
|
+ }
|
|
|
+ .icon-green {
|
|
|
+ color: #36a3f7;
|
|
|
+ background: #e8f4ff;
|
|
|
+ }
|
|
|
+ .icon-orange {
|
|
|
+ color: #f4516c;
|
|
|
+ background: #ffe8eb;
|
|
|
+ }
|
|
|
+ .icon-red {
|
|
|
+ color: #34bfa3;
|
|
|
+ background: #eafff6;
|
|
|
+ }
|
|
|
|
|
|
&:hover {
|
|
|
- color: #f06292;
|
|
|
- transform: scale(1.1);
|
|
|
- animation-play-state: paused;
|
|
|
+ .card-panel-icon-wrapper {
|
|
|
+ color: #fff;
|
|
|
+ }
|
|
|
+ .icon-blue {
|
|
|
+ background: #40c9c6;
|
|
|
+ }
|
|
|
+ .icon-green {
|
|
|
+ background: #36a3f7;
|
|
|
+ }
|
|
|
+ .icon-orange {
|
|
|
+ background: #f4516c;
|
|
|
+ }
|
|
|
+ .icon-red {
|
|
|
+ background: #34bfa3;
|
|
|
+ }
|
|
|
}
|
|
|
- }
|
|
|
- }
|
|
|
|
|
|
- @keyframes typewriter-animation {
|
|
|
- 0% {
|
|
|
- opacity: 0;
|
|
|
- transform: translateY(20px) rotate(-5deg);
|
|
|
- }
|
|
|
- 50% {
|
|
|
- opacity: 0.5;
|
|
|
- transform: translateY(10px) rotate(-2deg);
|
|
|
- }
|
|
|
- 100% {
|
|
|
- opacity: 1;
|
|
|
- transform: translateY(0) rotate(0deg);
|
|
|
- }
|
|
|
- }
|
|
|
+ .card-panel-description {
|
|
|
+ font-weight: bold;
|
|
|
+ text-align: right;
|
|
|
|
|
|
- @keyframes pulse {
|
|
|
- 0% {
|
|
|
- text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.2);
|
|
|
- transform: scale(1);
|
|
|
- }
|
|
|
- 50% {
|
|
|
- text-shadow:
|
|
|
- 0 0 15px rgba(45, 140, 240, 0.8),
|
|
|
- 0 0 30px rgba(45, 140, 240, 0.4);
|
|
|
- transform: scale(1.05);
|
|
|
- }
|
|
|
- 100% {
|
|
|
- text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.2);
|
|
|
- transform: scale(1);
|
|
|
+ .card-panel-text {
|
|
|
+ line-height: 18px;
|
|
|
+ color: #8c8c8c;
|
|
|
+ font-size: 16px;
|
|
|
+ margin-bottom: 12px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .card-panel-num {
|
|
|
+ font-size: 24px;
|
|
|
+ color: #333;
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
}
|