工作台第二版
This commit is contained in:
parent
89cb192ded
commit
43df520b17
@ -26,3 +26,32 @@ export function getMktPieData(startDate: string,endDate: string) {
|
||||
params: {startDate,endDate}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** 获取汇总用户卡片数据 */
|
||||
export function getSummaryCardData(startDate: string,endDate: string) {
|
||||
return request<null>({
|
||||
url: '/common/statistics/getSummaryCardData',
|
||||
method: 'get',
|
||||
params: {startDate,endDate}
|
||||
});
|
||||
}
|
||||
|
||||
/** 获取汇总用户line数据 */
|
||||
export function getSummaryLineData(startDate: string,endDate: string) {
|
||||
return request<null>({
|
||||
url: '/common/statistics/getSummaryLineData',
|
||||
method: 'get',
|
||||
params: {startDate,endDate}
|
||||
});
|
||||
}
|
||||
|
||||
/** 获取汇总用户pie数据 */
|
||||
export function getSummaryPieData(startDate: string,endDate: string) {
|
||||
return request<null>({
|
||||
url: '/common/statistics/getSummaryPieData',
|
||||
method: 'get',
|
||||
params: {startDate,endDate}
|
||||
});
|
||||
}
|
||||
|
||||
@ -3,10 +3,13 @@ import { computed, onMounted, ref, watch } from 'vue';
|
||||
import { useAppStore } from '@/store/modules/app';
|
||||
import HeaderBanner from './modules/header-banner.vue';
|
||||
import CardData from './modules/card-data.vue';
|
||||
import CardDataSummary from './modules/card-data-summary.vue';
|
||||
import CardDataMkt from './modules/card-data-mkt.vue';
|
||||
import LineChart from './modules/line-chart.vue';
|
||||
import LineChartSummary from './modules/line-chart-summary.vue';
|
||||
import LineChartMkt from './modules/line-chart-mkt.vue';
|
||||
import PieChart from './modules/pie-chart.vue';
|
||||
import PieChartSummary from './modules/pie-chart-summary.vue';
|
||||
import PieChartMkt from './modules/pie-chart-mkt.vue';
|
||||
import ProjectNews from './modules/project-news.vue';
|
||||
import CreativityBanner from './modules/creativity-banner.vue';
|
||||
@ -74,7 +77,11 @@ onMounted(() => {
|
||||
@date-change="handleDateChange"
|
||||
@quick-change="setDateRange"
|
||||
/>
|
||||
<CardData v-else />
|
||||
<CardDataSummary
|
||||
v-else
|
||||
:date-range="dateRange"
|
||||
@date-change="handleDateChange"
|
||||
@quick-change="setDateRange"/>
|
||||
|
||||
<!-- 线图、bar图 -->
|
||||
<NGrid :x-gap="gap" :y-gap="16" responsive="screen" item-responsive>
|
||||
@ -84,7 +91,9 @@ onMounted(() => {
|
||||
v-if="userInfo.user.userCategory === '0'"
|
||||
:date-range="dateRange"
|
||||
/>
|
||||
<LineChart v-else />
|
||||
<LineChartSummary
|
||||
v-else
|
||||
:date-range="dateRange"/>
|
||||
</NCard>
|
||||
</NGi>
|
||||
<NGi span="24 s:24 m:10">
|
||||
@ -93,7 +102,9 @@ onMounted(() => {
|
||||
v-if="userInfo.user.userCategory === '0'"
|
||||
:date-range="dateRange"
|
||||
/>
|
||||
<PieChart v-else />
|
||||
<PieChartSummary
|
||||
v-else
|
||||
:date-range="dateRange"/>
|
||||
</NCard>
|
||||
</NGi>
|
||||
</NGrid>
|
||||
|
||||
306
cds-fontend-2025.V1/src/views/home/modules/card-data-summary.vue
Normal file
306
cds-fontend-2025.V1/src/views/home/modules/card-data-summary.vue
Normal file
@ -0,0 +1,306 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, watch } from 'vue';
|
||||
import { createReusableTemplate } from '@vueuse/core';
|
||||
import { $t } from '@/locales';
|
||||
import dayjs from 'dayjs';
|
||||
import { getSummaryCardData } from '@/service/api/statistics/statistics'; // 引入获取汇总卡片数据的API
|
||||
|
||||
defineOptions({
|
||||
name: 'CardDataSummary'
|
||||
});
|
||||
|
||||
const props = defineProps<{
|
||||
dateRange: [number, number];
|
||||
}>();
|
||||
|
||||
const emit = defineEmits(['date-change', 'quick-change']);
|
||||
|
||||
// 卡片数据
|
||||
const cardDataList = ref([
|
||||
{
|
||||
key: 'staffCount',
|
||||
title: '总员工数',
|
||||
value: 0,
|
||||
unit: '',
|
||||
color: {
|
||||
start: '#ec4786',
|
||||
end: '#b955a4'
|
||||
},
|
||||
icon: 'ant-design:bar-chart-outlined'
|
||||
},
|
||||
{
|
||||
key: 'mpsCount',
|
||||
title: '总计件量',
|
||||
value: 0,
|
||||
unit: '',
|
||||
color: {
|
||||
start: '#865ec0',
|
||||
end: '#5144b4'
|
||||
},
|
||||
icon: 'ant-design:money-collect-outlined'
|
||||
},
|
||||
{
|
||||
key: 'busiCount',
|
||||
title: '总产品数',
|
||||
value: 0,
|
||||
unit: '',
|
||||
color: {
|
||||
start: '#56cdf3',
|
||||
end: '#719de3'
|
||||
},
|
||||
icon: 'carbon:document-download'
|
||||
},
|
||||
{
|
||||
key: 'customerCount',
|
||||
title: '总客户量',
|
||||
value: 0,
|
||||
unit: '',
|
||||
color: {
|
||||
start: '#fcbc25',
|
||||
end: '#f68057'
|
||||
},
|
||||
icon: 'ant-design:trademark-circle-outlined'
|
||||
}
|
||||
]);
|
||||
|
||||
const loading = ref(false);
|
||||
const formattedDateRange = computed(() => {
|
||||
if (!props.dateRange || props.dateRange.length !== 2) return '';
|
||||
|
||||
const format = 'YYYY-MM-DD';
|
||||
const start = dayjs(props.dateRange[0]).format(format);
|
||||
const end = dayjs(props.dateRange[1]).format(format);
|
||||
|
||||
return start === end ? `${start}` : `${start} 至 ${end}`;
|
||||
});
|
||||
|
||||
// 从API获取汇总卡片数据
|
||||
async function fetchSummaryData() {
|
||||
try {
|
||||
loading.value = true;
|
||||
|
||||
// 格式化日期参数
|
||||
const startDate = dayjs(props.dateRange[0]).format('YYYY-MM-DD');
|
||||
const endDate = dayjs(props.dateRange[1]).format('YYYY-MM-DD');
|
||||
|
||||
// 调用API获取数据
|
||||
const { error, data } = await getSummaryCardData(startDate, endDate);
|
||||
|
||||
if (error) {
|
||||
throw new Error('Failed to fetch summary card data');
|
||||
}
|
||||
|
||||
// 更新卡片数据
|
||||
cardDataList.value = cardDataList.value.map(item => ({
|
||||
...item,
|
||||
value: data[item.key as keyof typeof data] || 0
|
||||
}));
|
||||
} catch (error) {
|
||||
console.error('获取汇总卡片数据失败:', error);
|
||||
// 重置为0作为回退
|
||||
cardDataList.value.forEach(item => item.value = 0);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 设置快速日期范围
|
||||
const setDateRange = (type: 'today' | 'week' | 'month') => {
|
||||
let start, end;
|
||||
|
||||
switch (type) {
|
||||
case 'today':
|
||||
start = dayjs().startOf('day');
|
||||
end = dayjs().endOf('day');
|
||||
break;
|
||||
case 'week':
|
||||
start = dayjs().startOf('week');
|
||||
end = dayjs().endOf('week');
|
||||
break;
|
||||
case 'month':
|
||||
start = dayjs().startOf('month');
|
||||
end = dayjs().endOf('month');
|
||||
break;
|
||||
default:
|
||||
start = dayjs().startOf('day');
|
||||
end = dayjs().endOf('day');
|
||||
}
|
||||
|
||||
emit('date-change', [start.valueOf(), end.valueOf()]);
|
||||
};
|
||||
|
||||
// 处理日期选择器变化
|
||||
const handleDatePickerChange = (range: [number, number] | null) => {
|
||||
if (range && range.length === 2) {
|
||||
emit('date-change', range);
|
||||
}
|
||||
};
|
||||
|
||||
// 监听日期范围变化
|
||||
watch(() => props.dateRange, fetchSummaryData, { immediate: true });
|
||||
|
||||
interface GradientBgProps {
|
||||
gradientColor: string;
|
||||
}
|
||||
|
||||
const [DefineGradientBg, GradientBg] = createReusableTemplate<GradientBgProps>();
|
||||
|
||||
function getGradientColor(color: { start: string; end: string }) {
|
||||
return `linear-gradient(to bottom right, ${color.start}, ${color.end})`;
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NCard :bordered="false" size="small" class="card-wrapper">
|
||||
<!-- 顶部工具栏 -->
|
||||
<div class="date-selector-wrapper">
|
||||
<div class="date-range-display">
|
||||
<div class="title-container">
|
||||
<span class="title">汇总数据概览</span>
|
||||
<span class="value">{{ formattedDateRange }} 日</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="date-controls">
|
||||
<!-- 快速日期选项 -->
|
||||
<div class="quick-date-buttons">
|
||||
<NButton
|
||||
size="small"
|
||||
:type="dateRange[0] === dayjs().startOf('day').valueOf() ? 'primary' : 'default'"
|
||||
@click="setDateRange('today')"
|
||||
>
|
||||
本日
|
||||
</NButton>
|
||||
<NButton
|
||||
size="small"
|
||||
:type="dateRange[0] === dayjs().startOf('week').valueOf() ? 'primary' : 'default'"
|
||||
@click="setDateRange('week')"
|
||||
>
|
||||
本周
|
||||
</NButton>
|
||||
<NButton
|
||||
size="small"
|
||||
:type="dateRange[0] === dayjs().startOf('month').valueOf() ? 'primary' : 'default'"
|
||||
@click="setDateRange('month')"
|
||||
>
|
||||
本月
|
||||
</NButton>
|
||||
</div>
|
||||
|
||||
<!-- 日期选择器 -->
|
||||
<div class="date-picker-group">
|
||||
<NDatePicker
|
||||
:value="dateRange"
|
||||
type="daterange"
|
||||
clearable
|
||||
@update:value="handleDatePickerChange"
|
||||
class="date-picker"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- define component start: GradientBg -->
|
||||
<DefineGradientBg v-slot="{ $slots, gradientColor }">
|
||||
<div class="rd-8px px-16px pb-4px pt-8px text-white" :style="{ backgroundImage: gradientColor }">
|
||||
<component :is="$slots.default" />
|
||||
</div>
|
||||
</DefineGradientBg>
|
||||
<!-- define component end: GradientBg -->
|
||||
|
||||
<NGrid cols="s:1 m:2 l:4" responsive="screen" :x-gap="16" :y-gap="16">
|
||||
<NGi v-for="item in cardDataList" :key="item.key">
|
||||
<GradientBg :gradient-color="getGradientColor(item.color)" class="flex-1">
|
||||
<h3 class="text-16px">{{ item.title }}</h3>
|
||||
<div class="flex justify-between pt-12px">
|
||||
<SvgIcon :icon="item.icon" class="text-32px" />
|
||||
<CountTo
|
||||
:prefix="item.unit"
|
||||
:start-value="0"
|
||||
:end-value="item.value"
|
||||
class="text-30px text-white dark:text-dark"
|
||||
/>
|
||||
</div>
|
||||
</GradientBg>
|
||||
</NGi>
|
||||
</NGrid>
|
||||
</NCard>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
/* 日期选择器容器 - 与卡片风格统一 */
|
||||
.date-selector-wrapper {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 12px 0;
|
||||
margin-bottom: 16px;
|
||||
border-bottom: 1px dashed #eaecef;
|
||||
}
|
||||
|
||||
/* 标题容器 */
|
||||
.title-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
/* 标题样式 */
|
||||
.title {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #1d2129;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
/* 日期显示区域 */
|
||||
.date-range-display {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.date-range-display .value {
|
||||
color: #1890ff;
|
||||
font-weight: 500;
|
||||
font-size: 13px;
|
||||
margin-left: 26px;
|
||||
}
|
||||
|
||||
/* 日期控制区域 */
|
||||
.date-controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
/* 快速日期按钮组 */
|
||||
.quick-date-buttons {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
/* 日期选择器组 */
|
||||
.date-picker-group {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.date-picker {
|
||||
width: 280px;
|
||||
}
|
||||
|
||||
/* 卡片样式 */
|
||||
.card-wrapper {
|
||||
padding: 16px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
/* 当前选中的快速按钮样式 */
|
||||
.n-button--primary-type {
|
||||
background-color: #1890ff;
|
||||
color: white;
|
||||
border-color: #1890ff;
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,177 @@
|
||||
<script setup lang="ts">
|
||||
import { watch } from 'vue';
|
||||
import { useAppStore } from '@/store/modules/app';
|
||||
import { useEcharts } from '@/hooks/common/echarts';
|
||||
import { $t } from '@/locales';
|
||||
import dayjs from 'dayjs';
|
||||
import { getSummaryLineData } from '@/service/api/statistics/statistics'; // 引入获取汇总趋势数据的API
|
||||
|
||||
defineOptions({
|
||||
name: 'LineChartSummary'
|
||||
});
|
||||
|
||||
const props = defineProps<{
|
||||
dateRange: [number, number];
|
||||
}>();
|
||||
|
||||
const appStore = useAppStore();
|
||||
|
||||
// 初始化ECharts实例
|
||||
const { domRef, updateOptions } = useEcharts(() => ({
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'cross',
|
||||
label: {
|
||||
backgroundColor: '#6a7985'
|
||||
}
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
data: ['同比', '环比'] // 同比和环比
|
||||
},
|
||||
grid: {
|
||||
left: '3%',
|
||||
right: '4%',
|
||||
bottom: '3%',
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
boundaryGap: false,
|
||||
data: [] as string[]
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value'
|
||||
},
|
||||
series: [
|
||||
{
|
||||
color: '#8e9dff',
|
||||
name: '同比', // 同比
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
stack: 'Total',
|
||||
areaStyle: {
|
||||
color: {
|
||||
type: 'linear',
|
||||
x: 0,
|
||||
y: 0,
|
||||
x2: 0,
|
||||
y2: 1,
|
||||
colorStops: [
|
||||
{
|
||||
offset: 0.25,
|
||||
color: '#8e9dff'
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: '#fff'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
emphasis: {
|
||||
focus: 'series'
|
||||
},
|
||||
data: [] as number[]
|
||||
},
|
||||
{
|
||||
color: '#26deca',
|
||||
name: '环比', // 环比
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
stack: 'Total',
|
||||
areaStyle: {
|
||||
color: {
|
||||
type: 'linear',
|
||||
x: 0,
|
||||
y: 0,
|
||||
x2: 0,
|
||||
y2: 1,
|
||||
colorStops: [
|
||||
{
|
||||
offset: 0.25,
|
||||
color: '#26deca'
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: '#fff'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
emphasis: {
|
||||
focus: 'series'
|
||||
},
|
||||
data: [] as number[]
|
||||
}
|
||||
]
|
||||
}));
|
||||
|
||||
// 从API获取趋势数据
|
||||
async function fetchTrendData() {
|
||||
try {
|
||||
// 格式化日期参数
|
||||
const startDate = dayjs(props.dateRange[0]).format('YYYY-MM-DD');
|
||||
const endDate = dayjs(props.dateRange[1]).format('YYYY-MM-DD');
|
||||
|
||||
// 调用API获取数据
|
||||
const { error, data } = await getSummaryLineData(startDate, endDate);
|
||||
|
||||
if (error) {
|
||||
console.error('获取汇总趋势数据失败:', error);
|
||||
return;
|
||||
}
|
||||
|
||||
// 更新图表数据
|
||||
updateOptions(opts => {
|
||||
// 设置X轴日期标签
|
||||
opts.xAxis.data = data.dates;
|
||||
|
||||
// 设置同比数据
|
||||
opts.series[0].data = data.yoyData;
|
||||
|
||||
// 设置环比数据
|
||||
opts.series[1].data = data.momData;
|
||||
|
||||
return opts;
|
||||
});
|
||||
} catch (err) {
|
||||
console.error('获取汇总趋势数据时出错:', err);
|
||||
}
|
||||
}
|
||||
|
||||
// 更新多语言配置
|
||||
function updateLocale() {
|
||||
updateOptions((opts, factory) => {
|
||||
const originOpts = factory();
|
||||
|
||||
opts.legend.data = originOpts.legend.data;
|
||||
opts.series[0].name = originOpts.series[0].name;
|
||||
opts.series[1].name = originOpts.series[1].name;
|
||||
|
||||
return opts;
|
||||
});
|
||||
}
|
||||
|
||||
// 监听日期范围变化
|
||||
watch(() => props.dateRange, () => {
|
||||
fetchTrendData();
|
||||
}, { immediate: true });
|
||||
|
||||
// 监听语言变化
|
||||
watch(
|
||||
() => appStore.locale,
|
||||
() => {
|
||||
updateLocale();
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NCard :bordered="false" class="card-wrapper">
|
||||
<div ref="domRef" class="h-360px overflow-hidden"></div>
|
||||
</NCard>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
122
cds-fontend-2025.V1/src/views/home/modules/pie-chart-summary.vue
Normal file
122
cds-fontend-2025.V1/src/views/home/modules/pie-chart-summary.vue
Normal file
@ -0,0 +1,122 @@
|
||||
<script setup lang="ts">
|
||||
import { watch } from 'vue';
|
||||
import { useAppStore } from '@/store/modules/app';
|
||||
import { useEcharts } from '@/hooks/common/echarts';
|
||||
import { $t } from '@/locales';
|
||||
import dayjs from 'dayjs';
|
||||
import { getSummaryPieData } from '@/service/api/statistics/statistics'; // 引入获取汇总饼图数据的API
|
||||
|
||||
defineOptions({
|
||||
name: 'PieChartSummary'
|
||||
});
|
||||
|
||||
const props = defineProps<{
|
||||
dateRange: [number, number];
|
||||
}>();
|
||||
|
||||
const appStore = useAppStore();
|
||||
|
||||
// 初始化ECharts实例
|
||||
const { domRef, updateOptions } = useEcharts(() => ({
|
||||
tooltip: {
|
||||
trigger: 'item'
|
||||
},
|
||||
legend: {
|
||||
bottom: '1%',
|
||||
left: 'center',
|
||||
itemStyle: {
|
||||
borderWidth: 0
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
color: ['#5da8ff', '#8e9dff', '#fedc69', '#26deca'],
|
||||
name: '业务类别',
|
||||
type: 'pie',
|
||||
radius: ['45%', '75%'],
|
||||
avoidLabelOverlap: false,
|
||||
itemStyle: {
|
||||
borderRadius: 10,
|
||||
borderColor: '#fff',
|
||||
borderWidth: 1
|
||||
},
|
||||
label: {
|
||||
show: false,
|
||||
position: 'center'
|
||||
},
|
||||
emphasis: {
|
||||
label: {
|
||||
show: true,
|
||||
fontSize: '12'
|
||||
}
|
||||
},
|
||||
labelLine: {
|
||||
show: false
|
||||
},
|
||||
data: [] as { name: string; value: number }[]
|
||||
}
|
||||
]
|
||||
}));
|
||||
|
||||
// 从API获取饼图数据
|
||||
async function fetchPieData() {
|
||||
try {
|
||||
// 格式化日期参数
|
||||
const startDate = dayjs(props.dateRange[0]).format('YYYY-MM-DD');
|
||||
const endDate = dayjs(props.dateRange[1]).format('YYYY-MM-DD');
|
||||
|
||||
// 调用API获取数据
|
||||
const { error, data } = await getSummaryPieData(startDate, endDate);
|
||||
|
||||
if (error) {
|
||||
console.error('获取汇总饼图数据失败:', error);
|
||||
return;
|
||||
}
|
||||
|
||||
// 更新图表数据
|
||||
updateOptions(opts => {
|
||||
opts.series[0].data = data.map(item => ({
|
||||
name: item.name,
|
||||
value: item.value
|
||||
}));
|
||||
return opts;
|
||||
});
|
||||
} catch (err) {
|
||||
console.error('获取汇总饼图数据时出错:', err);
|
||||
}
|
||||
}
|
||||
|
||||
// 更新多语言配置
|
||||
function updateLocale() {
|
||||
updateOptions((opts, factory) => {
|
||||
const originOpts = factory();
|
||||
|
||||
// 更新系列名称
|
||||
opts.series[0].name = originOpts.series[0].name;
|
||||
|
||||
// 保留原有的数据项(数据会在fetchPieData中更新)
|
||||
return opts;
|
||||
});
|
||||
}
|
||||
|
||||
// 监听日期范围变化
|
||||
watch(() => props.dateRange, () => {
|
||||
fetchPieData();
|
||||
}, { immediate: true });
|
||||
|
||||
// 监听语言变化
|
||||
watch(
|
||||
() => appStore.locale,
|
||||
() => {
|
||||
updateLocale();
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NCard :bordered="false" class="card-wrapper">
|
||||
<div ref="domRef" class="h-360px overflow-hidden"></div>
|
||||
</NCard>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
@ -2,9 +2,7 @@ package org.dromara.statistics.controller;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.dromara.common.core.domain.R;
|
||||
import org.dromara.statistics.domain.vo.MktCardVo;
|
||||
import org.dromara.statistics.domain.vo.MktLineVo;
|
||||
import org.dromara.statistics.domain.vo.MktPieVo;
|
||||
import org.dromara.statistics.domain.vo.*;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
@ -14,9 +12,7 @@ import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* 统计分析控制器
|
||||
@ -129,4 +125,121 @@ public class StatisticsController {
|
||||
|
||||
return R.ok(data);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 汇总卡片数据接口
|
||||
@GetMapping("/getSummaryCardData")
|
||||
public R<Map<String, Integer>> getSummaryCardData(String startDate, String endDate) {
|
||||
System.out.println("获取汇总卡片数据: " + startDate + " 至 " + endDate);
|
||||
|
||||
// 计算日期范围天数
|
||||
LocalDate start = LocalDate.parse(startDate);
|
||||
LocalDate end = LocalDate.parse(endDate);
|
||||
long days = ChronoUnit.DAYS.between(start, end) + 1;
|
||||
|
||||
// 根据天数调整比例 (最大不超过2倍)
|
||||
double multiplier = Math.min(1 + days / 30.0, 2.0);
|
||||
|
||||
// 创建模拟数据
|
||||
Map<String, Integer> data = new HashMap<>();
|
||||
|
||||
// 基础值
|
||||
int[] baseValues = {400, 4000, 16, 66};
|
||||
String[] keys = {"staffCount", "mpsCount", "busiCount", "customerCount"};
|
||||
|
||||
Random random = new Random();
|
||||
for (int i = 0; i < keys.length; i++) {
|
||||
// 基础值乘以倍数,并添加随机波动
|
||||
int value = (int) Math.round(baseValues[i] * multiplier * (0.9 + 0.2 * random.nextDouble()));
|
||||
data.put(keys[i], value);
|
||||
}
|
||||
|
||||
return R.ok(data);
|
||||
}
|
||||
|
||||
|
||||
// 汇总趋势数据接口 (同比环比)
|
||||
@GetMapping("/getSummaryLineData")
|
||||
public R<SummaryLineVo> getSummaryLineData(String startDate, String endDate) {
|
||||
System.out.println("获取汇总趋势数据: " + startDate + " 至 " + endDate);
|
||||
|
||||
// 解析日期范围
|
||||
LocalDate start = LocalDate.parse(startDate);
|
||||
LocalDate end = LocalDate.parse(endDate);
|
||||
|
||||
// 生成日期列表
|
||||
List<String> dates = new ArrayList<>();
|
||||
List<Double> yoyData = new ArrayList<>(); // 同比数据
|
||||
List<Double> momData = new ArrayList<>(); // 环比数据
|
||||
|
||||
// 随机数生成器
|
||||
Random random = new Random();
|
||||
double baseValue = 5000 + random.nextInt(3000); // 基础值范围5000-8000
|
||||
|
||||
// 遍历日期范围
|
||||
for (LocalDate date = start; !date.isAfter(end); date = date.plusDays(1)) {
|
||||
// 添加格式化日期 (MM/dd)
|
||||
dates.add(date.format(DateTimeFormatter.ofPattern("MM/dd")));
|
||||
|
||||
// 生成模拟数据 - 基于基础值波动
|
||||
double dailyBase = baseValue * (0.9 + 0.2 * random.nextDouble());
|
||||
|
||||
// 同比数据:较去年同期的变化
|
||||
double yoyValue = dailyBase * (0.8 + 0.4 * random.nextDouble());
|
||||
|
||||
// 环比数据:较前一日的变化
|
||||
double momValue = dailyBase * (0.85 + 0.3 * random.nextDouble());
|
||||
|
||||
yoyData.add(Math.round(yoyValue * 100.0) / 100.0);
|
||||
momData.add(Math.round(momValue * 100.0) / 100.0);
|
||||
|
||||
// 更新基础值用于下一天
|
||||
baseValue = dailyBase * (0.95 + 0.1 * random.nextDouble());
|
||||
}
|
||||
|
||||
// 创建返回对象
|
||||
SummaryLineVo summaryLineVo = new SummaryLineVo();
|
||||
summaryLineVo.setDates(dates);
|
||||
summaryLineVo.setYoyData(yoyData);
|
||||
summaryLineVo.setMomData(momData);
|
||||
|
||||
return R.ok(summaryLineVo);
|
||||
}
|
||||
|
||||
// 汇总饼图数据接口
|
||||
@GetMapping("/getSummaryPieData")
|
||||
public R<List<SummaryPieVo>> getSummaryPieData(String startDate, String endDate) {
|
||||
System.out.println("获取汇总饼图数据: " + startDate + " 至 " + endDate);
|
||||
|
||||
// 创建模拟数据
|
||||
List<SummaryPieVo> data = new ArrayList<>();
|
||||
|
||||
// 产品类型及其基础值
|
||||
String[] productTypes = {"智E通", "社保类", "公积金", "代收付"};
|
||||
int[] baseValues = {20, 10, 40, 30};
|
||||
|
||||
// 计算日期范围天数
|
||||
LocalDate start = LocalDate.parse(startDate);
|
||||
LocalDate end = LocalDate.parse(endDate);
|
||||
long days = ChronoUnit.DAYS.between(start, end) + 1;
|
||||
|
||||
// 根据天数调整比例 (最大不超过2倍)
|
||||
double multiplier = Math.min(1 + days / 30.0, 2.0);
|
||||
|
||||
// 生成模拟数据
|
||||
Random random = new Random();
|
||||
for (int i = 0; i < productTypes.length; i++) {
|
||||
SummaryPieVo item = new SummaryPieVo();
|
||||
item.setName(productTypes[i]);
|
||||
|
||||
// 基础值乘以倍数,并添加随机波动
|
||||
int value = (int) Math.round(baseValues[i] * multiplier * (0.9 + 0.2 * random.nextDouble()));
|
||||
item.setValue(value);
|
||||
|
||||
data.add(item);
|
||||
}
|
||||
|
||||
return R.ok(data);
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,26 @@
|
||||
package org.dromara.statistics.domain.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 营销线图数据VO
|
||||
*/
|
||||
@Data
|
||||
public class SummaryLineVo {
|
||||
/**
|
||||
* 日期列表 (格式: MM/dd)
|
||||
*/
|
||||
private List<String> dates;
|
||||
|
||||
/**
|
||||
* 同比数据 (与去年同期比较)
|
||||
*/
|
||||
private List<Double> yoyData;
|
||||
|
||||
/**
|
||||
* 环比数据 (与前一日比较)
|
||||
*/
|
||||
private List<Double> momData;
|
||||
}
|
||||
@ -0,0 +1,19 @@
|
||||
package org.dromara.statistics.domain.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 营销线图数据VO
|
||||
*/
|
||||
@Data
|
||||
public class SummaryPieVo {
|
||||
/**
|
||||
* 产品名称
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 产品数量/占比值
|
||||
*/
|
||||
private Integer value;
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user