增加折线

This commit is contained in:
zhangyue 2026-07-25 18:49:50 +08:00
parent 55ff382434
commit a42c3f5824
1 changed files with 688 additions and 633 deletions

View File

@ -86,14 +86,14 @@
</template>
<script setup>
import { ref, onMounted, onBeforeUnmount, nextTick, computed } from 'vue';
import { message } from 'ant-design-vue';
import { ReloadOutlined } from '@ant-design/icons-vue';
import * as echarts from 'echarts';
import dayjs from 'dayjs';
import { getDeviceAlertStatistic, getAllDeviceList } from './datacenter.api';
import { ref, onMounted, onBeforeUnmount, nextTick, computed } from 'vue';
import { message } from 'ant-design-vue';
import { ReloadOutlined } from '@ant-design/icons-vue';
import * as echarts from 'echarts';
import dayjs from 'dayjs';
import { getDeviceAlertStatistic, getAllDeviceList } from './datacenter.api';
const fetchCategoryOptions = () => {
const fetchCategoryOptions = () => {
return new Promise((resolve, reject) => {
getAllDeviceList({})
.then((res) => {
@ -108,10 +108,10 @@
reject(error);
});
});
};
};
// ==================== ====================
const fetchChartDataByCategory = (deployId, summaryMode, startTime) => {
// ==================== ====================
const fetchChartDataByCategory = (deployId, summaryMode, startTime) => {
return new Promise((resolve, reject) => {
const chartData = [];
const dataMap = {
@ -195,22 +195,22 @@
reject(error);
});
});
};
};
// ==================== ====================
const categoryOptions = ref([]);
const categoryLoading = ref(false);
const selectedCategory = ref('');
const chartDataList = ref([]);
const loading = ref(false);
const chartInstances = [];
const chartRefs = {};
// ==================== ====================
const categoryOptions = ref([]);
const categoryLoading = ref(false);
const selectedCategory = ref('');
const chartDataList = ref([]);
const loading = ref(false);
const chartInstances = [];
const chartRefs = {};
const selectedDimension = ref('dayhours');
const selectedDate = ref(null);
const selectedDimension = ref('dayhours');
const selectedDate = ref(null);
// ==================== ====================
const dateFormat = computed(() => {
// ==================== ====================
const dateFormat = computed(() => {
switch (selectedDimension.value) {
case 'dayhours':
return 'YYYY-MM-DD';
@ -221,9 +221,9 @@
default:
return 'YYYY-MM-DD';
}
});
});
const pickerType = computed(() => {
const pickerType = computed(() => {
switch (selectedDimension.value) {
case 'dayhours':
return 'date';
@ -234,9 +234,9 @@
default:
return 'date';
}
});
});
const datePlaceholder = computed(() => {
const datePlaceholder = computed(() => {
switch (selectedDimension.value) {
case 'dayhours':
return '选择日期';
@ -247,27 +247,27 @@
default:
return '选择日期';
}
});
});
// ==================== ====================
const filterOption = (input, option) => {
// ==================== ====================
const filterOption = (input, option) => {
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0;
};
};
const getCategoryLabel = (value) => {
const getCategoryLabel = (value) => {
const found = categoryOptions.value.find((item) => item.id === value);
return found ? found.deployDes : value;
};
};
// ==================== DOM ====================
const setChartRef = (el, index) => {
// ==================== DOM ====================
const setChartRef = (el, index) => {
if (el) {
chartRefs[index] = el;
}
};
};
// ==================== ====================
const loadCategories = async () => {
// ==================== ====================
const loadCategories = async () => {
categoryLoading.value = true;
try {
const res = await fetchCategoryOptions();
@ -282,12 +282,15 @@
} finally {
categoryLoading.value = false;
}
};
};
// ==================== deployId, summaryMode, startTime ====================
const loadChartData = async (deployId, summaryMode, startTime) => {
// ==================== deployId, summaryMode, startTime ====================
const loadChartData = async (deployId, summaryMode, startTime) => {
if (!deployId) return;
// tag 0
chartDataList.value = [];
loading.value = true;
try {
//
@ -312,19 +315,19 @@
} finally {
loading.value = false;
}
};
};
// ==================== ====================
const handleSearch = () => {
// ==================== ====================
const handleSearch = () => {
if (!selectedCategory.value) {
message.warning('请先选择设备');
return;
}
loadChartData(selectedCategory.value, selectedDimension.value, selectedDate.value);
};
};
// ==================== ====================
const initCharts = () => {
// ==================== ====================
const initCharts = () => {
chartInstances.forEach((instance) => {
if (instance._resizeHandler) {
window.removeEventListener('resize', instance._resizeHandler);
@ -356,16 +359,16 @@
window.addEventListener('resize', resizeHandler);
chart._resizeHandler = resizeHandler;
});
};
};
// ==================== ====================
const roundToTwo = (num) => {
// ==================== ====================
const roundToTwo = (num) => {
if (num === undefined || num === null || isNaN(num)) return num;
return Math.round(num * 100) / 100;
};
};
// ==================== y markLine ====================
const calculateYAxisRange = (data, markLines) => {
// ==================== y markLine ====================
const calculateYAxisRange = (data, markLines) => {
if (!data || data.length === 0) {
return { min: null, max: null };
}
@ -410,10 +413,10 @@
min: roundToTwo(dataMin - padding),
max: roundToTwo(dataMax + padding),
};
};
};
// ==================== ECharts ====================
const getChartOption = (item) => {
// ==================== ECharts ====================
const getChartOption = (item) => {
// y markLine
const yAxisRange = calculateYAxisRange(item.data, item.markLines);
@ -531,6 +534,51 @@
},
},
},
// 线
{
name: item.title || '趋势',
type: 'line',
data: item.data || [],
smooth: true,
symbol: 'circle',
symbolSize: 6,
lineStyle: {
color: '#ff6b6b',
width: 2,
},
itemStyle: {
color: '#ff6b6b',
},
areaStyle: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{ offset: 0, color: 'rgba(255, 107, 107, 0.3)' },
{ offset: 1, color: 'rgba(255, 107, 107, 0.05)' },
],
},
},
emphasis: {
focus: 'series',
},
label: {
show: true,
position: 'top',
fontSize: 10,
color: '#ff6b6b',
fontWeight: 500,
formatter: (params) => {
return params.value;
},
},
// 线 x
xAxisIndex: 0,
yAxisIndex: 0,
},
],
legend: {
show: true,
@ -544,6 +592,13 @@
name: item.title || '数据',
icon: 'roundRect',
},
{
name: item.title || '趋势',
icon: 'line',
itemStyle: {
color: '#ff6b6b',
},
},
...(item.markLines || []).map((line) => ({
name: line.name,
icon: 'line',
@ -554,14 +609,14 @@
],
},
};
};
};
// ==================== ====================
onMounted(async () => {
// ==================== ====================
onMounted(async () => {
await loadCategories();
});
});
onBeforeUnmount(() => {
onBeforeUnmount(() => {
chartInstances.forEach((instance) => {
if (instance._resizeHandler) {
window.removeEventListener('resize', instance._resizeHandler);
@ -571,103 +626,103 @@
}
});
chartInstances.length = 0;
});
});
defineExpose({
defineExpose({
handleSearch,
loadChartData,
selectedCategory,
categoryOptions,
selectedDimension,
selectedDate,
});
});
</script>
<style scoped>
.dashboard-charts {
.dashboard-charts {
padding: 24px;
background: #f0f2f5;
min-height: 100vh;
}
}
/* ==================== 筛选区域样式 ==================== */
.filter-section {
/* ==================== 筛选区域样式 ==================== */
.filter-section {
margin-bottom: 20px;
}
}
.filter-card {
.filter-card {
border-radius: 12px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
}
}
.filter-card :deep(.ant-card-body) {
.filter-card :deep(.ant-card-body) {
padding: 16px 20px;
}
}
.filter-item {
.filter-item {
display: flex;
align-items: center;
gap: 6px;
}
}
.filter-label {
.filter-label {
font-size: 13px;
font-weight: 500;
color: #1d2129;
white-space: nowrap;
min-width: 32px;
}
}
/* ==================== 图表区域样式 ==================== */
.chart-grid {
/* ==================== 图表区域样式 ==================== */
.chart-grid {
margin: 0 -8px;
}
}
.chart-col {
.chart-col {
padding: 0 8px;
margin-bottom: 16px;
}
}
.chart-card {
.chart-card {
border-radius: 12px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
transition: all 0.3s ease;
height: 100%;
display: flex;
flex-direction: column;
}
}
.chart-card:hover {
.chart-card:hover {
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
transform: translateY(-2px);
}
}
.chart-card :deep(.ant-card-head) {
.chart-card :deep(.ant-card-head) {
padding: 0 20px;
min-height: 52px;
border-bottom: 1px solid #f0f0f0;
background: #fafafa;
border-radius: 12px 12px 0 0;
}
}
.chart-card :deep(.ant-card-head-title) {
.chart-card :deep(.ant-card-head-title) {
padding: 14px 0;
font-weight: 500;
}
}
.chart-card :deep(.ant-card-body) {
.chart-card :deep(.ant-card-body) {
padding: 16px 12px 12px 12px;
flex: 1;
}
}
.card-header {
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
}
}
.chart-title {
.chart-title {
font-size: 14px;
font-weight: 600;
color: #1d2129;
@ -676,32 +731,32 @@
overflow: hidden;
text-overflow: ellipsis;
max-width: 70%;
}
}
.chart-container {
.chart-container {
width: 100%;
height: 280px;
display: flex;
align-items: center;
justify-content: center;
}
}
.chart-instance {
.chart-instance {
width: 100%;
height: 280px;
}
}
/* 空状态样式 */
.empty-state {
/* 空状态样式 */
.empty-state {
margin-top: 60px;
background: #ffffff;
padding: 60px 0;
border-radius: 16px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.04);
}
}
/* ==================== 响应式调整 ==================== */
@media screen and (max-width: 768px) {
/* ==================== 响应式调整 ==================== */
@media screen and (max-width: 768px) {
.dashboard-charts {
padding: 16px;
}
@ -738,9 +793,9 @@
.chart-card :deep(.ant-card-body) {
padding: 12px 8px 8px 8px;
}
}
}
@media screen and (max-width: 480px) {
@media screen and (max-width: 480px) {
.chart-container {
height: 200px;
}
@ -757,5 +812,5 @@
padding: 10px 0;
font-size: 13px;
}
}
}
</style>