feat: 首页改成图表展示

This commit is contained in:
caiyuchao
2025-09-25 20:09:53 +08:00
parent 622fcca62b
commit 62aac874c4
5 changed files with 223 additions and 15 deletions

View File

@@ -24,6 +24,7 @@ export namespace CustomerApi {
userCount: number;
contractCount: number;
signingRate: number;
completionRate: number;
}
}

View File

@@ -0,0 +1,78 @@
<script lang="ts" setup>
import type { EchartsUIType } from '@vben/plugins/echarts';
import type { CustomerApi } from '#/api/license/customer';
import { onMounted, ref, watch } from 'vue';
import { EchartsUI, useEcharts } from '@vben/plugins/echarts';
const props = defineProps<{ dashboardValue: CustomerApi.Dashboard }>();
const chartRef = ref<EchartsUIType>();
const { renderEcharts } = useEcharts(chartRef);
function renderChart() {
renderEcharts({
tooltip: {},
// legend: { bottom: 0, data: ['总授权', '已签合同'] },
grid: { left: '8%', right: '8%', top: '20%', bottom: '20%' },
xAxis: { type: 'value', max: 350 },
yAxis: {
type: 'category',
axisTick: {
show: false,
},
axisLabel: { show: false },
data: [''],
},
series: [
{
// 总授权
name: '授权License数',
type: 'bar',
data: [props.dashboardValue.licenseCount],
barWidth: 20,
itemStyle: { color: '#5470c6' },
z: 1,
// label: {
// show: true,
// formatter: '{a}: {c}',
// },
},
{
// 已签合同
name: '已签合同数',
type: 'bar',
data: [props.dashboardValue.contractCount],
barWidth: 20,
itemStyle: { color: '#91c7ae' },
z: 2,
label: {
show: true,
position: 'right',
formatter: `{c}/${props.dashboardValue.licenseCount} (${props.dashboardValue.completionRate}%)`,
rich: { per: { color: '#333', fontWeight: 'bold' } },
},
},
],
});
}
onMounted(() => {
renderChart();
});
/** 监听数据变化,重新渲染图表 */
watch(
() => props.dashboardValue,
(newVal) => {
if (newVal) {
renderChart();
}
},
{ deep: true },
);
</script>
<template>
<EchartsUI ref="chartRef" />
</template>

View File

@@ -0,0 +1,54 @@
<script lang="ts" setup>
import type { EchartsUIType } from '@vben/plugins/echarts';
import { onMounted, ref, watch } from 'vue';
import { EchartsUI, useEcharts } from '@vben/plugins/echarts';
const props = withDefaults(defineProps<{ signingRate?: number }>(), {
signingRate: 0,
});
const chartRef = ref<EchartsUIType>();
const { renderEcharts } = useEcharts(chartRef);
function renderChart() {
renderEcharts({
// title: { text: '签单率', left: 'center' },
series: [
{
type: 'gauge',
startAngle: 225,
endAngle: -45,
progress: { show: true, width: 18 },
axisLine: { lineStyle: { width: 18 } },
pointer: { width: 6 },
detail: { fontSize: 24, formatter: '{value}%' },
data: [{ value: props.signingRate }],
},
],
tooltip: {
trigger: 'item',
},
});
}
onMounted(() => {
renderChart();
});
watch(
() => props.signingRate,
(newVal) => {
if (newVal) {
renderChart();
}
},
{ deep: true },
);
</script>
<template>
<EchartsUI ref="chartRef" />
</template>

View File

@@ -0,0 +1,64 @@
<script lang="ts" setup>
import type { EchartsUIType } from '@vben/plugins/echarts';
import type { CustomerApi } from '#/api/license/customer';
import { onMounted, ref, watch } from 'vue';
import { EchartsUI, useEcharts } from '@vben/plugins/echarts';
const props = defineProps<{ dashboardValue: CustomerApi.Dashboard }>();
const chartRef = ref<EchartsUIType>();
const { renderEcharts } = useEcharts(chartRef);
function renderChart() {
renderEcharts({
// title: { text: '员工 / 客户 / 项目', left: 'center' },
tooltip: { trigger: 'item' },
series: [
{
type: 'pie',
roseType: 'radius',
radius: [20, 100],
label: { show: true, fontSize: 14 },
data: [
{
value: props.dashboardValue.userCount,
name: '员工',
itemStyle: { color: '#4F81BD' },
},
{
value: props.dashboardValue.customerCount,
name: '客户',
itemStyle: { color: '#9BBB59' },
},
{
value: props.dashboardValue.projectCount,
name: '项目',
itemStyle: { color: '#C0504D' },
},
],
},
],
});
}
onMounted(() => {
renderChart();
});
/** 监听数据变化,重新渲染图表 */
watch(
() => props.dashboardValue,
(newVal) => {
if (newVal) {
renderChart();
}
},
{ deep: true },
);
</script>
<template>
<EchartsUI ref="chartRef" />
</template>

