feat(property): 添加电表趋势分析功能

This commit is contained in:
2025-08-28 01:22:23 +08:00
parent d8a91ff4be
commit 5f32b3141d
5 changed files with 405 additions and 237 deletions

View File

@@ -1,4 +1,4 @@
import type { MeterRecordVO, MeterRecordForm, MeterRecordQuery } from './model'; import type { MeterRecordVO, MeterRecordForm, MeterRecordQuery, MeterRecordTrend } from './model';
import type { ID, IDS } from '#/api/common'; import type { ID, IDS } from '#/api/common';
import type { PageResult } from '#/api/common'; import type { PageResult } from '#/api/common';
@@ -59,3 +59,13 @@ export function meterRecordUpdate(data: MeterRecordForm) {
export function meterRecordRemove(id: ID | IDS) { export function meterRecordRemove(id: ID | IDS) {
return requestClient.deleteWithMsg<void>(`/property/meterRecord/${id}`); return requestClient.deleteWithMsg<void>(`/property/meterRecord/${id}`);
} }
/**
* 获取用电/气/水趋势分析数据
*
* @param params
* @returns 用电/气/水趋势分析数据
*/
export function meterRecordTrend(params: MeterRecordTrend) {
return requestClient.get<void>('/property/meterRecord/trend', { params });
}

View File

@@ -147,3 +147,36 @@ export interface MeterRecordQuery extends PageQuery {
*/ */
params?: any params?: any
} }
export interface MeterRecordTrend {
/**
* 仪表类型
*/
meterType?: string | number
/**
* 仪表ID
*/
meterId: string | number
/**
* 楼层ID
*/
floorId: string | number
/**
* 日期
*/
day?: string
/**
* 月份
*/
month?: string
/**
* 年份
*/
year?: string
}

View File

