refactor(property): 1

This commit is contained in:
2025-09-14 21:46:33 +08:00
parent ede9049bc9
commit 3a3da25128
6 changed files with 371 additions and 185 deletions

View File

@@ -27,10 +27,10 @@ const getDaysInMonth = (date: any) => {
};
const getMeterRecordTrend = async (selectedDate: any, meterType: any) => {
const res = await meterRecordTrend({
day: dayjs().format('YYYY-MM-DD'),
month: selectedDate.value,
year: dayjs().format('YYYY'),
meterType: meterType.value,
day: null,
year: null,
meterId: null,
floorId: null,
});

View File

@@ -1,10 +1,10 @@
<script setup lang="ts">
import * as echarts from 'echarts';
import { onMounted, ref, nextTick } from 'vue';
import type { Dayjs } from 'dayjs';
import dayjs from 'dayjs';
import { Dayjs } from 'dayjs';
import * as echarts from 'echarts';
import { Page } from '@vben/common-ui';
import { DatePicker } from 'ant-design-vue';
import { onMounted, onUnmounted, ref } from 'vue';
import FloorTree from '../components/floor-tree.vue';
import { meterRecordTrend } from '#/api/property/energyManagement/meterRecord';
@@ -21,13 +21,24 @@ const disabledYear = (current: Dayjs) => {
return current && current > dayjs().endOf('year');
};
onMounted(() => {
setTimeout(() => {
initChart();
}, 300);
window.addEventListener('resize', resizeChart);
});
onUnmounted(() => {
window.removeEventListener('resize', resizeChart);
});
const chartInstances = {
day: null as echarts.ECharts | null,
month: null as echarts.ECharts | null,
year: null as echarts.ECharts | null,
};
onMounted(() => {
function initChart() {
//day
const chartDay = document.getElementById('day');
chartInstances.day = echarts.init(chartDay);
@@ -36,14 +47,10 @@ onMounted(() => {
show: true,
trigger: 'axis',
},
legend: {
data: ['当日', '昨日'],
},
toolbox: {
show: true,
feature: {
magicType: { show: true, type: ['line', 'bar'] },
restore: { show: true },
},
},
calculable: true,
@@ -51,32 +58,6 @@ onMounted(() => {
{
type: 'category',
name: '时',
data: [
'0:00',
'1:00',
'2:00',
'3:00',
'4:00',
'5:00',
'6:00',
'7:00',
'8:00',
'9:00',
'10:00',
'11:00',
'12:00',
'13:00',
'14:00',
'15:00',
'16:00',
'17:00',
'18:00',
'19:00',
'20:00',
'21:00',
'22:00',
'23:00',
],
},
],
yAxis: [
@@ -95,14 +76,10 @@ onMounted(() => {
tooltip: {
trigger: 'axis',
},
legend: {
data: ['当月', '上月'],
},
toolbox: {
show: true,
feature: {
magicType: { show: true, type: ['line', 'bar'] },
restore: { show: true },
},
},
calculable: true,
@@ -110,10 +87,6 @@ onMounted(() => {
{
type: 'category',
name: '日',
data: [
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
],
},
],
yAxis: [
@@ -140,14 +113,10 @@ onMounted(() => {
tooltip: {
trigger: 'axis',
},
legend: {
data: ['当年', '去年'],
},
toolbox: {
show: true,
feature: {
magicType: { show: true, type: ['line', 'bar'] },
restore: { show: true },
},
},
calculable: true,
@@ -155,7 +124,6 @@ onMounted(() => {
{
type: 'category',
name: '月',
data: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12],
},
],
yAxis: [
@@ -228,7 +196,13 @@ onMounted(() => {
dataZoomSelectActive: false,
});
});
});
}
function resizeChart() {
chartInstances.day && chartInstances.day.resize();
chartInstances.month && chartInstances.month.resize();
chartInstances.year && chartInstances.year.resize();
}
const hourTotal = ref<number>();
const dayTotal = ref<number>();
@@ -253,11 +227,18 @@ async function handleSelectFloor(selectedKeys, info) {
// 更新日数据图表
if (chartInstances.day && trend.hour) {
const yesterday = currentDay.value
.clone()
.subtract(1, 'day')
.format('YYYY-MM-DD');
hourTotal.value = trend.hour.today.total;
chartInstances.day.setOption({
legend: {
data: [yesterday, data.day],
},
series: [
{
name: '昨日',
name: yesterday,
type: 'bar',
data: trend.hour.yesterday.data || [],
markPoint: {
@@ -271,7 +252,7 @@ async function handleSelectFloor(selectedKeys, info) {
},
},
{
name: '当日',
name: data.day,
type: 'bar',
data: trend.hour.today.data || [],
markPoint: {
@@ -290,11 +271,18 @@ async function handleSelectFloor(selectedKeys, info) {
// 更新月数据图表
if (chartInstances.month && trend.day) {
const lastMonth = currentDay.value
.clone()
.subtract(1, 'month')
.format('YYYY-MM');
dayTotal.value = trend.day.nowMonth.total;
chartInstances.month.setOption({
legend: {
data: [lastMonth, data.month],
},
series: [
{
name: '上月',
name: lastMonth,
type: 'bar',
data: trend.day.lastMonth.data || [],
markPoint: {
@@ -308,7 +296,7 @@ async function handleSelectFloor(selectedKeys, info) {
},
},
{
name: '当月',
name: data.month,
type: 'bar',
data: trend.day.nowMonth.data || [],
markPoint: {
@@ -327,11 +315,18 @@ async function handleSelectFloor(selectedKeys, info) {
// 更新年数据图表
if (chartInstances.year && trend.month) {
const lastYear = currentDay.value
.clone()
.subtract(1, 'year')
.format('YYYY');
monthTotal.value = trend.month.nowYear.total;
chartInstances.year.setOption({
legend: {
data: [lastYear, data.year],
},
series: [
{
name: '去年',
name: lastYear,
type: 'bar',
data: trend.month.lastYear.data || [],
markPoint: {
@@ -345,7 +340,7 @@ async function handleSelectFloor(selectedKeys, info) {
},
},
{
name: '当年',
name: data.year,
type: 'bar',
data: trend.month.nowYear.data || [],
markPoint: {

View File

@@ -1,13 +1,23 @@
import type { FormSchemaGetter } from '#/adapter/form';
import type { VxeGridProps } from '#/adapter/vxe-table';
import {getDictOptions} from "#/utils/dict";
import dayjs from 'dayjs';
import type { FormSchemaGetter } from '#/adapter/form'
import type { VxeGridProps } from '#/adapter/vxe-table'
export const querySchema: FormSchemaGetter = () => [
{
component: 'Select',
componentProps: {
options: getDictOptions('pro_qoq_type'),
options: [
{
label: '日',
value: '0',
},
{
label: '月',
value: '1',
},
{
label: '年',
value: '2',
},
],
},
fieldName: 'date',
label: '日期',
@@ -16,47 +26,59 @@ export const querySchema: FormSchemaGetter = () => [
{
component: 'DatePicker',
componentProps: (formData) => {
const type: 0 | 1 | 2 | 3 = formData.date ?? 0;
const today = dayjs();
const type: 0 | 1 | 2 = formData.date ?? 0
const propsMap = {
0: {
picker: 'date',
format: 'YYYY-MM-DD',
valueFormat: 'YYYY-MM-DD',
showTime: false,
defaultValue: today.format('YYYY-MM-DD'),
},
1: {
picker: 'week',
format: 'YYYY-MM-DD',
valueFormat: 'YYYY-MM-DD',
showTime: false,
defaultValue: today.startOf('week').format('YYYY-MM-DD'),
},
2: {
picker: 'month',
format: 'YYYY-MM',
valueFormat: 'YYYY-MM',
showTime: false,
defaultValue: today.format('YYYY-MM'),
},
3: {
2: {
picker: 'year',
format: 'YYYY',
valueFormat: 'YYYY',
showTime: false,
defaultValue: today.format('YYYY'),
},
};
return propsMap[type];
}
return propsMap[type]
},
fieldName: 'chioceDate',
dependencies: {
triggerFields: ['date'],
},
},
];
]
export function initColumns(type: String) {
const columns: VxeGridProps['columns'] = [
{
title: '能源节点',
field: 'roomNumber',
},
{
title: type === '0' ? '当日用能(kw.h)' : type === '1' ? '当月用能(kw.h)' : '当年用能(kw.h)',
field: 'chargeItem',
},
{
title: type === '0' ? '昨日用能(kw.h)' : type === '1' ? '上月用能(kw.h)' : '去年用能(kw.h)',
field: 'chargeCycle',
},
{
title: '增加值(kw.h)',
field: 'startTime',
},
{
title: '环比(%)',
field: 'endTime',
},
]
return columns
}
export const columns: VxeGridProps['columns'] = [
{
title: '能源节点',
@@ -78,4 +100,4 @@ export const columns: VxeGridProps['columns'] = [
title: '环比(%)',
field: 'endTime',
},
];
]

View File

@@ -1,14 +1,13 @@
<script setup lang="ts">
import { Page, type VbenFormProps } from '@vben/common-ui'
import {
useVbenVxeGrid,
type VxeGridProps
} from '#/adapter/vxe-table'
import {
paymentReviewList,
} from '#/api/property/costManagement/paymentReview'
import { columns, querySchema } from './data'
import FloorTree from "../components/floor-tree.vue"
import dayjs from 'dayjs';
import { onMounted, ref } from 'vue';
import { cloneDeep } from '@vben/utils';
import { message } from 'ant-design-vue';
import { initColumns, querySchema } from './data';
import FloorTree from '../components/floor-tree.vue';
import { Page, type VbenFormProps } from '@vben/common-ui';
import { useVbenVxeGrid, type VxeGridProps } from '#/adapter/vxe-table';
import { meterRecordTrend } from '#/api/property/energyManagement/meterRecord';
const formOptions: VbenFormProps = {
commonConfig: {
@@ -19,45 +18,111 @@ const formOptions: VbenFormProps = {
},
schema: querySchema(),
wrapperClass: 'grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4',
}
};
const gridOptions: VxeGridProps = {
checkboxConfig: {
highlight: true,
reserve: true,
},
columns,
height: 'auto',
keepSource: true,
pagerConfig: {},
pagerConfig: {
enabled: false,
},
proxyConfig: {
ajax: {
query: async ({ page }, formValues = {}) => {
return await paymentReviewList({
pageNum: page.currentPage,
pageSize: page.pageSize,
...formValues,
})
initColumnsTest(formValues);
},
},
},
rowConfig: {
keyField: 'id',
},
id: 'property-paymentReview-index'
}
id: 'property-paymentReview-index',
};
const [BasicTable, tableApi] = useVbenVxeGrid({
formOptions,
gridOptions,
});
const dateType = ref<String>(null);
const chioceDate = ref<String>('');
const idArr = ref<string[]>(['1', '2']);
const levelNode = ref<any>(null);
function initColumnsTest(form: any) {
tableApi.setGridOptions({
columns: initColumns(form.date),
});
tableApi.reload();
dateType.value = form.date;
chioceDate.value =
form.date === '0'
? form.chioceDate.format('YYYY-MM-DD')
: form.date === '1'
? form.chioceDate.format('YYYY-MM')
: form.date === '2'
? form.chioceDate.format('YYYY')
: '';
}
async function handleSelectFloor(selectedKeys, info) {
if (selectedKeys.length === 0) {
return;
}
if (!chioceDate.value) {
message.warning('请选择时间');
return;
}
let data = {
day: null,
month: null,
year: null,
meterType: 1,
meterId: null,
floorId: null,
};
if (dateType.value === '0') {
data.day = chioceDate.value;
} else if (dateType.value === '1') {
data.month = chioceDate.value;
} else if (dateType.value === '2') {
data.year = chioceDate.value;
}
if (info.node.level == 3) {
data.floorId = selectedKeys[0];
} else {
data.meterId = selectedKeys[0];
}
const res = await meterRecordTrend(data);
if (dateType.value === '0') {
res.hour.forEach(element => {
});
} else if (dateType.value === '1') {
data.month = chioceDate.value;
} else if (dateType.value === '2') {
data.year = chioceDate.value;
}
}
</script>
<template>
<Page :auto-content-height="true">
<div class="flex h-full gap-[8px]">
<FloorTree class="w-[260px]"></FloorTree>
<BasicTable class="flex-1 overflow-hidden" table-title="用电环比分析列表" />
<FloorTree class="w-[260px]" @select="handleSelectFloor"></FloorTree>
<BasicTable
class="flex-1 overflow-hidden"
table-title="用电环比分析列表"
/>
</div>
</Page>
</template>

View File

@@ -1,77 +1,60 @@
<script setup lang="ts">
import * as echarts from 'echarts'
import { onMounted, ref } from "vue"
import type { Dayjs } from 'dayjs'
import dayjs from 'dayjs'
import { Page } from '@vben/common-ui'
import { Table, DatePicker } from 'ant-design-vue'
import { SearchOutlined } from '@ant-design/icons-vue'
import FloorTree from "../components/floor-tree.vue"
import dayjs from 'dayjs';
import * as echarts from 'echarts';
import type { Dayjs } from 'dayjs';
import { Page } from '@vben/common-ui';
import { onMounted, onUnmounted, ref } from 'vue';
import { Table, DatePicker, message } from 'ant-design-vue';
import FloorTree from '../components/floor-tree.vue';
import { SearchOutlined } from '@ant-design/icons-vue';
import { meterRecordTrend } from '#/api/property/energyManagement/meterRecord';
const currentYear = ref<Dayjs>(dayjs())
const currentYear = ref<Dayjs>(dayjs());
const disabledYear = (current: Dayjs) => {
return current && current > dayjs().endOf('year')
}
return current && current > dayjs().endOf('year');
};
const chartInstances = {
year: null as echarts.ECharts | null,
};
onMounted(() => {
setTimeout(() => {
initChart();
}, 300);
window.addEventListener('resize', resizeChart);
});
onUnmounted(() => {
window.removeEventListener('resize', resizeChart);
});
function initChart() {
//year
const chartYear = document.getElementById('year')
const myChartYear = echarts.init(chartYear)
const chartYear = document.getElementById('year');
chartInstances.year = echarts.init(chartYear);
const optionYear = {
tooltip: {
trigger: 'axis'
},
legend: {
data: ['本期', '同期']
trigger: 'axis',
},
toolbox: {
show: true,
feature: {
magicType: { show: true, type: ['line', 'bar'] },
restore: { show: true },
}
},
},
calculable: true,
xAxis: [
{
type: 'category',
name: '月',
data: ['01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12']
}
},
],
yAxis: [
{
type: 'value',
name: 'KW.h'
}
],
series: [
{
name: '本期',
type: 'bar',
data: [
2.0, 4.9, 7.0, 23.2, 25.6, 76.7, 135.6, 162.2, 32.6, 20.0, 6.4, 3.3
],
markPoint: {
data: [
{ type: 'max', name: 'Max' },
{ type: 'min', name: 'Min' }
]
name: 'KW.h',
},
},
{
name: '同期',
type: 'bar',
data: [
2.6, 5.9, 9.0, 26.4, 28.7, 70.7, 175.6, 182.2, 48.7, 18.8, 6.0, 2.3
],
markPoint: {
data: [
{ type: 'max', name: 'Max' },
{ type: 'min', name: 'Min' }
]
},
}
],
dataZoom: [
{
@@ -81,76 +64,197 @@ onMounted(() => {
filterMode: 'filter',
},
],
}
optionYear && myChartYear.setOption(optionYear)
};
optionYear && chartInstances.year.setOption(optionYear);
// 鼠标悬停时激活缩放
myChartYear.on('mouseover', { seriesIndex: 0 }, () => {
myChartYear.dispatchAction({
chartInstances.year.on('mouseover', { seriesIndex: 0 }, () => {
chartInstances.year.dispatchAction({
type: 'takeGlobalCursor',
key: 'dataZoomSelect',
dataZoomSelectActive: true,
})
})
});
});
// 鼠标离开时取消缩放
myChartYear.on('mouseout', { seriesIndex: 0 }, () => {
myChartYear.dispatchAction({
chartInstances.year.on('mouseout', { seriesIndex: 0 }, () => {
chartInstances.year.dispatchAction({
type: 'takeGlobalCursor',
key: 'dataZoomSelect',
dataZoomSelectActive: false,
})
})
})
});
});
}
function resizeChart() {
chartInstances.year && chartInstances.year.clear();
}
const compareData = ref<any[]>(null);
const columns = [
{
title: '本期时间',
dataIndex: 'name',
dataIndex: 'month',
},
{
title: '本期能耗(kw.h)',
dataIndex: 'age',
dataIndex: 'now',
},
{
title: '同比能耗(kw.h)',
dataIndex: 'address',
dataIndex: 'last',
},
{
title: '同比(%)',
dataIndex: 'address',
dataIndex: 'percent',
},
]
];
const data = [...Array(32)].map((_, i) => ({
const idArr = ref<string[]>([]);
const levelNode = ref<any>(null);
async function handleSelectFloor(selectedKeys, info) {
if (selectedKeys.length === 0 && idArr.value.length === 0) {
message.warning('请选择楼栋或电表');
return;
}
if (info != null) {
// 赋值节点层级
levelNode.value = info.node.level;
}
if (selectedKeys.length > 0) {
idArr.value = selectedKeys;
}
let data = {
year: currentYear.value.format('YYYY'),
day: null,
month: null,
meterType: 1,
meterId: null,
floorId: null,
};
if (levelNode.value == 3) {
data.floorId = selectedKeys.length > 0 ? selectedKeys[0] : idArr.value[0];
} else {
data.meterId = selectedKeys.length > 0 ? selectedKeys[0] : idArr.value[0];
}
const trend = await meterRecordTrend(data);
// 更新年数据图表
if (chartInstances.year && trend.month) {
const lastYear = currentYear.value
.clone()
.subtract(1, 'year')
.format('YYYY');
chartInstances.year.setOption({
legend: {
data: [lastYear, data.year],
},
series: [
{
name: lastYear,
type: 'bar',
data: trend.month.lastYear.data || [],
markPoint: {
data: [
{ type: 'max', name: 'Max' },
{ type: 'min', name: 'Min' },
],
},
markLine: {
data: [{ type: 'average', name: 'Avg' }],
},
},
{
name: data.year,
type: 'bar',
data: trend.month.nowYear.data || [],
markPoint: {
data: [
{ type: 'max', name: 'Max' },
{ type: 'min', name: 'Min' },
],
},
markLine: {
data: [{ type: 'average', name: 'Avg' }],
},
},
],
});
}
// 同比能耗
const lastYear = trend.month.lastYear.data || [];
const nowYear = trend.month.nowYear.data || [];
let tableData = [];
nowYear.forEach((now, i) => {
if (now[0] === lastYear[i][0]) {
let percent = (
((parseFloat(now[1]) - parseFloat(lastYear[i][1])) /
parseFloat(lastYear[i][1])) *
100
).toFixed(2);
tableData.push({
key: i,
name: `${i + 1}`,
age: '--',
address: `--`,
}));
month: `${i + 1}`,
now: parseInt(now[1]) === 0 ? '--' : now[1],
last: parseInt(lastYear[i][1]) === 0 ? '--' : lastYear[i][1],
percent:
parseInt(now[1]) !== 0 && parseInt(lastYear[i][1]) !== 0
? parseFloat(percent) >= 0
? percent + '↑'
: -percent + '↓'
: '--',
});
i;
}
});
compareData.value = tableData;
}
</script>
<template>
<Page :auto-content-height="true">
<div class="flex h-full gap-[8px]">
<FloorTree class="w-[260px]"></FloorTree>
<FloorTree class="w-[260px]" @select="handleSelectFloor"></FloorTree>
<div class="flex-1 overflow-hidden">
<div style="background: #fff;border-radius: 8px;padding: 10px;margin-bottom: 16px;">
<div
style="
background: #fff;
border-radius: 8px;
padding: 10px;
margin-bottom: 16px;
height: 33%;
"
>
<div>
年份
<DatePicker style="margin: 0 10px;" v-model:value="currentYear" :disabled-date="disabledYear" picker="year" />
<a-button type="primary">
<DatePicker
style="margin: 0 10px"
v-model:value="currentYear"
:disabled-date="disabledYear"
picker="year"
/>
<a-button type="primary" @click="handleSelectFloor([], null)">
<template #icon>
<SearchOutlined />
</template>
查询
</a-button>
</div>
<div id="year" style="height: 250px;width: 100%;"></div>
<div id="year" style="height: 100%; width: 100%"></div>
</div>
<div>
<Table style="border-radius: 8px;" :columns="columns" :data-source="data" :pagination="false"
:scroll="{ y: '50vh' }" />
<Table
style="border-radius: 8px"
:columns="columns"
:data-source="compareData"
:pagination="false"
/>
</div>
</div>
</div>

View File

@@ -20,9 +20,9 @@ const getDaysInMonth = (date: any) => {
const emit = defineEmits(['getPowerDate']);
const getMeterRecordTrend = async (selectedDate: any) => {
const res = await meterRecordTrend({
day: dayjs().format('YYYY-MM-DD'),
month: selectedDate.value,
year: dayjs().format('YYYY'),
day: null,
year: null,
meterType: 1,
meterId: null,
floorId: null,