View File

@@ -1,9 +1,11 @@
<script lang="ts" setup>
import type { AnalysisOverviewItem, WorkbenchTrendItem } from '@vben/common-ui';
import { onMounted, shallowRef } from 'vue';
import type { CustomerApi } from '#/api/license/customer';
import { AnalysisOverview, WorkbenchTrends } from '@vben/common-ui';
import { onMounted, ref, shallowRef } from 'vue';
import { AnalysisChartCard, WorkbenchTrends } from '@vben/common-ui';
import dayjs from 'dayjs';
import relativeTime from 'dayjs/plugin/relativeTime';
@@ -14,14 +16,22 @@ import { getLicenseExpiry } from '#/api/license/license';
// import { DictTag } from '#/components/dict-tag';
import { DICT_TYPE, getDictObj } from '#/utils';
import AnalyticsVisitsData from './analytics-visits-data.vue';
import AnalyticsVisitsSales from './analytics-visits-sales.vue';
import AnalyticsVisitsSource from './analytics-visits-source.vue';
const overviewItems = shallowRef<AnalysisOverviewItem[]>([]);
const trendItems = shallowRef<WorkbenchTrendItem[]>([]);
const projectProgressItems = shallowRef<WorkbenchTrendItem[]>([]);
dayjs.extend(relativeTime);
const dashboardValue = ref<CustomerApi.Dashboard>({} as CustomerApi.Dashboard);
onMounted(async () => {
const data = await dashboard();
dashboardValue.value = data;
overviewItems.value = [
{
icon: 'icon-[streamline-plump-color--user-pin-flat] size-8 flex-shrink-0',
@@ -105,6 +115,7 @@ onMounted(async () => {
date: dayjs(item.updateTime).format('YYYY-MM-DD HH:mm:ss') || '',
title: `${item.projectName ?? ''} `,
status: dict,
info: '',
customerName: item.customerName,
serialNo: item.serialNo,
businessOwner: item.businessOwner,
@@ -126,7 +137,19 @@ onMounted(async () => {
<template #description> 今日晴20 - 32 </template>
</WorkbenchHeader> -->
<AnalysisOverview :items="overviewItems" />
<!-- <AnalysisOverview :items="overviewItems" /> -->
<div class="w-full md:flex">
<AnalysisChartCard class="mt-5 md:mr-4 md:mt-0 md:w-1/3" title="签单率">
<AnalyticsVisitsSales :signing-rate="dashboardValue.signingRate" />
</AnalysisChartCard>
<AnalysisChartCard class="mt-5 md:mr-4 md:mt-0 md:w-1/3" title="完成度">
<AnalyticsVisitsData :dashboard-value="dashboardValue" />
</AnalysisChartCard>
<AnalysisChartCard class="mt-5 md:mt-0 md:w-1/3" title="概览">
<AnalyticsVisitsSource :dashboard-value="dashboardValue" />
</AnalysisChartCard>
</div>
<div class="mt-2 flex flex-col lg:flex-row">
<div class="lg:w mr-4 w-full">
@@ -153,17 +176,5 @@ onMounted(async () => {
<AnalyticsVisits />
</template>
</AnalysisChartsTabs> -->
<!-- <div class="mt-5 w-full md:flex">
<AnalysisChartCard class="mt-5 md:mr-4 md:mt-0 md:w-1/3" title="访问数量">
<AnalyticsVisitsData />
</AnalysisChartCard>
<AnalysisChartCard class="mt-5 md:mr-4 md:mt-0 md:w-1/3" title="访问来源">
<AnalyticsVisitsSource />
</AnalysisChartCard>
<AnalysisChartCard class="mt-5 md:mt-0 md:w-1/3" title="访问来源">
<AnalyticsVisitsSales />
</AnalysisChartCard>
</div> -->
</div>
</template>