@@ -1,59 +1,60 @@
<script setup lang="ts"> <script setup lang="ts">
import type { PropType } from "vue";
import { onMounted, ref } from "vue";
import { handleNode } from "@vben/utils";
import { Empty, Skeleton, Tree } from "ant-design-vue";
import { queryTree } from "#/api/property/energyManagement/meterInfo";
import type { TreeNode } from "#/api/common";
import type { PropType } from 'vue' defineOptions({ inheritAttrs: false });
import { onMounted, ref } from 'vue'
import { handleNode } from '@vben/utils'
import { Empty, Skeleton, Tree } from 'ant-design-vue'
import { queryTree } from "#/api/property/energyManagement/meterInfo"
import type { TreeNode } from '#/api/common'
defineOptions({ inheritAttrs: false }) withDefaults(defineProps<{ showSearch?: boolean }>(), { showSearch: true });
withDefaults(defineProps<{ showSearch?: boolean }>(), { showSearch: true })
const emit = defineEmits<{ const emit = defineEmits<{
/** /**
* 点击刷新按钮的事件 * 点击刷新按钮的事件
*/ */
reload: [] reload: [];
/** /**
* 点击节点的事件 * 点击节点的事件
* @param selectedKeys 选中的节点keys
* @param info 节点信息对象
*/ */
select: [] select: [selectedKeys: string[], info: any];
}>() }>();
const selectFloorId = defineModel('selectFloorId', { const selectTreeId = defineModel("selectTreeId", {
type: Array as PropType<string[]>, type: Array as PropType<string[]>,
}) });
const searchValue = defineModel('searchValue', { const searchValue = defineModel("searchValue", {
type: String, type: String,
default: '', default: "",
}) });
const treeArray = ref<TreeNode[]>([]) const treeArray = ref<TreeNode[]>([]);
/** 骨架屏加载 */ /** 骨架屏加载 */
const showTreeSkeleton = ref<boolean>(true) const showTreeSkeleton = ref<boolean>(true);
async function loadTree() { async function loadTree() {
showTreeSkeleton.value = true showTreeSkeleton.value = true;
searchValue.value = '' searchValue.value = "";
const ret = await queryTree(1) const ret = await queryTree(1);
handleNode(ret, 3) handleNode(ret, 3);
treeArray.value = ret treeArray.value = ret;
showTreeSkeleton.value = false showTreeSkeleton.value = false;
} }
function handleNode(nodes: any[], level: number) { function handleNode(nodes: any[], level: number) {
nodes.forEach((node) => { nodes.forEach((node) => {
node.key = node.id node.key = node.id;
if (node.level < level) { if (node.level < level) {
node.disabled = true node.disabled = true;
} }
if (node.children) { if (node.children) {
handleNode(node.children, level) handleNode(node.children, level);
} }
}) });
} }
onMounted(loadTree); onMounted(loadTree);
@@ -61,11 +62,20 @@ onMounted(loadTree);
<template> <template>
<div :class="$attrs.class"> <div :class="$attrs.class">
<Skeleton :loading="showTreeSkeleton" :paragraph="{ rows: 8 }" active class="p-[8px] flex-1 min-h-0"> <Skeleton :loading="showTreeSkeleton"
:paragraph="{ rows: 8 }"
active
class="p-[8px] flex-1 min-h-0">
<div class="bg-background flex h-full flex-col overflow-y-auto rounded-lg"> <div class="bg-background flex h-full flex-col overflow-y-auto rounded-lg">
<div class="h-full overflow-x-hidden px-[8px]"> <div class="h-full overflow-x-hidden px-[8px]">
<Tree v-bind="$attrs" v-if="treeArray.length > 0" :show-line="{ showLeafIcon: false }" :tree-data="treeArray" <Tree v-bind="$attrs"
:virtual="false" default-expand-all @select="$emit('select')"> v-if="treeArray.length > 0"
v-model:selected-keys="selectTreeId"
:show-line="{ showLeafIcon: false }"
:tree-data="treeArray"
:virtual="false"
default-expand-all
@select="(selectedKeys, info) => $emit('select', selectedKeys, info)">
<template #title="{ label }"> <template #title="{ label }">
<span v-if="label.indexOf(searchValue) > -1"> <span v-if="label.indexOf(searchValue) > -1">
{{ label.substring(0, label.indexOf(searchValue)) }} {{ label.substring(0, label.indexOf(searchValue)) }}
@@ -75,8 +85,10 @@ onMounted(loadTree);
<span v-else>{{ label }}</span> <span v-else>{{ label }}</span>
</template> </template>
</Tree> </Tree>
<div v-else class="mt-5"> <div v-else
<Empty :image="Empty.PRESENTED_IMAGE_SIMPLE" description="暂无数据" /> class="mt-5">
<Empty :image="Empty.PRESENTED_IMAGE_SIMPLE"
description="暂无数据" />
</div> </div>
</div> </div>
</div> </div>

View File

