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 cloneDeep from 'lodash/cloneDeep'
|
||||
import dataJson from './data.json'
|
||||
import { pad, padEnd } from 'lodash'
|
||||
import { calcPadding } from '@visactor/vchart/esm/util'
|
||||
|
||||
export const includes = ['legend']
|
||||
|
||||
|
||||
export const selectStyleOption = {
|
||||
showAngle: false,
|
||||
color: '#1B9FCC',
|
||||
backgroundColor: '#0F4557',
|
||||
borderTop: '1px solid #1CA4D2',
|
||||
cursor: 'pointer',
|
||||
borderRadius: '2px',
|
||||
}
|
||||
const otherConfig = {
|
||||
// 轮播动画
|
||||
isCarousel: false,
|
||||
@ -16,9 +21,26 @@ const otherConfig = {
|
||||
show: true,
|
||||
selectValue: 'day',
|
||||
dataset: [
|
||||
{ label: '今日', value: 'day' },
|
||||
{ label: '本周', value: 'week' },
|
||||
{ label: '本月', value: 'month' }
|
||||
{
|
||||
label: '当天',
|
||||
value: 'day'
|
||||
},
|
||||
{
|
||||
label: '本周',
|
||||
value: 'week'
|
||||
},
|
||||
{
|
||||
label: '当月',
|
||||
value: 'month'
|
||||
},
|
||||
{
|
||||
label: '本季度',
|
||||
value: 'quarter'
|
||||
},
|
||||
{
|
||||
label: '当年',
|
||||
value: 'year'
|
||||
}
|
||||
]
|
||||
},
|
||||
titleText: '今日数据',
|
||||
@ -34,6 +56,8 @@ const otherConfig = {
|
||||
paddingRight: 0,
|
||||
paddingTop: 0,
|
||||
paddingBottom: 0
|
||||
}, selectStyleOption: {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,26 +1,26 @@
|
||||
{
|
||||
"dimensions": [
|
||||
"name",
|
||||
"value",
|
||||
"alarmLevel",
|
||||
"count",
|
||||
"itemColor",
|
||||
"borderColor"
|
||||
],
|
||||
"source": [
|
||||
{
|
||||
"name": "分类一",
|
||||
"value": 94
|
||||
"alarmLevel": "分类一",
|
||||
"count": 94
|
||||
},
|
||||
{
|
||||
"name": "分类二",
|
||||
"value": 82
|
||||
"alarmLevel": "分类3",
|
||||
"count": 4
|
||||
},
|
||||
{
|
||||
"name": "分类三",
|
||||
"value": 78
|
||||
"alarmLevel": "分类2",
|
||||
"count": 30
|
||||
},
|
||||
{
|
||||
"name": "分类四",
|
||||
"value": 60
|
||||
"alarmLevel": "分类4",
|
||||
"count": 40
|
||||
}
|
||||
]
|
||||
}
|
@ -1,7 +1,9 @@
|
||||
<template>
|
||||
<div class="go-border-box">
|
||||
<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"
|
||||
autoresize> </v-chart>
|
||||
</SmallBaorder01>
|
||||
@ -16,7 +18,7 @@ import { use } from 'echarts/core'
|
||||
import { CanvasRenderer } from 'echarts/renderers'
|
||||
import { PieChart } from 'echarts/charts'
|
||||
import { mergeTheme } from '@/packages/public/chart'
|
||||
import config, { includes } from './config'
|
||||
import config, { includes, selectStyleOption } from './config'
|
||||
import { useChartDataFetch } from '@/hooks'
|
||||
import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
|
||||
import SmallBaorder01 from '../SmallBorder01Co/index.vue'
|
||||
@ -28,6 +30,7 @@ import {
|
||||
TitleComponent, GraphicComponent
|
||||
} from 'echarts/components'
|
||||
|
||||
import axiosInstance from '@/api/axios'
|
||||
|
||||
|
||||
const props = defineProps({
|
||||
@ -56,9 +59,44 @@ use([
|
||||
TitleComponent,
|
||||
GraphicComponent
|
||||
])
|
||||
// const option = computed(() => {
|
||||
// return mergeTheme(props.chartConfig.option, props.themeSetting, includes)
|
||||
// })
|
||||
|
||||
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) => {
|
||||
if (!data || !data.source) return 0
|
||||
@ -76,48 +114,104 @@ const calculatePercentage = (value: number, total: number) => {
|
||||
// 处理数据更新
|
||||
const dataHandle = (newData: any) => {
|
||||
if (!newData) return
|
||||
const total = calculateTotal(newData)
|
||||
|
||||
// 更新标题总数
|
||||
props.chartConfig.option.title.text = `${total}`
|
||||
// 更新图例格式化器,确保使用最新数据
|
||||
props.chartConfig.option.legend.formatter = (name: string) => {
|
||||
const item = newData.source.find((it: any) => it[newData.dimensions[0]] === name)
|
||||
if (!item) return name;
|
||||
const value = item[newData.dimensions[1]] ?? 0
|
||||
const percentage = calculatePercentage(value, total)
|
||||
|
||||
// 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})`;
|
||||
};
|
||||
|
||||
props.chartConfig.option.dataset = newData;
|
||||
}
|
||||
// 辅助函数:格式化日期为 YYYY-MM-DD HH:mm:ss 格式
|
||||
function formatDateTime(date: Date): string {
|
||||
const year = date.getFullYear();
|
||||
const month = (date.getMonth() + 1).toString().padStart(2, '0'); // 月份从0开始,所以+1
|
||||
const day = date.getDate().toString().padStart(2, '0');
|
||||
const hours = date.getHours().toString().padStart(2, '0');
|
||||
const minutes = date.getMinutes().toString().padStart(2, '0');
|
||||
const seconds = date.getSeconds().toString().padStart(2, '0');
|
||||
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
|
||||
}
|
||||
|
||||
// 聚合函数:根据周期生成时间范围
|
||||
function generateTimeRange(period: string): { startTime: string; endTime: string } {
|
||||
const now = new Date();
|
||||
let startTime: Date;
|
||||
let endTime: Date;
|
||||
|
||||
const handleBorderSelectChange = (newValue: string) => {
|
||||
props.chartConfig.option.selectOption.selectValue = newValue // <--- 更新本地的响应式数据
|
||||
console.log('选择器值已更改为:', newValue)
|
||||
// 更新图表数据
|
||||
endTime = new Date(now);
|
||||
endTime.setDate(endTime.getDate() + 1); // 移动到明天
|
||||
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(
|
||||
() => props.chartConfig.option.dataset,
|
||||
(newData) => {
|
||||
@ -129,7 +223,7 @@ watch(
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
deep: false
|
||||
deep: true
|
||||
}
|
||||
)
|
||||
|
||||
@ -137,8 +231,10 @@ const { vChartRef } = useChartDataFetch(props.chartConfig, useChartEditStore, (n
|
||||
props.chartConfig.option.dataset = newData
|
||||
dataHandle(newData)
|
||||
})
|
||||
onMounted(() => {
|
||||
dataHandle(props.chartConfig.option.dataset)
|
||||
onMounted(async () => {
|
||||
const timeRange = generateTimeRange(option.value.selectOption.selectValue);
|
||||
const fetchedData = await fetchAlarmData(timeRange);
|
||||
dataHandle(fetchedData);
|
||||
})
|
||||
|
||||
</script>
|
||||
|
@ -222,8 +222,9 @@
|
||||
{{ props.titleText }}
|
||||
</span>
|
||||
<span class="right-select">
|
||||
<ConsumSelect v-show="props.selectOption.show" :options="props.selectOption.dataset"
|
||||
:selectedValue="props.selectOption.selectValue" @change="handleSelectChange" />
|
||||
<ConsumSelect v-show="selectOption.show" :options="selectOption.dataset"
|
||||
:selectedValue="selectOption.selectValue" @change="handleSelectChange"
|
||||
:select-style-config="selectStyleConfig" />
|
||||
</span>
|
||||
</div>
|
||||
<div class="body">
|
||||
@ -282,6 +283,11 @@ const props = defineProps({
|
||||
}
|
||||
]
|
||||
})
|
||||
}, selectStyleConfig: {
|
||||
default: () => ({
|
||||
showAngle: false
|
||||
}),
|
||||
required: false
|
||||
}
|
||||
})
|
||||
const emits = defineEmits(['change'])
|
||||
@ -296,7 +302,7 @@ const handleSelectChange = (value: any) => {
|
||||
height: 100%;
|
||||
// --- 定义变量 ---
|
||||
$border-outside-padding: 5px; // 外部容器的内边距
|
||||
$bg-top-offset: 40px; // 背景SVG的top偏移量
|
||||
$bg-top-offset: 0px; // 背景SVG的top偏移量
|
||||
padding: $border-outside-padding;
|
||||
box-sizing: border-box;
|
||||
|
||||
@ -329,6 +335,7 @@ const handleSelectChange = (value: any) => {
|
||||
// 高度 = 100% - 背景的 top 偏移量
|
||||
height: calc(100% - #{$bg-top-offset});
|
||||
object-fit: scale-down;
|
||||
z-index: -2;
|
||||
|
||||
}
|
||||
}
|
||||
@ -345,7 +352,7 @@ const handleSelectChange = (value: any) => {
|
||||
// background-color: antiquewhite;
|
||||
|
||||
.header {
|
||||
margin-top: 8px;
|
||||
margin-top: 5px;
|
||||
margin-bottom: 0px;
|
||||
padding: 0 20px;
|
||||
display: flex;
|
||||
@ -362,7 +369,7 @@ const handleSelectChange = (value: any) => {
|
||||
}
|
||||
|
||||
.body {
|
||||
margin-top: 10px;
|
||||
margin-top: 0px;
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
|
@ -1,17 +1,15 @@
|
||||
<template>
|
||||
<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-arrow" :class="{ 'arrow-up': isDropdownOpen }">▼</span>
|
||||
<span v-if="selectStyleConfig.showAngle" class="select-arrow"
|
||||
:class="{ 'arrow-up': isDropdownOpen }">▼</span>
|
||||
</div>
|
||||
<div class="select-dropdown" v-show="isDropdownOpen">
|
||||
<div
|
||||
v-for="item in options"
|
||||
:key="item.value"
|
||||
class="select-option"
|
||||
:class="{ 'selected': item.value === selectedValue }"
|
||||
@click.stop="selectOption(item)"
|
||||
>
|
||||
<div class="select-dropdown" v-show="isDropdownOpen"
|
||||
:style="{ backgroundColor: selectStyleConfig.backgroundColor }">
|
||||
<div v-for="item in options" :key="item.value"
|
||||
:style="{ backgroundColor: selectStyleConfig.backgroundColor }" class="select-option"
|
||||
:class="{ 'selected': item.value === selectedValue }" @click.stop="selectOption(item)">
|
||||
{{ item.label }}
|
||||
</div>
|
||||
</div>
|
||||
@ -24,7 +22,8 @@ import { ref, onMounted, onUnmounted } from 'vue'
|
||||
// 定义props
|
||||
const props = defineProps<{
|
||||
options: Array<{ label: string; value: any }>
|
||||
selectedValue: any
|
||||
selectedValue: any,
|
||||
selectStyleConfig: any
|
||||
}>()
|
||||
|
||||
// 定义emits
|
||||
@ -117,7 +116,8 @@ onUnmounted(() => {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
right: 0;
|
||||
width: fit-content; // 下拉菜单的宽度由其内容决定(最长选项)
|
||||
min-width: 100%; // 确保最小宽度与父元素(即 .select-display)相同
|
||||
background-color: rgb(48, 110, 100);
|
||||
border-radius: 6px;
|
||||
margin-top: 2px;
|
||||
@ -131,6 +131,8 @@ onUnmounted(() => {
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s ease;
|
||||
width: 100%;
|
||||
text-wrap: nowrap;
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
@ -151,6 +153,7 @@ onUnmounted(() => {
|
||||
opacity: 0;
|
||||
transform: translateY(-10px);
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
|
Loading…
Reference in New Issue
Block a user