feat:有限空间饼图加接口以及请求
This commit is contained in:
parent
f0075ba4c0
commit
5a15829710
@ -3,11 +3,16 @@ import { PieCircleCommenConfig } from './index'
|
|||||||
import { CreateComponentType } from '@/packages/index.d'
|
import { CreateComponentType } from '@/packages/index.d'
|
||||||
import cloneDeep from 'lodash/cloneDeep'
|
import cloneDeep from 'lodash/cloneDeep'
|
||||||
import dataJson from './data.json'
|
import dataJson from './data.json'
|
||||||
import { pad, padEnd } from 'lodash'
|
|
||||||
import { calcPadding } from '@visactor/vchart/esm/util'
|
|
||||||
export const includes = ['legend']
|
export const includes = ['legend']
|
||||||
|
export const selectStyleOption = {
|
||||||
|
showAngle: false,
|
||||||
|
color: '#1B9FCC',
|
||||||
|
backgroundColor: '#0F4557',
|
||||||
|
borderTop: '1px solid #1CA4D2',
|
||||||
|
cursor: 'pointer',
|
||||||
|
borderRadius: '2px',
|
||||||
|
}
|
||||||
const otherConfig = {
|
const otherConfig = {
|
||||||
// 轮播动画
|
// 轮播动画
|
||||||
isCarousel: false,
|
isCarousel: false,
|
||||||
@ -16,9 +21,26 @@ const otherConfig = {
|
|||||||
show: true,
|
show: true,
|
||||||
selectValue: 'day',
|
selectValue: 'day',
|
||||||
dataset: [
|
dataset: [
|
||||||
{ label: '今日', value: 'day' },
|
{
|
||||||
{ label: '本周', value: 'week' },
|
label: '当天',
|
||||||
{ label: '本月', value: 'month' }
|
value: 'day'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '本周',
|
||||||
|
value: 'week'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '当月',
|
||||||
|
value: 'month'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '本季度',
|
||||||
|
value: 'quarter'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '当年',
|
||||||
|
value: 'year'
|
||||||
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
titleText: '今日数据',
|
titleText: '今日数据',
|
||||||
@ -34,6 +56,8 @@ const otherConfig = {
|
|||||||
paddingRight: 0,
|
paddingRight: 0,
|
||||||
paddingTop: 0,
|
paddingTop: 0,
|
||||||
paddingBottom: 0
|
paddingBottom: 0
|
||||||
|
}, selectStyleOption: {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,26 +1,26 @@
|
|||||||
{
|
{
|
||||||
"dimensions": [
|
"dimensions": [
|
||||||
"name",
|
"alarmLevel",
|
||||||
"value",
|
"count",
|
||||||
"itemColor",
|
"itemColor",
|
||||||
"borderColor"
|
"borderColor"
|
||||||
],
|
],
|
||||||
"source": [
|
"source": [
|
||||||
{
|
{
|
||||||
"name": "分类一",
|
"alarmLevel": "分类一",
|
||||||
"value": 94
|
"count": 94
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "分类二",
|
"alarmLevel": "分类3",
|
||||||
"value": 82
|
"count": 4
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "分类三",
|
"alarmLevel": "分类2",
|
||||||
"value": 78
|
"count": 30
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "分类四",
|
"alarmLevel": "分类4",
|
||||||
"value": 60
|
"count": 40
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
@ -1,7 +1,9 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="go-border-box">
|
<div class="go-border-box">
|
||||||
<SmallBaorder01 :titleText="option.titleText" :title-option="option.titleOption"
|
<SmallBaorder01 :titleText="option.titleText" :title-option="option.titleOption"
|
||||||
:select-option="option.selectOption" :header-option="option.headerOption" @change="handleBorderSelectChange">
|
:select-option="option.selectOption" :header-option="option.headerOption" @change="handleBorderSelectChange"
|
||||||
|
:selectStyleConfig="selectStyleOption
|
||||||
|
">
|
||||||
<v-chart class=" chart" ref="vChartRef" :theme="themeColor" :init-options="initOptions" :option="option"
|
<v-chart class=" chart" ref="vChartRef" :theme="themeColor" :init-options="initOptions" :option="option"
|
||||||
autoresize> </v-chart>
|
autoresize> </v-chart>
|
||||||
</SmallBaorder01>
|
</SmallBaorder01>
|
||||||
@ -16,7 +18,7 @@ import { use } from 'echarts/core'
|
|||||||
import { CanvasRenderer } from 'echarts/renderers'
|
import { CanvasRenderer } from 'echarts/renderers'
|
||||||
import { PieChart } from 'echarts/charts'
|
import { PieChart } from 'echarts/charts'
|
||||||
import { mergeTheme } from '@/packages/public/chart'
|
import { mergeTheme } from '@/packages/public/chart'
|
||||||
import config, { includes } from './config'
|
import config, { includes, selectStyleOption } from './config'
|
||||||
import { useChartDataFetch } from '@/hooks'
|
import { useChartDataFetch } from '@/hooks'
|
||||||
import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
|
import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
|
||||||
import SmallBaorder01 from '../SmallBorder01Co/index.vue'
|
import SmallBaorder01 from '../SmallBorder01Co/index.vue'
|
||||||
@ -28,6 +30,7 @@ import {
|
|||||||
TitleComponent, GraphicComponent
|
TitleComponent, GraphicComponent
|
||||||
} from 'echarts/components'
|
} from 'echarts/components'
|
||||||
|
|
||||||
|
import axiosInstance from '@/api/axios'
|
||||||
|
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
@ -56,9 +59,44 @@ use([
|
|||||||
TitleComponent,
|
TitleComponent,
|
||||||
GraphicComponent
|
GraphicComponent
|
||||||
])
|
])
|
||||||
|
// const option = computed(() => {
|
||||||
|
// return mergeTheme(props.chartConfig.option, props.themeSetting, includes)
|
||||||
|
// })
|
||||||
|
|
||||||
const option = computed(() => {
|
const option = computed(() => {
|
||||||
return mergeTheme(props.chartConfig.option, props.themeSetting, includes)
|
const merged = mergeTheme(props.chartConfig.option, props.themeSetting, includes);
|
||||||
})
|
const dataset = merged.dataset || {};
|
||||||
|
const total = calculateTotal(dataset);
|
||||||
|
|
||||||
|
merged.title.text = total;
|
||||||
|
// 每次都创建新的formatter函数
|
||||||
|
merged.legend = {
|
||||||
|
...merged.legend,
|
||||||
|
formatter: (name: string) => {
|
||||||
|
const item = dataset.source?.find((it: any) => it[dataset.dimensions?.[0]] === name);
|
||||||
|
if (!item) return name;
|
||||||
|
const value = item[dataset.dimensions?.[1]] ?? 0;
|
||||||
|
const percentage = calculatePercentage(value, total);
|
||||||
|
return `{title|${name}}\n{detail|${value}\t${percentage}}`;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 更新tooltip formatter
|
||||||
|
merged.tooltip = {
|
||||||
|
...merged.tooltip,
|
||||||
|
formatter: (params: any) => {
|
||||||
|
const name = params.name;
|
||||||
|
const item = dataset.source?.find((it: any) => it[dataset.dimensions?.[0]] === name);
|
||||||
|
if (!item) return `${name}: 0(0.0%)`;
|
||||||
|
const value = item[dataset.dimensions?.[1]] ?? 0;
|
||||||
|
const percentage = calculatePercentage(value, total);
|
||||||
|
return `${name}: ${value}(${percentage})`;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return merged;
|
||||||
|
});
|
||||||
|
|
||||||
// 总数计算
|
// 总数计算
|
||||||
const calculateTotal = (data: any) => {
|
const calculateTotal = (data: any) => {
|
||||||
if (!data || !data.source) return 0
|
if (!data || !data.source) return 0
|
||||||
@ -76,48 +114,104 @@ const calculatePercentage = (value: number, total: number) => {
|
|||||||
// 处理数据更新
|
// 处理数据更新
|
||||||
const dataHandle = (newData: any) => {
|
const dataHandle = (newData: any) => {
|
||||||
if (!newData) return
|
if (!newData) return
|
||||||
const total = calculateTotal(newData)
|
props.chartConfig.option.dataset = newData;
|
||||||
|
}
|
||||||
// 更新标题总数
|
// 辅助函数:格式化日期为 YYYY-MM-DD HH:mm:ss 格式
|
||||||
props.chartConfig.option.title.text = `${total}`
|
function formatDateTime(date: Date): string {
|
||||||
// 更新图例格式化器,确保使用最新数据
|
const year = date.getFullYear();
|
||||||
props.chartConfig.option.legend.formatter = (name: string) => {
|
const month = (date.getMonth() + 1).toString().padStart(2, '0'); // 月份从0开始,所以+1
|
||||||
const item = newData.source.find((it: any) => it[newData.dimensions[0]] === name)
|
const day = date.getDate().toString().padStart(2, '0');
|
||||||
if (!item) return name;
|
const hours = date.getHours().toString().padStart(2, '0');
|
||||||
const value = item[newData.dimensions[1]] ?? 0
|
const minutes = date.getMinutes().toString().padStart(2, '0');
|
||||||
const percentage = calculatePercentage(value, total)
|
const seconds = date.getSeconds().toString().padStart(2, '0');
|
||||||
|
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
|
||||||
// const value = item[newData.dimensions[1]];
|
|
||||||
// const percentage = calculatePercentage(value, total);
|
|
||||||
|
|
||||||
return `{title|${name}}\n{detail|${value}\t${percentage}}`;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
// 更新tooplip
|
|
||||||
props.chartConfig.option.tooltip.formatter = (params: any) => {
|
|
||||||
// 获取当前数据项的名称和值
|
|
||||||
const name = params.name;
|
|
||||||
const item = newData.source.find((it: any) => it[newData.dimensions[0]] === name);
|
|
||||||
if (!item) return `${name}: 0(0.0%)`;
|
|
||||||
|
|
||||||
const value = item[newData.dimensions[1]] ?? 0;
|
|
||||||
const percentage = calculatePercentage(value, total);
|
|
||||||
|
|
||||||
return `${name}: ${value}(${percentage})`;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 聚合函数:根据周期生成时间范围
|
||||||
|
function generateTimeRange(period: string): { startTime: string; endTime: string } {
|
||||||
|
const now = new Date();
|
||||||
|
let startTime: Date;
|
||||||
|
let endTime: Date;
|
||||||
|
|
||||||
const handleBorderSelectChange = (newValue: string) => {
|
endTime = new Date(now);
|
||||||
props.chartConfig.option.selectOption.selectValue = newValue // <--- 更新本地的响应式数据
|
endTime.setDate(endTime.getDate() + 1); // 移动到明天
|
||||||
console.log('选择器值已更改为:', newValue)
|
endTime.setHours(0, 0, 0, 0); // 设置为明天的 00:00:00
|
||||||
// 更新图表数据
|
|
||||||
|
|
||||||
|
switch (period) {
|
||||||
|
case 'day':
|
||||||
|
startTime = new Date(now);
|
||||||
|
startTime.setHours(0, 0, 0, 0);
|
||||||
|
break;
|
||||||
|
case 'week':
|
||||||
|
// 本周:7 天前的 00:00:00
|
||||||
|
startTime = new Date(now);
|
||||||
|
startTime.setDate(startTime.getDate() - 7);
|
||||||
|
startTime.setHours(0, 0, 0, 0);
|
||||||
|
break;
|
||||||
|
case 'month':
|
||||||
|
// 当月:本月第一天的 00:00:00
|
||||||
|
startTime = new Date(now.getFullYear(), now.getMonth(), 1);
|
||||||
|
startTime.setHours(0, 0, 0, 0);
|
||||||
|
break;
|
||||||
|
case 'quarter':
|
||||||
|
// 本季度:当前季度第一天的 00:00:00
|
||||||
|
const currentMonth = now.getMonth();
|
||||||
|
const quarterStartMonth = Math.floor(currentMonth / 3) * 3; // 计算当前季度的起始月份 (0, 3, 6, 9)
|
||||||
|
startTime = new Date(now.getFullYear(), quarterStartMonth, 1);
|
||||||
|
startTime.setHours(0, 0, 0, 0);
|
||||||
|
break;
|
||||||
|
case 'year':
|
||||||
|
// 当年:当年第一天的 00:00:00
|
||||||
|
startTime = new Date(now.getFullYear(), 0, 1); // 月份 0 代表一月
|
||||||
|
startTime.setHours(0, 0, 0, 0);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// 如果传入的周期不匹配,默认使用 'day' 的逻辑
|
||||||
|
console.warn(`未知周期类型: ${period},默认使用 'day'`);
|
||||||
|
startTime = new Date(now);
|
||||||
|
startTime.setHours(0, 0, 0, 0);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 监听配置中的数据集变化(配置时)
|
return {
|
||||||
|
startTime: formatDateTime(startTime),
|
||||||
|
endTime: formatDateTime(endTime),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const fetchAlarmData = async ({ startTime, endTime }: { startTime: string; endTime: string }) => {
|
||||||
|
try {
|
||||||
|
const res = await axiosInstance({
|
||||||
|
method: 'GET',
|
||||||
|
url: `/space/getNumberByAlarmLevel`,
|
||||||
|
params: { startTime, endTime },
|
||||||
|
responseType: 'json',
|
||||||
|
});
|
||||||
|
if (res && (res as any).value && Array.isArray((res as any).value)) {
|
||||||
|
return {
|
||||||
|
dimensions: props.chartConfig.option.dataset.dimensions, // 保持原有的维度配置
|
||||||
|
source: (res as any).value,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
console.warn('API returned unexpected data structure:', res);
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching alarm data:', error);
|
||||||
|
return undefined; // 返回 undefined 表示获取失败
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleBorderSelectChange = async (newValue: string) => {
|
||||||
|
props.chartConfig.option.selectOption.selectValue = newValue;
|
||||||
|
const { startTime, endTime } = generateTimeRange(newValue);
|
||||||
|
const fetchedData = await fetchAlarmData({ startTime, endTime });
|
||||||
|
if (fetchedData) {
|
||||||
|
dataHandle(fetchedData);
|
||||||
|
} else {
|
||||||
|
dataHandle(undefined); // 清空图表或显示默认状态
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => props.chartConfig.option.dataset,
|
() => props.chartConfig.option.dataset,
|
||||||
(newData) => {
|
(newData) => {
|
||||||
@ -129,7 +223,7 @@ watch(
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
immediate: true,
|
immediate: true,
|
||||||
deep: false
|
deep: true
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -137,8 +231,10 @@ const { vChartRef } = useChartDataFetch(props.chartConfig, useChartEditStore, (n
|
|||||||
props.chartConfig.option.dataset = newData
|
props.chartConfig.option.dataset = newData
|
||||||
dataHandle(newData)
|
dataHandle(newData)
|
||||||
})
|
})
|
||||||
onMounted(() => {
|
onMounted(async () => {
|
||||||
dataHandle(props.chartConfig.option.dataset)
|
const timeRange = generateTimeRange(option.value.selectOption.selectValue);
|
||||||
|
const fetchedData = await fetchAlarmData(timeRange);
|
||||||
|
dataHandle(fetchedData);
|
||||||
})
|
})
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
@ -222,8 +222,9 @@
|
|||||||
{{ props.titleText }}
|
{{ props.titleText }}
|
||||||
</span>
|
</span>
|
||||||
<span class="right-select">
|
<span class="right-select">
|
||||||
<ConsumSelect v-show="props.selectOption.show" :options="props.selectOption.dataset"
|
<ConsumSelect v-show="selectOption.show" :options="selectOption.dataset"
|
||||||
:selectedValue="props.selectOption.selectValue" @change="handleSelectChange" />
|
:selectedValue="selectOption.selectValue" @change="handleSelectChange"
|
||||||
|
:select-style-config="selectStyleConfig" />
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="body">
|
<div class="body">
|
||||||
@ -282,6 +283,11 @@ const props = defineProps({
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
}, selectStyleConfig: {
|
||||||
|
default: () => ({
|
||||||
|
showAngle: false
|
||||||
|
}),
|
||||||
|
required: false
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
const emits = defineEmits(['change'])
|
const emits = defineEmits(['change'])
|
||||||
@ -296,7 +302,7 @@ const handleSelectChange = (value: any) => {
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
// --- 定义变量 ---
|
// --- 定义变量 ---
|
||||||
$border-outside-padding: 5px; // 外部容器的内边距
|
$border-outside-padding: 5px; // 外部容器的内边距
|
||||||
$bg-top-offset: 40px; // 背景SVG的top偏移量
|
$bg-top-offset: 0px; // 背景SVG的top偏移量
|
||||||
padding: $border-outside-padding;
|
padding: $border-outside-padding;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
|
||||||
@ -329,6 +335,7 @@ const handleSelectChange = (value: any) => {
|
|||||||
// 高度 = 100% - 背景的 top 偏移量
|
// 高度 = 100% - 背景的 top 偏移量
|
||||||
height: calc(100% - #{$bg-top-offset});
|
height: calc(100% - #{$bg-top-offset});
|
||||||
object-fit: scale-down;
|
object-fit: scale-down;
|
||||||
|
z-index: -2;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -345,7 +352,7 @@ const handleSelectChange = (value: any) => {
|
|||||||
// background-color: antiquewhite;
|
// background-color: antiquewhite;
|
||||||
|
|
||||||
.header {
|
.header {
|
||||||
margin-top: 8px;
|
margin-top: 5px;
|
||||||
margin-bottom: 0px;
|
margin-bottom: 0px;
|
||||||
padding: 0 20px;
|
padding: 0 20px;
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -362,7 +369,7 @@ const handleSelectChange = (value: any) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.body {
|
.body {
|
||||||
margin-top: 10px;
|
margin-top: 0px;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
@ -1,17 +1,15 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="custom-select" @click="toggleDropdown">
|
<div class="custom-select" @click="toggleDropdown">
|
||||||
<div class="select-display">
|
<div class="select-display" :style="selectStyleConfig">
|
||||||
<span class="select-text">{{ getSelectedLabel() }}</span>
|
<span class="select-text">{{ getSelectedLabel() }}</span>
|
||||||
<span class="select-arrow" :class="{ 'arrow-up': isDropdownOpen }">▼</span>
|
<span v-if="selectStyleConfig.showAngle" class="select-arrow"
|
||||||
|
:class="{ 'arrow-up': isDropdownOpen }">▼</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="select-dropdown" v-show="isDropdownOpen">
|
<div class="select-dropdown" v-show="isDropdownOpen"
|
||||||
<div
|
:style="{ backgroundColor: selectStyleConfig.backgroundColor }">
|
||||||
v-for="item in options"
|
<div v-for="item in options" :key="item.value"
|
||||||
:key="item.value"
|
:style="{ backgroundColor: selectStyleConfig.backgroundColor }" class="select-option"
|
||||||
class="select-option"
|
:class="{ 'selected': item.value === selectedValue }" @click.stop="selectOption(item)">
|
||||||
:class="{ 'selected': item.value === selectedValue }"
|
|
||||||
@click.stop="selectOption(item)"
|
|
||||||
>
|
|
||||||
{{ item.label }}
|
{{ item.label }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -24,7 +22,8 @@ import { ref, onMounted, onUnmounted } from 'vue'
|
|||||||
// 定义props
|
// 定义props
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
options: Array<{ label: string; value: any }>
|
options: Array<{ label: string; value: any }>
|
||||||
selectedValue: any
|
selectedValue: any,
|
||||||
|
selectStyleConfig: any
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
// 定义emits
|
// 定义emits
|
||||||
@ -117,7 +116,8 @@ onUnmounted(() => {
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
top: 100%;
|
top: 100%;
|
||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
width: fit-content; // 下拉菜单的宽度由其内容决定(最长选项)
|
||||||
|
min-width: 100%; // 确保最小宽度与父元素(即 .select-display)相同
|
||||||
background-color: rgb(48, 110, 100);
|
background-color: rgb(48, 110, 100);
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
margin-top: 2px;
|
margin-top: 2px;
|
||||||
@ -131,6 +131,8 @@ onUnmounted(() => {
|
|||||||
color: #fff;
|
color: #fff;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: background-color 0.2s ease;
|
transition: background-color 0.2s ease;
|
||||||
|
width: 100%;
|
||||||
|
text-wrap: nowrap;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: rgba(255, 255, 255, 0.1);
|
background-color: rgba(255, 255, 255, 0.1);
|
||||||
@ -151,6 +153,7 @@ onUnmounted(() => {
|
|||||||
opacity: 0;
|
opacity: 0;
|
||||||
transform: translateY(-10px);
|
transform: translateY(-10px);
|
||||||
}
|
}
|
||||||
|
|
||||||
to {
|
to {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
transform: translateY(0);
|
transform: translateY(0);
|
||||||
|
Loading…
Reference in New Issue
Block a user