@@ -1,320 +1,423 @@
<script setup lang="ts"> <script setup lang="ts">
import * as echarts from 'echarts' import * as echarts from "echarts";
import { onMounted, ref } from "vue" import { onMounted, ref } from "vue";
import type { Dayjs } from 'dayjs' import type { Dayjs } from "dayjs";
import dayjs from 'dayjs' import dayjs from "dayjs";
import { Page } from '@vben/common-ui' import { Page } from "@vben/common-ui";
import { DatePicker } from 'ant-design-vue' import { DatePicker } from "ant-design-vue";
import FloorTree from "../components/floor-tree.vue" import FloorTree from "../components/floor-tree.vue";
import { meterRecordTrend } from "#/api/property/energyManagement/meterRecord";
const currentDay = ref<Dayjs>(dayjs()) const currentDay = ref<Dayjs>(dayjs());
const currentMonth = ref<Dayjs>(dayjs()) const currentMonth = ref<Dayjs>(dayjs());
const currentYear = ref<Dayjs>(dayjs()) const currentYear = ref<Dayjs>(dayjs());
const disabledDay = (current: Dayjs) => { const disabledDay = (current: Dayjs) => {
return current && current > dayjs().endOf('day') return current && current > dayjs().endOf("day");
} };
const disabledMonth = (current: Dayjs) => { const disabledMonth = (current: Dayjs) => {
return current && current > dayjs().endOf('month') return current && current > dayjs().endOf("month");
} };
const disabledYear = (current: Dayjs) => { const disabledYear = (current: Dayjs) => {
return current && current > dayjs().endOf('year') return current && current > dayjs().endOf("year");
} };
onMounted(() => { onMounted(() => {
//day //day
const chartDay = document.getElementById('day') const chartDay = document.getElementById("day");
const myChartDay = echarts.init(chartDay) const myChartDay = echarts.init(chartDay);
const optionDay = { const optionDay = {
tooltip: { tooltip: {
trigger: 'axis' trigger: "axis",
}, },
legend: { legend: {
data: ['当日', '昨日'] data: ["当日", "昨日"],
}, },
toolbox: { toolbox: {
show: true, show: true,
feature: { feature: {
magicType: { show: true, type: ['line', 'bar'] }, magicType: { show: true, type: ["line", "bar"] },
restore: { show: true }, restore: { show: true },
} },
}, },
calculable: true, calculable: true,
xAxis: [ xAxis: [
{ {
type: 'category', type: "category",
name: '时', name: "时",
data: ['00:00', '01:00', '02:00', '03:00', '04:00', '05:00', '06:00', '07:00', '08:00', '09: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'] data: [
} "00:00",
"01:00",
"02:00",
"03:00",
"04:00",
"05:00",
"06:00",
"07:00",
"08:00",
"09: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: [ yAxis: [
{ {
type: 'value', type: "value",
name: 'KW.h' name: "KW.h",
} },
], ],
series: [ series: [
{ {
name: '当日', name: "当日",
type: 'bar', type: "bar",
data: [ 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, 2.0, 4.9, 7.0, 23.2, 25.6, 76.7, 135.6, 162.2, 32.6, 20.0, 6.4, 3.3 2.0, 4.9, 7.0, 23.2, 25.6, 76.7, 135.6, 162.2, 32.6, 20.0, 6.4, 3.3,
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: { markPoint: {
data: [ data: [
{ type: 'max', name: 'Max' }, { type: "max", name: "Max" },
{ type: 'min', name: 'Min' } { type: "min", name: "Min" },
] ],
}, },
}, },
{ {
name: '昨日', name: "昨日",
type: 'bar', type: "bar",
data: [ data: [
2.6, 5.9, 9.0, 26.4, 28.7, 70.7, 175.6, 152.2, 48.7, 18.8, 6.0, 2.3, 2.6, 5.9, 9.0, 26.4, 28.7, 70.7, 175.6, 182.2, 48.7, 18.8, 6.0, 2.3 2.6, 5.9, 9.0, 26.4, 28.7, 70.7, 175.6, 152.2, 48.7, 18.8, 6.0, 2.3,
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: { markPoint: {
data: [ data: [
{ type: 'max', name: 'Max' }, { type: "max", name: "Max" },
{ type: 'min', name: 'Min' } { type: "min", name: "Min" },
] ],
}, },
} },
], ],
dataZoom: [ dataZoom: [
{ {
type: 'inside', type: "inside",
xAxisIndex: 0, xAxisIndex: 0,
zoomOnMouseWheel: true, zoomOnMouseWheel: true,
filterMode: 'filter', filterMode: "filter",
}, },
], ],
} };
optionDay && myChartDay.setOption(optionDay) optionDay && myChartDay.setOption(optionDay);
//month //month
const chartMonth = document.getElementById('month') const chartMonth = document.getElementById("month");
const myChartMonth = echarts.init(chartMonth) const myChartMonth = echarts.init(chartMonth);
const optionMonth = { const optionMonth = {
tooltip: { tooltip: {
trigger: 'axis' trigger: "axis",
}, },
legend: { legend: {
data: ['当月', '上月'] data: ["当月", "上月"],
}, },
toolbox: { toolbox: {
show: true, show: true,
feature: { feature: {
magicType: { show: true, type: ['line', 'bar'] }, magicType: { show: true, type: ["line", "bar"] },
restore: { show: true }, restore: { show: true },
} },
}, },
calculable: true, calculable: true,
xAxis: [ xAxis: [
{ {
type: 'category', type: "category",
name: '日', name: "日",
data: ['01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23', '24', '25', '26', '27', '28', '29', '30', '31'] data: [
} "01",
"02",
"03",
"04",
"05",
"06",
"07",
"08",
"09",
"10",
"11",
"12",
"13",
"14",
"15",
"16",
"17",
"18",
"19",
"20",
"21",
"22",
"23",
"24",
"25",
"26",
"27",
"28",
"29",
"30",
"31",
],
},
], ],
yAxis: [ yAxis: [
{ {
type: 'value', type: "value",
name: 'KW.h' name: "KW.h",
} },
], ],
series: [ series: [
{ {
name: '当月', name: "当月",
type: 'bar', type: "bar",
data: [ 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, 2.0, 4.9, 7.0, 23.2, 25.6, 76.7, 135.6, 162.2, 32.6, 20.0, 6.4, 3.3, 2.0, 4.9, 7.0, 23.2, 25.6, 76.7, 135.6 2.0, 4.9, 7.0, 23.2, 25.6, 76.7, 135.6, 162.2, 32.6, 20.0, 6.4, 3.3,
2.0, 4.9, 7.0, 23.2, 25.6, 76.7, 135.6, 162.2, 32.6, 20.0, 6.4, 3.3,
2.0, 4.9, 7.0, 23.2, 25.6, 76.7, 135.6,
], ],
markPoint: { markPoint: {
data: [ data: [
{ type: 'max', name: 'Max' }, { type: "max", name: "Max" },
{ type: 'min', name: 'Min' } { type: "min", name: "Min" },
] ],
}, },
}, },
{ {
name: '上月', name: "上月",
type: 'bar', type: "bar",
data: [ 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, 2.0, 4.9, 7.0, 23.2, 25.6, 76.7, 135.6, 162.2, 32.6, 20.0, 6.4, 3.3, 2.0, 4.9, 7.0, 23.2, 25.6, 76.7, 135.6 2.6, 5.9, 9.0, 26.4, 28.7, 70.7, 175.6, 182.2, 48.7, 18.8, 6.0, 2.3,
2.0, 4.9, 7.0, 23.2, 25.6, 76.7, 135.6, 162.2, 32.6, 20.0, 6.4, 3.3,
2.0, 4.9, 7.0, 23.2, 25.6, 76.7, 135.6,
], ],
markPoint: { markPoint: {
data: [ data: [
{ type: 'max', name: 'Max' }, { type: "max", name: "Max" },
{ type: 'min', name: 'Min' } { type: "min", name: "Min" },
] ],
}, },
} },
], ],
dataZoom: [ dataZoom: [
{ {
type: 'inside', type: "inside",
xAxisIndex: 0, xAxisIndex: 0,
zoomOnMouseWheel: true, zoomOnMouseWheel: true,
filterMode: 'filter', filterMode: "filter",
}, },
], ],
} };
optionMonth && myChartMonth.setOption(optionMonth) optionMonth && myChartMonth.setOption(optionMonth);
//year //year
const chartYear = document.getElementById('year') const chartYear = document.getElementById("year");
const myChartYear = echarts.init(chartYear) const myChartYear = echarts.init(chartYear);
const optionYear = { const optionYear = {
tooltip: { tooltip: {
trigger: 'axis' trigger: "axis",
}, },
legend: { legend: {
data: ['当年', '去年'] data: ["当年", "去年"],
}, },
toolbox: { toolbox: {
show: true, show: true,
feature: { feature: {
magicType: { show: true, type: ['line', 'bar'] }, magicType: { show: true, type: ["line", "bar"] },
restore: { show: true }, restore: { show: true },
} },
}, },
calculable: true, calculable: true,
xAxis: [ xAxis: [
{ {
type: 'category', type: "category",
name: '月', name: "月",
data: ['01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12'] data: [
} "01",
"02",
"03",
"04",
"05",
"06",
"07",
"08",
"09",
"10",
"11",
"12",
],
},
], ],
yAxis: [ yAxis: [
{ {
type: 'value', type: "value",
name: 'KW.h' name: "KW.h",
} },
], ],
series: [ series: [
{ {
name: '当年', name: "当年",
type: 'bar', type: "bar",
data: [ 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 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: { markPoint: {
data: [ data: [
{ type: 'max', name: 'Max' }, { type: "max", name: "Max" },
{ type: 'min', name: 'Min' } { type: "min", name: "Min" },
] ],
}, },
}, },
{ {
name: '去年', name: "去年",
type: 'bar', type: "bar",
data: [ 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 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: { markPoint: {
data: [ data: [
{ type: 'max', name: 'Max' }, { type: "max", name: "Max" },
{ type: 'min', name: 'Min' } { type: "min", name: "Min" },
] ],
}, },
} },
], ],
dataZoom: [ dataZoom: [
{ {
type: 'inside', type: "inside",
xAxisIndex: 0, xAxisIndex: 0,
zoomOnMouseWheel: true, zoomOnMouseWheel: true,
filterMode: 'filter', filterMode: "filter",
}, },
], ],
};
optionYear && myChartYear.setOption(optionYear);
// 鼠标悬停时激活缩放
myChartDay.on("mouseover", { seriesIndex: 0 }, () => {
myChartDay.dispatchAction({
type: "takeGlobalCursor",
key: "dataZoomSelect",
dataZoomSelectActive: true,
});
});
// 鼠标离开时取消缩放
myChartDay.on("mouseout", { seriesIndex: 0 }, () => {
myChartDay.dispatchAction({
type: "takeGlobalCursor",
key: "dataZoomSelect",
dataZoomSelectActive: false,
});
});
// 鼠标悬停时激活缩放
myChartYear.on("mouseover", { seriesIndex: 0 }, () => {
myChartYear.dispatchAction({
type: "takeGlobalCursor",
key: "dataZoomSelect",
dataZoomSelectActive: true,
});
});
// 鼠标离开时取消缩放
myChartYear.on("mouseout", { seriesIndex: 0 }, () => {
myChartYear.dispatchAction({
type: "takeGlobalCursor",
key: "dataZoomSelect",
dataZoomSelectActive: false,
});
});
// 鼠标悬停时激活缩放
myChartMonth.on("mouseover", { seriesIndex: 0 }, () => {
myChartMonth.dispatchAction({
type: "takeGlobalCursor",
key: "dataZoomSelect",
dataZoomSelectActive: true,
});
});
// 鼠标离开时取消缩放
myChartMonth.on("mouseout", { seriesIndex: 0 }, () => {
myChartMonth.dispatchAction({
type: "takeGlobalCursor",
key: "dataZoomSelect",
dataZoomSelectActive: false,
});
});
});
function handleSelectFloor(selectedKeys, info) {
let data = {
day: currentDay.value.format("YYYY-MM-DD"),
month: currentMonth.value.format("YYYY-MM"),
year: currentYear.value.format("YYYY"),
meterType: 1,
};
if (info.node.level == 3) {
data.floorId = selectedKeys[0];
} else {
data.meterId = selectedKeys[0];
} }
optionYear && myChartYear.setOption(optionYear)
// 鼠标悬停时激活缩放 meterRecordTrend(data);
myChartDay.on('mouseover', { seriesIndex: 0 }, () => { }
myChartDay.dispatchAction({
type: 'takeGlobalCursor',
key: 'dataZoomSelect',
dataZoomSelectActive: true,
})
})
// 鼠标离开时取消缩放
myChartDay.on('mouseout', { seriesIndex: 0 }, () => {
myChartDay.dispatchAction({
type: 'takeGlobalCursor',
key: 'dataZoomSelect',
dataZoomSelectActive: false,
})
})
// 鼠标悬停时激活缩放
myChartYear.on('mouseover', { seriesIndex: 0 }, () => {
myChartYear.dispatchAction({
type: 'takeGlobalCursor',
key: 'dataZoomSelect',
dataZoomSelectActive: true,
})
})
// 鼠标离开时取消缩放
myChartYear.on('mouseout', { seriesIndex: 0 }, () => {
myChartYear.dispatchAction({
type: 'takeGlobalCursor',
key: 'dataZoomSelect',
dataZoomSelectActive: false,
})
})
// 鼠标悬停时激活缩放
myChartMonth.on('mouseover', { seriesIndex: 0 }, () => {
myChartMonth.dispatchAction({
type: 'takeGlobalCursor',
key: 'dataZoomSelect',
dataZoomSelectActive: true,
})
})
// 鼠标离开时取消缩放
myChartMonth.on('mouseout', { seriesIndex: 0 }, () => {
myChartMonth.dispatchAction({
type: 'takeGlobalCursor',
key: 'dataZoomSelect',
dataZoomSelectActive: false,
})
})
})
</script> </script>
<template> <template>
<Page :auto-content-height="true"> <Page :auto-content-height="true">
<div class="flex h-full gap-[8px]"> <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 class=" flex-1 overflow-hidden">
<div style="background: #fff;border-radius: 8px;padding: 10px;height: 33%"> <div style="background: #fff;border-radius: 8px;padding: 10px;height: 33%">
<div> <div>
<div style="display: flex;justify-content: space-between;"> <div style="display: flex;justify-content: space-between;">
<DatePicker v-model:value="currentDay" :disabled-date="disabledDay" />当日能耗总值125.04KW.h <DatePicker v-model:value="currentDay"
:disabled-date="disabledDay" />当日能耗总值125.04KW.h
</div> </div>
</div> </div>
<div id="day" style="height: 100%;width: 100%;"></div> <div id="day"
style="height: 100%;width: 100%;"></div>
</div> </div>
<div style="background: #fff;border-radius: 8px;padding: 10px;margin-top: 16px;height: 33%"> <div style="background: #fff;border-radius: 8px;padding: 10px;margin-top: 16px;height: 33%">
<div> <div>
<div style="display: flex;justify-content: space-between;"> <div style="display: flex;justify-content: space-between;">
<DatePicker v-model:value="currentMonth" :disabled-date="disabledMonth" picker="month" />当月能耗总值125.04KW.h <DatePicker v-model:value="currentMonth"
:disabled-date="disabledMonth"
picker="month" />当月能耗总值125.04KW.h
</div> </div>
</div> </div>
<div id="month" style="height: 100%;width: 100%;"></div> <div id="month"
style="height: 100%;width: 100%;"></div>
</div> </div>
<div style="background: #fff;border-radius: 8px;padding: 10px;margin-top: 16px;height: 33%"> <div style="background: #fff;border-radius: 8px;padding: 10px;margin-top: 16px;height: 33%">
<div> <div>
<div style="display: flex;justify-content: space-between;"> <div style="display: flex;justify-content: space-between;">
<DatePicker v-model:value="currentYear" :disabled-date="disabledYear" picker="year" />当年能耗总值125.04KW.h <DatePicker v-model:value="currentYear"
:disabled-date="disabledYear"
picker="year" />当年能耗总值125.04KW.h
</div> </div>
</div> </div>
<div id="year" style="height: 100%;width: 100%;"></div> <div id="year"
style="height: 100%;width: 100%;"></div>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -1,54 +1,53 @@
<script setup lang="ts"> <script setup lang="ts">
import type { PropType } from "vue";
import { onMounted, ref } from "vue";
import { handleNode } from "@vben/utils";
import { Empty, Skeleton, Tree } from "ant-design-vue";
import { communityTree } from "#/api/property/community";
import type { CommunityVO } from "#/api/property/community/model";
import type { PropType } from 'vue' defineOptions({ inheritAttrs: false });
import { onMounted, ref } from 'vue'
import { handleNode } from '@vben/utils'
import { Empty, Skeleton, Tree } from 'ant-design-vue'
import { communityTree } from "#/api/property/community"
import type { CommunityVO } from "#/api/property/community/model"
defineOptions({ inheritAttrs: false }) withDefaults(defineProps<{ showSearch?: boolean }>(), { showSearch: true });
withDefaults(defineProps<{ showSearch?: boolean }>(), { showSearch: true })
const emit = defineEmits<{ const emit = defineEmits<{
/** /**
* 点击刷新按钮的事件 * 点击刷新按钮的事件
*/ */
reload: [] reload: [];
/** /**
* 点击节点的事件 * 点击节点的事件
*/ */
select: [] select: [];
}>() }>();
const selectFloorId = defineModel('selectFloorId', { const selectFloorId = defineModel("selectFloorId", {
type: Array as PropType<string[]>, type: Array as PropType<string[]>,
}) });
const searchValue = defineModel('searchValue', { const searchValue = defineModel("searchValue", {
type: String, type: String,
default: '', default: "",
}) });
type TreeArray = CommunityVO[] type TreeArray = CommunityVO[];
const treeArray = ref<TreeArray>([]) const treeArray = ref<TreeArray>([]);
/** 骨架屏加载 */ /** 骨架屏加载 */
const showTreeSkeleton = ref<boolean>(true) const showTreeSkeleton = ref<boolean>(true);
async function loadTree() { async function loadTree() {
showTreeSkeleton.value = true showTreeSkeleton.value = true;
searchValue.value = '' searchValue.value = "";
selectFloorId.value = [] selectFloorId.value = [];
const ret = await communityTree(3) const ret = await communityTree(3);
const splitStr = '/' const splitStr = "/";
handleNode(ret, 'label', splitStr, function (node: any) { handleNode(ret, "label", splitStr, function (node: any) {
if (node.level != 3) { if (node.level != 3) {
node.disabled = true node.disabled = true;
} }
}) });
treeArray.value = ret treeArray.value = ret;
showTreeSkeleton.value = false showTreeSkeleton.value = false;
} }
onMounted(loadTree); onMounted(loadTree);
@@ -56,12 +55,21 @@ onMounted(loadTree);
<template> <template>
<div :class="$attrs.class"> <div :class="$attrs.class">
<Skeleton :loading="showTreeSkeleton" :paragraph="{ rows: 8 }" active class="p-[8px] flex-1 min-h-0"> <Skeleton :loading="showTreeSkeleton"
:paragraph="{ rows: 8 }"
active
class="p-[8px] flex-1 min-h-0">
<div class="bg-background flex h-full flex-col overflow-y-auto rounded-lg"> <div class="bg-background flex h-full flex-col overflow-y-auto rounded-lg">
<div class="h-full overflow-x-hidden px-[8px]"> <div class="h-full overflow-x-hidden px-[8px]">
<Tree v-bind="$attrs" v-if="treeArray.length > 0" v-model:selected-keys="selectFloorId" <Tree v-bind="$attrs"
:field-names="{ title: 'label', key: 'id' }" :show-line="{ showLeafIcon: false }" :tree-data="treeArray" v-if="treeArray.length > 0"
:virtual="false" default-expand-all @select="$emit('select')"> v-model:selected-keys="selectFloorId"
:field-names="{ title: 'label', key: 'id' }"
:show-line="{ showLeafIcon: false }"
:tree-data="treeArray"
:virtual="false"
default-expand-all
@select="$emit('select')">
<template #title="{ label }"> <template #title="{ label }">
<span v-if="label.indexOf(searchValue) > -1"> <span v-if="label.indexOf(searchValue) > -1">
{{ label.substring(0, label.indexOf(searchValue)) }} {{ label.substring(0, label.indexOf(searchValue)) }}
@@ -71,8 +79,10 @@ onMounted(loadTree);
<span v-else>{{ label }}</span> <span v-else>{{ label }}</span>
</template> </template>
</Tree> </Tree>
<div v-else class="mt-5"> <div v-else
<Empty :image="Empty.PRESENTED_IMAGE_SIMPLE" description="暂无数据" /> class="mt-5">
<Empty :image="Empty.PRESENTED_IMAGE_SIMPLE"
description="暂无数据" />
</div> </div>
</div> </div>
</div> </div>