feat: 首页数据对接

This commit is contained in:
fyy
2025-09-06 00:49:44 +08:00
parent 9bd24de458
commit 6f987f29ef
11 changed files with 388 additions and 349 deletions

View File

@@ -2,43 +2,32 @@
import type { EchartsUIType } from '@vben/plugins/echarts';
import { EchartsUI, useEcharts } from '@vben/plugins/echarts';
import { onMounted, ref } from 'vue';
import { onMounted, ref, defineExpose } from 'vue';
import { meterRecordTrend } from '#/api/property/energyManagement/meterRecord';
import dayjs from 'dayjs';
const chartRef = ref<EchartsUIType>();
const { renderEcharts } = useEcharts(chartRef);
// 添加日期选择相关逻辑
const selectedDate = ref<string>(dayjs().format('YYYY-MM'));
// 获取当月天数
const getDaysInMonth = (date: string) => {
const getDaysInMonth = (date: any) => {
const year = parseInt(date.split('-')[0]);
const month = parseInt(date.split('-')[1]);
const daysInMonth = new Date(year, month, 0).getDate();
return Array.from({ length: daysInMonth }, (_, i) => i + 1);
};
const handleDateChange = () => {
console.log('selectedDate', selectedDate.value);
getMeterRecordTrend();
};
const getMeterRecordTrend = async () => {
const res = await meterRecordTrend(
{
day: dayjs().format('YYYY-MM-DD'),
month: selectedDate.value || dayjs().format('YYYY-MM'),
const getMeterRecordTrend = async (selectedDate: any) => {
const res = await meterRecordTrend({
day: dayjs().format('YYYY-MM-DD'),
month: selectedDate.value,
year: dayjs().format('YYYY'),
meterType: 1,
meterId: null,
floorId: null,
}
);
console.log(res);
});
// 处理返回的数据
const chartData = res.day.nowMonth.data || [];
console.log(chartData);
// 创建一个映射,将日期和数值对应起来
const dataMap: Record<string, string> = {};
@@ -50,7 +39,7 @@ console.log(chartData);
const days = getDaysInMonth(selectedDate.value);
// 为没有数据的日期填充0构建完整的数据数组
const seriesData = days.map(day => {
const seriesData = days.map((day) => {
return parseFloat(dataMap[day.toString()] || '0');
});
renderEcharts({
@@ -70,7 +59,7 @@ console.log(chartData);
},
smooth: true,
type: 'line',
}
},
],
tooltip: {
axisPointer: {
@@ -120,13 +109,16 @@ console.log(chartData);
});
};
onMounted(async () => {
getMeterRecordTrend();
getMeterRecordTrend(selectedDate);
});
// 暴露方法给父组件调用
defineExpose({
getMeterRecordTrend,
});
</script>
<template>
<div>
<EchartsUI ref="chartRef" />
<EchartsUI ref="chartRef" />
</div>
</template>

View File

@@ -2,12 +2,35 @@
import type { EchartsUIType } from '@vben/plugins/echarts';
import { EchartsUI, useEcharts } from '@vben/plugins/echarts';
import { onMounted, ref } from 'vue';
import { watch, onMounted, ref, defineProps, computed } from 'vue';
const chartRef = ref<EchartsUIType>();
const { renderEcharts } = useEcharts(chartRef);
const props = defineProps<{
statisticsData?: any[]; // 根据实际数据结构调整类型
}>();
// 在组件顶层定义 computed 属性
const chartData = computed(() => {
if (!props.statisticsData) return [];
return props.statisticsData.map((item) => ({
name: item.typeName,
value: item.total,
}));
});
onMounted(() => {
// 监听数据变化,重新渲染图表
watch(
() => props.statisticsData,
(newData) => {
if (newData) {
updateChart();
}
},
{ immediate: true },
);
// 封装图表更新逻辑
const updateChart = () => {
renderEcharts({
legend: {
bottom: '2%',
@@ -22,12 +45,7 @@ onMounted(() => {
animationType: 'scale',
avoidLabelOverlap: false,
color: ['#5ab1ef', '#b6a2de', '#67e0e3', '#2ec7c9'],
data: [
{ name: '搜索引擎', value: 1048 },
{ name: '直接访问', value: 735 },
{ name: '邮件营销', value: 580 },
// { name: '联盟广告', value: 484 },
],
data: chartData.value,
emphasis: {
label: {
fontSize: '12',
@@ -42,7 +60,7 @@ onMounted(() => {
labelLine: {
show: false,
},
name: '访问来源',
name: '预警类型',
type: 'pie',
},
],
@@ -50,6 +68,13 @@ onMounted(() => {
trigger: 'item',
},
});
};
onMounted(() => {
// 组件挂载时尝试更新图表
if (props.statisticsData) {
updateChart();
}
});
</script>

View File

@@ -2,12 +2,34 @@
import type { EchartsUIType } from '@vben/plugins/echarts';
import { EchartsUI, useEcharts } from '@vben/plugins/echarts';
import { onMounted, ref } from 'vue';
import { watch, onMounted, ref, defineProps, computed } from 'vue';
const props = defineProps<{
workData?: any[]; // 根据实际数据结构调整类型
}>();
const chartRef = ref<EchartsUIType>();
const { renderEcharts } = useEcharts(chartRef);
const chartData = computed(() => {
if (!props.workData) return [];
return props.workData.map((item) => ({
name: item.type,
value: item.quantity,
}));
});
onMounted(() => {
// 监听数据变化,重新渲染图表
watch(
() => props.workData,
(newData) => {
if (newData) {
updateChart();
}
},
{ immediate: true },
);
// 封装图表更新逻辑
const updateChart = () => {
renderEcharts({
series: [
{
@@ -18,15 +40,8 @@ onMounted(() => {
animationType: 'scale',
center: ['50%', '50%'],
color: ['#5ab1ef', '#b6a2de', '#67e0e3', '#2ec7c9'],
data: [
{ name: '外包', value: 500 },
{ name: '定制', value: 310 },
{ name: '技术支持', value: 274 },
{ name: '远程', value: 400 },
].sort((a, b) => {
return a.value - b.value;
}),
name: '商业占比',
data: chartData.value,
name: '工单类型',
radius: '80%',
roseType: 'radius',
type: 'pie',
@@ -37,6 +52,13 @@ onMounted(() => {
trigger: 'item',
},
});
};
onMounted(() => {
// 组件挂载时尝试更新图表
if (props.workData) {
updateChart();
}
});
</script>

View File

@@ -2,12 +2,41 @@
import type { EchartsUIType } from '@vben/plugins/echarts';
import { EchartsUI, useEcharts } from '@vben/plugins/echarts';
import { onMounted, ref } from 'vue';
import { watch, onMounted, ref, defineProps, computed } from 'vue';
const props = defineProps<{
statusData?: any[]; // 根据实际数据结构调整类型
}>();
const chartRef = ref<EchartsUIType>();
const { renderEcharts } = useEcharts(chartRef);
const chartData = computed(() => {
if (!props.statusData) return [];
return props.statusData.map((item) => ({
name:
item.state == 0
? '使用中'
: item.state == 1
? '停用中'
: item.state == 2
? '已报废'
: '闲置中',
value: item.quantity,
}));
});
onMounted(() => {
// 监听数据变化,重新渲染图表
watch(
() => props.statusData,
(newData) => {
if (newData) {
updateChart();
}
},
{ immediate: true },
);
// 封装图表更新逻辑
const updateChart = () => {
renderEcharts({
legend: {
bottom: '2%',
@@ -22,12 +51,7 @@ onMounted(() => {
animationType: 'scale',
avoidLabelOverlap: false,
color: ['#5ab1ef', '#b6a2de', '#67e0e3', '#2ec7c9'],
data: [
{ name: '搜索引擎', value: 1048 },
{ name: '直接访问', value: 735 },
{ name: '邮件营销', value: 580 },
// { name: '联盟广告', value: 484 },
],
data: chartData.value,
emphasis: {
label: {
fontSize: '12',
@@ -47,7 +71,7 @@ onMounted(() => {
labelLine: {
show: false,
},
name: '访问来源',
name: '设备状态',
radius: ['40%', '65%'],
type: 'pie',
},
@@ -56,6 +80,14 @@ onMounted(() => {
trigger: 'item',
},
});
};
onMounted(() => {
console.log('Child component mounted, props:', props.statusData);
// 组件挂载时尝试更新图表
if (props.statusData) {
updateChart();
}
});
</script>

View File

@@ -1,12 +1,5 @@
<script lang="ts" setup>
import type { AnalysisOverviewItem } from '@vben/common-ui';
import type { TabOption } from '@vben/types';
import {
AnalysisChartCard,
AnalysisChartsTabs,
AnalysisOverview,
} from '@vben/common-ui';
import { AnalysisChartCard, AnalysisOverview } from '@vben/common-ui';
import {
SvgBellIcon,
SvgCakeIcon,
@@ -18,20 +11,21 @@ import AnalyticsTrends from './analytics-trends.vue';
import AnalyticsVisitsData from './analytics-visits-data.vue';
import AnalyticsVisitsSales from './analytics-visits-sales.vue';
import AnalyticsVisitsSource from './analytics-visits-source.vue';
import AnalyticsVisits from './analytics-visits.vue';
import { ref,onMounted } from 'vue';
import { ref, onMounted } from 'vue';
import { DatePicker } from 'ant-design-vue';
import { Button, Radio } from 'ant-design-vue';
import type { RadioChangeEvent } from 'ant-design-vue';
import {getIndexCount,getStatisticsCurrDay,getStatistics} from '#/api/analytics/index.ts';
const overviewItems =ref<AnalysisOverviewItem[]>(
[
import { Radio } from 'ant-design-vue';
import {
getIndexCount,
getStatisticsCurrDay,
getStatistics,
} from '#/api/analytics/index';
const overviewItems = ref<any[]>([
{
icon: SvgCardIcon,
title: '今日访客量',
totalTitle: '总访客量',
totalValue: 78,
value: 15,
totalValue: 0,
value: 0,
},
{
icon: SvgCakeIcon,
@@ -44,8 +38,8 @@ const overviewItems =ref<AnalysisOverviewItem[]>(
icon: SvgDownloadIcon,
title: '今日预警数量',
totalTitle: '总预警量',
totalValue: 17,
value: 2,
totalValue: 0,
value: 0,
},
{
icon: SvgBellIcon,
@@ -54,44 +48,44 @@ const overviewItems =ref<AnalysisOverviewItem[]>(
totalValue: 0,
value: 0,
},
]
);
]);
const chartTabs: TabOption[] = [
{
label: '电量',
value: 'trends',
},
// {
// label: '水量',
// value: 'visits',
// },
];
//tab选择
const timeUnit = ref(1);
// 月份选择
const selectedDate = ref('');
const handleDateChange = (date: string) => {
selectedDate.value = date;
// 在这里可以添加根据选择日期筛选数据的逻辑
console.log('选择的日期:', date);
const selectedDate = ref<any>(null);
const paramDate = ref(null);
const statisticsList = ref<any>();
const workList = ref<any>();
const statusDataList = ref<any>();
const analyticsTrendsRef = ref<InstanceType<typeof AnalyticsTrends> | null>(
null,
);
const handleDateChange = (date: any) => {
paramDate.value = date.format('YYYY-MM');
// 调用子组件的方法
if (
analyticsTrendsRef.value &&
analyticsTrendsRef.value.getMeterRecordTrend
) {
analyticsTrendsRef.value.getMeterRecordTrend(paramDate);
}
};
// 工单数量
const indexCount = ref(0);
async function getIndex() {
console.log(123);
const res = await getIndexCount();
console.log(res);
// overviewItems[3].value = res.workOrdersToday
// overviewItems[3].totalValue = res.workOrdersTotald
console.log(overviewItems);
overviewItems.value[3].value = res.workOrdersToday;
overviewItems.value[3].totalValue = res.workOrdersTotal;
overviewItems.value[0].value = res.visitorsToday;
overviewItems.value[0].totalValue = res.visitorsTotal;
workList.value = res.satisfactionChartList;
statusDataList.value = res.statusChartVoChartList;
}
// 车流量
async function getCarCount() {
const startDate = new Date();
const startDate = new Date();
startDate.setHours(0, 0, 0, 0);
const endDate = new Date();
endDate.setHours(23, 59, 59, 999);
@@ -101,102 +95,87 @@ async function getCarCount() {
const localTime = new Date(date.getTime() - timezoneOffset);
return localTime.toISOString().slice(0, -1) + 'Z';
};
const response = await fetch(
'https://server.cqnctc.com:6081/web/lot/net/queryOrderParkForPage',
{
method: 'POST',
headers: {
'X-Auth-Token': `${sessionStorage.getItem('token')}`,
'Content-Type': 'application/json',
Authorization: `Bearer ${sessionStorage.getItem('token')}`,
Accept: 'application/json, text/plain, */*',
},
body: JSON.stringify({
pageReq: {
pageNum: 1,
pageSize: 10
const response = await fetch(
'https://server.cqnctc.com:6081/web/lot/net/queryOrderParkForPage',
{
method: 'POST',
headers: {
'X-Auth-Token': `${sessionStorage.getItem('token')}`,
'Content-Type': 'application/json',
Authorization: `Bearer ${sessionStorage.getItem('token')}`,
Accept: 'application/json, text/plain, */*',
},
body: JSON.stringify({
pageReq: {
pageNum: 1,
pageSize: 10,
},
plNos: ['PFN000000012', 'PFN000000022', 'PFN000000025'],
parkInBeginTime: toLocalISOString(startDate),
parkInEndTime: toLocalISOString(endDate),
orgId: 10012,
parkOrderTypes: [100, 200, 201, 300, 500],
terminalSource: 50,
remark: '',
}),
},
plNos: [
"PFN000000012",
"PFN000000022",
"PFN000000025"
],
parkInBeginTime: toLocalISOString(startDate),
parkInEndTime: toLocalISOString(endDate),
orgId: 10012,
parkOrderTypes: [
100,
200,
201,
300,
500
],
terminalSource: 50,
remark: ""
}),
},
);
const result = await response.json();
overviewItems.value[1].value = result.data.pageTotals;
const response2 = await fetch(
'https://server.cqnctc.com:6081/web/lot/net/queryOrderParkForPage',
{
method: 'POST',
headers: {
'X-Auth-Token': `${sessionStorage.getItem('token')}`,
'Content-Type': 'application/json',
Authorization: `Bearer ${sessionStorage.getItem('token')}`,
Accept: 'application/json, text/plain, */*',
},
body: JSON.stringify({
pageReq: {
pageNum: 1,
pageSize: 10
);
const result = await response.json();
overviewItems.value[1].value = result.data.pageTotals;
const response2 = await fetch(
'https://server.cqnctc.com:6081/web/lot/net/queryOrderParkForPage',
{
method: 'POST',
headers: {
'X-Auth-Token': `${sessionStorage.getItem('token')}`,
'Content-Type': 'application/json',
Authorization: `Bearer ${sessionStorage.getItem('token')}`,
Accept: 'application/json, text/plain, */*',
},
body: JSON.stringify({
pageReq: {
pageNum: 1,
pageSize: 10,
},
plNos: ['PFN000000012', 'PFN000000022', 'PFN000000025'],
parkInBeginTime: '',
parkInEndTime: '',
orgId: 10012,
parkOrderTypes: [100, 200, 201, 300, 500],
terminalSource: 50,
remark: '',
}),
},
plNos: [
"PFN000000012",
"PFN000000022",
"PFN000000025"
],
parkInBeginTime: "",
parkInEndTime: "",
orgId: 10012,
parkOrderTypes: [
100,
200,
201,
300,
500
],
terminalSource: 50,
remark: ""
}),
},
);
const result2 = await response2.json();
overviewItems.value[1].totalValue = result2.data.pageTotals;
);
const result2 = await response2.json();
overviewItems.value[1].totalValue = result2.data.pageTotals;
}
// 预警
async function getStatisticsCurrDayCount() {
const res = await getStatisticsCurrDay();
console.log(res);
const res = await getStatisticsCurrDay();
let total = 0;
for (const item of res) {
total += item.total;
}
overviewItems.value[2].value = total;
}
async function getStatisticsCount() {
const res = await getStatistics();
console.log(res);
const res = await getStatistics();
let total = 0;
for (const item of res) {
total += item.total;
}
overviewItems.value[2].totalValue = total;
statisticsList.value = res;
}
// 电量
onMounted(() => {
getIndex();
getCarCount();
getStatisticsCurrDayCount();
getStatisticsCount();
})
getIndex();
getCarCount();
getStatisticsCurrDayCount();
getStatisticsCount();
});
</script>
<template>
@@ -210,19 +189,12 @@ onMounted(() => {
<AnalyticsVisits />
</template>
</AnalysisChartsTabs> -->
<div class="mt-5 bg-white rounded-lg">
<div class="mt-5 rounded-lg bg-white">
<div
class="p-5 flex items-center justify-between "
style="
width: 100%;
height: 100px;
"
class="flex items-center justify-between p-5"
style="width: 100%; height: 100px"
>
<Radio.Group
v-model:value="timeUnit"
@change="handleViewModeChange"
size="large"
>
<Radio.Group v-model:value="timeUnit" size="large">
<Radio.Button value="1">电量</Radio.Button>
</Radio.Group>
<DatePicker
@@ -234,21 +206,24 @@ onMounted(() => {
/>
</div>
<div v-if="timeUnit == 1">
<AnalyticsTrends />
<AnalyticsTrends
ref="analyticsTrendsRef"
:selected-date="selectedDate"
/>
</div>
</div>
<div class="mt-5 w-full md:flex">
<AnalysisChartCard class="mt-5 md:mr-4 md:mt-0 md:w-1/3" title="预警类型">
<AnalyticsVisitsData />
<AnalyticsVisitsData :statisticsData="statisticsList" />
</AnalysisChartCard>
<AnalysisChartCard
class="mt-5 md:mr-4 md:mt-0 md:w-1/3"
title="设备使用状态"
>
<AnalyticsVisitsSource />
<AnalyticsVisitsSource :statusData="statusDataList" />
</AnalysisChartCard>
<AnalysisChartCard class="mt-5 md:mt-0 md:w-1/3" title="工单类型">
<AnalyticsVisitsSales />
<AnalyticsVisitsSales :workData="workList" />
</AnalysisChartCard>
</div>
</div>