提交 b548a9ad authored 作者: 汪雄's avatar 汪雄

完成曲线模板配置流程

上级 3b22b605
......@@ -118,9 +118,56 @@ export function getCapacityAndTraffic(data) {
granularity: data.granularity,
startTime: data.startTime,
endTime: data.endTime,
regionName:data.regionName || null,
regionName: data.regionName || null,
signalSiteCode: data.signalSiteCode || null,
},
});
}
//规划可用性
export function getAvailability(data) {
return request({
url: "/commPanel/getAvailability",
method: "get",
params: {
gridCodes: data.gridCodes || null,
granularity: data.granularity,
startTime: data.startTime,
endTime: data.endTime,
regionName: data.regionName || null,
signalSiteCode: data.signalSiteCode || null,
},
});
}
//接入时延(搜星)
export function getDelay(data) {
return request({
url: "/commPanel/getDelay",
method: "get",
params: {
gridCodes: data.gridCodes || null,
granularity: data.granularity,
startTime: data.startTime,
endTime: data.endTime,
regionName: data.regionName || null,
signalSiteCode: data.signalSiteCode || null,
},
});
}
//丢包率获取
export function getLossRate(data) {
return request({
url: "/commPanel/getLossRate",
method: "get",
params: {
gridCodes: data.gridCodes || null,
granularity: data.granularity,
startTime: data.startTime,
endTime: data.endTime,
regionName: data.regionName || null,
signalSiteCode: data.signalSiteCode || null,
},
});
}
......@@ -131,4 +178,51 @@ export function getGridInfoList() {
url: "/GridInfo/list",
method: "get",
});
}
\ No newline at end of file
}
//模板配置列表
export function getTemplateList() {
return request({
url: "/templateApi/list",
method: "get",
});
}
//模板配置新增
export function templateAdd(data) {
return request({
url: "/templateApi/add",
method: "post",
data,
});
}
//模板配置删除
export function templateDel({ data }) {
return request({
url: "/templateApi/delete",
method: "get",
params: {
templateId: data.templateId,
},
});
}
//模板配置详情
export function templateDetail({ data }) {
return request({
url: "/templateApi/detail",
method: "get",
params: {
templateId: data.templateId,
},
});
}
//模板配置修改
export function templateuUpdate({ data }) {
return request({
url: "/templateApi/update",
method: "POST",
data,
});
}
<template>
<!-- CDF曲线图 -->
<!-- PDF曲线图 -->
<div class="container">
<div class="main">
<div :id="idName" class="cdfCurve"></div>
<div class="showDetial" @click="showViewClick" v-if="isShowDetial">
<el-icon v-if="showView" class="icon"><View /></el-icon>
<el-icon v-else class="icon"><Hide /></el-icon>
<el-icon v-if="showView" class="icon">
<View />
</el-icon>
<el-icon v-else class="icon">
<Hide />
</el-icon>
<span>详情</span>
</div>
<div class="showValue">
<div v-for="(item, index) in markLineData" :key="index">
<span style="font-weight: 700;font-size: 10px; padding: 0 5.5px;" :style="{ color: item.color }">--</span>{{ item.descript }}
<span style="font-weight: 700;font-size: 10px; padding: 0 5.5px;" :style="{ color: item.color }">--</span>{{
item.descript }}
</div>
</div>
</div>
<el-dialog
v-model="isDetialDialogVisible"
:show-close="false"
:modal="false"
custom-class="custom-cdf-dialog"
width="200"
height="100"
:destroy-on-close="true"
:before-close="isDetialDialogVisibleHandleClose"
style="margin: 0;padding: 0; background-color: rgba(0, 0, 0, 0);"
>
<childrenCdfCurve ref="childrenCdfRef" :propsDate="propsDate" @isDetialDialogVisibleHandleClose="isDetialDialogVisibleHandleClose" />
</el-dialog>
<el-dialog v-model="isDetialDialogVisible" :show-close="false" :modal="false" custom-class="custom-cdf-dialog"
width="200" height="100" :destroy-on-close="false" :before-close="isDetialDialogVisibleHandleClose"
style="margin: 0;padding: 0; background-color: rgba(0, 0, 0, 0);">
<childrenCdfCurve ref="childrenCdfRef" :propsDate="propsDate"
@isDetialDialogVisibleHandleClose="isDetialDialogVisibleHandleClose" />
</el-dialog>
</div>
</template>
......@@ -41,15 +38,11 @@ export default {
import * as echarts from 'echarts';
import childrenCdfCurve from './childrenCdfCurve.vue'
import { onMounted, onUnmounted, ref } from 'vue';
import { onMounted, onUnmounted, ref, nextTick } from 'vue';
const idName = ref(Math.random().toString(30).slice(2, 8))
const props = defineProps({
idName: {
type: String,
default: 'main'
},
propsDate: {
type: Object,
default: () => ({})
......@@ -83,30 +76,32 @@ function showViewClick() {
// //console.log("时间");
isDetialDialogVisible.value = !isDetialDialogVisible.value;
showView.value = true;
if (window.ue5) {
window.ue5("callBackAllDialogFn", String(JSON.stringify({
type:"CDFCurveDetailDialog",
status:"open"
})));
}
if (window.ue5) {
window.ue5("callBackAllDialogFn", String(JSON.stringify({
type: "CDFCurveDetailDialog",
status: "open"
})));
}
// //console.log("当前的值:",showView.value);
}
function isDetialDialogVisibleHandleClose() {
isDetialDialogVisible.value = false;
showView.value = false;
if (window.ue5) {
window.ue5("callBackAllDialogFn", String(JSON.stringify({
type:"CDFCurveDetailDialog",
status:"close"
})));
}
if (window.ue5) {
window.ue5("callBackAllDialogFn", String(JSON.stringify({
type: "CDFCurveDetailDialog",
status: "close"
})));
}
}
onMounted(() => {
initChart();
nextTick(() => {
initChart();
})
//console.log("-------cdfCurve-------",props.idName);
document.getElementById(props.idName).style.height = '240px';
// document.getElementById(props.idName).style.height = '240px';
});
onUnmounted(() => {
......@@ -117,7 +112,7 @@ onUnmounted(() => {
});
function initChart() {
const chartDom = document.getElementById(props.idName);
const chartDom = document.getElementById(idName.value);
chartDom.style.height = '180px';
chartDom.style.width = '170px';
myChart = echarts.init(chartDom);
......@@ -125,7 +120,7 @@ function initChart() {
const option = {
legend: {
show: true,
data: ['CDF曲线图'],
data: ['PDF曲线图'],
textStyle: {
color: '#fff',
fontSize: 12
......@@ -139,7 +134,7 @@ function initChart() {
icon: 'rect', // 使用矩形图标
itemWidth: 15, // 图例宽度
itemHeight: 5, // 图例高度,设为较小值形成线条效果
},
grid: {
top: '23%',
......@@ -180,7 +175,7 @@ function initChart() {
},
series: [
{
name: 'CDF曲线图',
name: 'PDF曲线图',
data: [12, 13, 5, 103, 19, 3, 18],
type: 'line',
smooth: 0.6,
......@@ -214,24 +209,24 @@ function initChart() {
label: {
show: false
},
data: [
{
yAxis: 45,
lineStyle: {
color: '#ff0000',
width: 2,
type: 'dashed'
},
},
{
yAxis: 70,
lineStyle: {
color: '#ff55f5',
width: 2,
type: 'dashed'
},
}
]
// data: [
// {
// yAxis: 45,
// lineStyle: {
// color: '#ff0000',
// width: 2,
// type: 'dashed'
// },
// },
// {
// yAxis: 70,
// lineStyle: {
// color: '#ff55f5',
// width: 2,
// type: 'dashed'
// },
// }
// ]
}
}
],
......@@ -292,11 +287,13 @@ function resizeChart() {
position: relative;
z-index: 1;
}
.cdfCurve div{
.cdfCurve div {
width: 100%;
height: 100%;
}
.showDetial{
.showDetial {
position: absolute;
/* flex-direction: row; */
font: 12px "微软雅黑";
......@@ -308,19 +305,23 @@ function resizeChart() {
/* justify-content: center;
align-items: center; */
}
.showDetial .icon{
.showDetial .icon {
margin: 0;
padding: 0;
top: 2px;
}
.showDetial span{
}
.showDetial span {
padding-left: 5px;
}
.showDetial:hover{
.showDetial:hover {
/* background-color: rgba(255, 255, 255, 0.91); */
color: #607FB0;
}
/* .custom-cdf-dialog{
position: absolute;
left: 100px;
......
......@@ -7,7 +7,7 @@
</div>
<button class="close-btn" @click="closePanel">×</button>
</div>
<!-- 设备列表内容区 -->
<div class="content">
<div class="content_header">
......@@ -17,28 +17,12 @@
type="datetimerange"
/> -->
<el-date-picker
popper-class="custom-date-popper"
v-model="dateRange"
type="daterange"
size="small"
unlink-panels
range-separator="->"
start-placeholder="开始时间"
end-placeholder="结束时间"
format="YYYY-MM-DD HH:mm:ss"
date-format="YYYY/MM/DD ddd"
time-format="A hh:mm:ss"
:default-time="defaultDateTimeRange"
:disabled-date="disabledDate"
:disabled-hours="disabledHours"
:cell-class-name="(date) => 'date-picker-custom'"
/>
<el-button @click="handleDateRangeChange" size="small">查询</el-button>
<el-date-picker popper-class="custom-date-popper" v-model="dateRange" type="daterange" size="small"
unlink-panels range-separator="->" start-placeholder="开始时间" end-placeholder="结束时间"
format="YYYY-MM-DD HH:mm:ss" date-format="YYYY/MM/DD ddd" time-format="A hh:mm:ss"
:default-time="defaultDateTimeRange" :disabled-date="disabledDate" :disabled-hours="disabledHours"
:cell-class-name="(date) => 'date-picker-custom'" />
<el-button @click="handleDateRangeChange" size="small">查询</el-button>
</div>
<div id="ghCurve" class="cdfCurve"></div>
<div id="sjCurve" class="cdfCurve"></div>
......@@ -54,8 +38,8 @@ import * as echarts from 'echarts';
const props = defineProps(['devices','isDetialDialogVisible'])
const emits = defineEmits(['errorDialoghandleClose','isDetialDialogVisibleHandleClose'])
const props = defineProps(['devices', 'isDetialDialogVisible'])
const emits = defineEmits(['errorDialoghandleClose', 'isDetialDialogVisibleHandleClose'])
let myCharts = []; // 存储多个图表实例
const lineColor = ['#2e7dff', '#6FFCBA', '#7590bc', '#ff95c7'];
......@@ -68,7 +52,7 @@ const chartConfigs = ref([
{
name: '空口链路丢包率',
data: [12, 13, 85, 53, 19, 23, 18],
Xdata: [1,2,3,4,5,6,7],
Xdata: [1, 2, 3, 4, 5, 6, 7],
colorIndex: 0
}
]
......@@ -79,7 +63,7 @@ const chartConfigs = ref([
{
name: '网络延迟',
data: [25, 35, 65, 45, 30, 40, 28],
Xdata: [1,2,3,4,5,6,7],
Xdata: [1, 2, 3, 4, 5, 6, 7],
colorIndex: 1
}
]
......@@ -104,7 +88,7 @@ const chartConfigs = ref([
]);
const defaultDateTimeRange = ref([
new Date(new Date().getFullYear(), new Date().getMonth() + 1, new Date().getDate(), 0, 0, 0),
new Date(new Date().getFullYear(), new Date().getMonth() + 1, new Date().getDate(), 0, 0, 0),
......@@ -121,74 +105,74 @@ function formatDate(date) {
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
}
const dateRange = ref([]);
function handleDateRangeChange() {
function handleDateRangeChange() {
const submitData = {
startTime: formatDate(dateRange.value[0]),
endTime: formatDate(dateRange.value[1])
}
//console.log("提交:",submitData);
if (submitData.startTime && submitData.endTime) {
chartConfigs.value = [
{
id: 'ghCurve',
series: [
if (submitData.startTime && submitData.endTime) {
chartConfigs.value = [
{
name: '空口链路450Mbps-丢包率',
data: [12, 3, 5, 93, 19, 23, 18],
Xdata: [1,2,3,4,5,6,7],
colorIndex: 0
}
]
},
{
id: 'sjCurve',
series: [
{
name: '网络cxcz延迟',
data: [25, 95, 15, 5, 30, 40, 28],
Xdata: [1,2,3,4,5,6,7],
colorIndex: 1
}
]
},
{
id: 'dblCurve',
series: [
id: 'ghCurve',
series: [
{
name: '空口链路450Mbps-丢包率',
data: [12, 3, 5, 93, 19, 23, 18],
Xdata: [1, 2, 3, 4, 5, 6, 7],
colorIndex: 0
}
]
},
{
name: '发送速率size',
data: [20, 70, 60, 25, 90, 22, 60, 18, 12, 28, 12, 56],
Xdata: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12],
colorIndex: 2
id: 'sjCurve',
series: [
{
name: '网络cxcz延迟',
data: [25, 95, 15, 5, 30, 40, 28],
Xdata: [1, 2, 3, 4, 5, 6, 7],
colorIndex: 1
}
]
},
{
name: '接收速率size',
data: [110, 18, 60, 18, 12, 28, 12, 56, 89, 21, 7, 25],
Xdata: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12],
colorIndex: 3
id: 'dblCurve',
series: [
{
name: '发送速率size',
data: [20, 70, 60, 25, 90, 22, 60, 18, 12, 28, 12, 56],
Xdata: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12],
colorIndex: 2
},
{
name: '接收速率size',
data: [110, 18, 60, 18, 12, 28, 12, 56, 89, 21, 7, 25],
Xdata: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12],
colorIndex: 3
}
]
}
]
}
];
myCharts.forEach(chart => {
if (chart) {
chart.dispose();
}
});
chartConfigs.value.forEach(config => {
const chartDom = document.getElementById(config.id);
if (chartDom) {
initChart(chartDom, config);
// resizeChart()
}
});
}else {
];
myCharts.forEach(chart => {
if (chart) {
chart.dispose();
}
});
chartConfigs.value.forEach(config => {
const chartDom = document.getElementById(config.id);
if (chartDom) {
initChart(chartDom, config);
// resizeChart()
}
});
} else {
ElMessage({
type: 'warning',
message: '完善开始时间和结束时间的选择',
});
}
}
// 禁用未来日期
const disabledDate = (time) => {
......@@ -197,6 +181,8 @@ const disabledDate = (time) => {
onMounted(() => {
// 初始化所有图表
chartConfigs.value.forEach(config => {
alert(config)
console.log('初始化config', config)
const chartDom = document.getElementById(config.id);
if (chartDom) {
initChart(chartDom, config);
......@@ -217,10 +203,10 @@ onUnmounted(() => {
function initChart(chartDom, config) {
const myChart = echarts.init(chartDom);
myCharts.push(myChart); // 存储图表实例
// 根据series配置动态构建legend数据
const legendData = config.series.map(series => series.name);
// 动态构建series配置
const seriesOptions = config.series.map((seriesItem) => {
const color = lineColor[seriesItem.colorIndex];
......@@ -327,11 +313,11 @@ function initChart(chartDom, config) {
};
myChart.setOption(option);
// 添加resize处理
const handleResize = () => myChart.resize();
window.addEventListener('resize', handleResize);
// 保存resize处理函数以便后续可能需要移除
myChart._resizeHandler = handleResize;
}
......@@ -364,7 +350,7 @@ const closePanel = () => {
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.header {
......@@ -407,7 +393,7 @@ const closePanel = () => {
.close-btn {
width: 15px;
height: 15px;
background: #1E1E2D;
background: #1E1E2D;
border: none;
color: #aaa;
font-size: 24px;
......@@ -433,10 +419,11 @@ const closePanel = () => {
background: #000000;
}
.cdfCurve{
.cdfCurve {
width: 900px;
height: 220px;
}
.content_header {
width: 55%;
display: flex;
......@@ -445,41 +432,50 @@ const closePanel = () => {
gap: 10px;
/* background-color: #2a2a3a; */
}
.content_header span {
width: 60px;
font-size: 11px;
color: #e0e0e0;
}
:deep(.el-date-editor) {
background-color: #000000;
box-shadow: 0 0 2px rgba(131, 131, 131, 0.95);
}
:deep(.el-button) {
background: #000000;
background: #000000;
border: none;
color: #e0e0e0;
width: 70px;
}
:deep(.el-range-input){
:deep(.el-range-input) {
color: #ffffff;
}
:deep(.el-range-separator){
:deep(.el-range-separator) {
color: #ffffff;
}
:deep(.el-button:hover) {
background: linear-gradient(to bottom, #2C76F1, #000000);
border: none;
border: none;
box-shadow: none;
}
:deep(.custom-date-popper) {
background-color: #1e1e2d;
border: 1px solid #383850;
}
:deep(.date-picker-custom) {
background-color: #000000;
box-shadow: none;
}
/* .date-picker-custom :deep(.el-range-editor) {
width: 10%;
height: 100%;
......
......@@ -7,33 +7,27 @@
<div class="divider-line"></div>
</div>
<div class="hot-title">
<div class="hot-title-text">巴西-最小值(CDF曲线图)</div>
<div class="hot-title-enlarge">
<svg width="20" height="20" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="0.5" y="0.5" width="31" height="31" rx="3.5" stroke="white" stroke-opacity="0.65"/>
<path
class="arrow-path "
d="M23.5002 8.5V13.5H21.8335V11.4043L18.0119 15.2259L16.8334 14.0474L20.7141 10.1667H18.5002V8.5H23.5002Z"
fill="white"
fill-opacity="0.65"
/>
<path
class="arrow-path "
d="M8.5 23.5592V18.5592H10.1667V20.6549L13.9883 16.8333L15.1668 18.0118L11.2861 21.8925H13.5V23.5592H8.5Z"
fill="white"
fill-opacity="0.65"
/>
</svg>
</div>
<div class="hot-title-text">{{ props.title }}</div>
<div class="hot-title-enlarge">
<svg width="20" height="20" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="0.5" y="0.5" width="31" height="31" rx="3.5" stroke="white" stroke-opacity="0.65" />
<path class="arrow-path "
d="M23.5002 8.5V13.5H21.8335V11.4043L18.0119 15.2259L16.8334 14.0474L20.7141 10.1667H18.5002V8.5H23.5002Z"
fill="white" fill-opacity="0.65" />
<path class="arrow-path "
d="M8.5 23.5592V18.5592H10.1667V20.6549L13.9883 16.8333L15.1668 18.0118L11.2861 21.8925H13.5V23.5592H8.5Z"
fill="white" fill-opacity="0.65" />
</svg>
</div>
</div>
<!-- 核心数据指标 -->
<div class="container">
<div class="flex-box flex-left" >
<cdfCurve :idName="`${cdfCurveLeft}+${allIdName[0]}`" :propsDate="componentProps"></cdfCurve>
<div class="flex-box flex-left">
<cdfCurve :propsDate="componentProps"></cdfCurve>
</div>
<div class="flex-box flex-right">
<cdfCurve :idName="`${cdfCurveRight}+${allIdName[1]}`" :propsDate="componentProps" :isShowDetial="true"></cdfCurve>
</div>
<cdfCurve :propsDate="componentProps" :isShowDetial="true"></cdfCurve>
</div>
</div>
</div>
</template>
......@@ -41,23 +35,21 @@
<script setup>
import { onBeforeUnmount } from 'vue';
import cdfCurve from './cdfCurve.vue'
const cdfCurveRight = ref('chart-right');
const cdfCurveLeft = ref('chart-left');
const props = defineProps({
allIdName: {
type: Array,
default: () => ['allIdName1','allIdName2'],
},
title: {
type: String,
default: '位置默认'
}
});
onBeforeUnmount(() => {
onBeforeUnmount(() => {
//console.log("中,zhelixieza");
});
const componentProps = ref({});
</script>
<style scoped>
/* 基础样式 */
.header-container {
display: flex;
......@@ -91,14 +83,17 @@ const componentProps = ref({});
display: flex;
align-items: center;
}
.hot-title {
.hot-title {
display: flex;
justify-content: space-between;
}
.hot-title-text{
font-weight: 700;
font-size: 13px;
.hot-title-text {
font-weight: 700;
font-size: 13px;
}
.hot-title-enlarge {
display: inline-flex;
align-items: center;
......@@ -108,11 +103,12 @@ const componentProps = ref({});
transition: all 0.3s ease;
}
.hot-title-enlarge:hover .rect {
/* transform: scale(1.51); */
.hot-title-enlarge:hover .rect {
/* transform: scale(1.51); */
transform-origin: center;
}
.hot-title-enlarge:hover .arrow-path {
.hot-title-enlarge:hover .arrow-path {
transform: scale(1.2) skew(-10deg, -10deg);
transform-origin: center;
transition: transform 0.3s ease;
......@@ -132,12 +128,13 @@ const componentProps = ref({});
.flex-box {
flex: 1;
/* display: flex;
align-items: center;
justify-content: center; */
}
.flex-left{
.flex-left {
/* background-color: rgb(180, 180, 180); */
}
}
</style>
\ No newline at end of file
<!-- <template>
<热力图
<div style="height: 100%;background-color: blueviolet;" >
<div style="width: 100%;height: 100%;">
<div class="main">
我是hotMap
</div>
</div>
</div>
</template>
<script>
export default {
name: "hotMap_chart"
}
</script>
<script setup>
// import { onMounted, onBeforeUnmount, computed, ref, reactive, nextTick, markRaw, watch } from "vue";
// import * as echarts from 'echarts';
// import { getLanguage, setLanguage } from '@/utils/auth'
// import visualApi from "../../../api/move/visual.js";
// const ChineseEnglishGlossary = ref({
// "前向分布": "Forward links availability",
// "反向分布": "Backward links availability",
// "Forward links availability": "Forward links availability",
// "Backward links availability": "Backward links availability"
// })
// const props = defineProps({
// componentProps: {
// type: Object,
// default: () => { }
// },
// time: {
// type: String,
// default: () => ''
// },
// eight: {
// type: Boolean,
// default: () => true
// },
// dataType: {
// type: Number,
// default: () => 0
// },
// tableObj: {
// type: Object,
// default: () => { }
// }
// })
// const timeIntal = ref(true)
// var watchList = []
// const watch1 = watch(() => props.time, (newVal) => {
// if (selectStatus.value) {
// // //console.log(objs.value[newVal], newVal);
// selectId.value = newVal
// if (timeIntal.value) {
// timeIntal.value = false
// setTimeout(() => {
// if (objs.value[newVal] && objs.value[newVal] != 'undefined') {
// planFn(objs.value[newVal], xName.value, yName.value, props.componentProps)
// }
// timeIntal.value = true
// }, 500);
// }
// }
// if (props.componentProps.dataType && props.componentProps.dataType == 1) {
// propsFn(props.componentProps)
// }
// })
// watchList.push(watch1)
// const watch2 = watch(() => props.tableObj.update, (newVal) => {
// if (newVal) {
// if (myChart) {
// propsFn(props.componentProps)
// myChart.resize()
// }
// }
// })
// watchList.push(watch2)
// var pieces1 = [
// { min: 0, max: 0, label: '0', color: '#ffffff' },
// { min: 1, max: 0, label: '1', color: '#72EFDD' },
// { min: 2, max: 2, label: '2', color: '#56CFE1' },
// { min: 3, max: 3, label: '3', color: '#4EA8DE' },
// // { min: 3, max: 10000000000, label: '≥3', color: '#BE5C37' }
// { min: 3, max: 10000000000, label: '≥4', color: '#5E60CE' }
// ]
// var pieces2 = [
// { min: 0, max: 0.6, label: '0.0 - 0.6', color: '#FFFFFF' },
// { min: 0.6, max: 0.8, label: '0.6 - 0.8', color: '#72EFDD' },
// { min: 0.8, max: 0.95, label: '0.8 - 0.95', color: '#56CFE1' },
// { min: 0.95, max: 0.99, label: '0.95 - 0.99', color: '#4EA8DE' },
// { min: 0.99, max: 0.997, label: '0.99 - 0.997', color: '#5E60CE' },
// { min: 0.997, max: 1, label: '0.997 - 1.0', color: '#7400B8' },
// ]
// var arr = []
// const planFn = (data, x, y, obj) => {
// var mappedData = data.map(function (item) {
// return { name: item[x], value: item[y[0].name] };
// });
// // 获取字段'value'的最大值
// const maxValue = mappedData.reduce((max, item) => Math.max(max, item.value), -Infinity);
// // 获取字段'value'的最小值
// const minValue = mappedData.reduce((min, item) => Math.min(min, item.value), Infinity);
// //console.log(mappedData, '热力图数据');
// chartDom = document.getElementById(obj.id);
// myChart = echarts.init(chartDom);
// option = {
// title: {
// text: props.eight ? obj.columnLabel : '',
// left: 'center',
// textStyle: {
// color: '#ccc', // 设置标题颜色为灰色
// fonSize: 1
// }
// },
// tooltip: {
// trigger: 'item',
// showDelay: 0,
// transitionDuration: 0.2,
// // textStyle: {
// // color: '#ff0000' // 这里设置鼠标悬浮时的文字颜色为红色
// // }
// },
// visualMap: {
// type: 'piecewise',
// left: 'right',
// top: 'bottom',
// calculable: true,
// realtime: false,
// // splitNumber: 6,
// formatter: function (a, b) {
// //console.log(a, b, '问题');
// // 将数值转换为整数并格式化输出
// return `${Number(a).toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")} - ${Number(b).toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")}`
// },
// label: '测试',
// pieces: obj.pieces == 1 ? pieces1 : pieces2,
// min: minValue - 1,
// max: maxValue,
// textStyle: {
// color: '#ffffff' // 这里设置颜色选择器文字的颜色
// },
// // range: [0, 1000],
// inRange: {
// color: [
// '#ffffbf',
// '#fee090',
// '#DBBF92',
// '#D78851',
// '#BE5C37',
// ]
// },
// // outOfRange: {
// // color: ['#fdae61',
// // '#f46d43',
// // '#d73027',
// // '#a50026'
// // ]
// // },
// // color: ['#d94e5d', '#eac736', '#50a3ba'],
// // calculable: true,
// // piecewise: true,
// },
// // visualMap: {
// // min: 0,
// // max: 1000,
// // orient: 'vertical',
// // left: 'left',
// // top: 'bottom',
// // color: ['#d94e5d', '#eac736', '#50a3ba'],
// // calculable: true,
// // piecewise: true
// // },
// series: [
// {
// name: obj.columnLabel,
// type: 'map',
// roam: true,
// map: obj.id,
// data: mappedData,
// emphasis: {
// label: {
// show: false
// },
// itemStyle: {
// // 鼠标悬浮时的样式
// // borderColor: 'red', // 鼠标悬停时边框颜色
// // borderWidth: 2,
// color: '#000000' // 鼠标悬停时内部填充颜色
// }
// },
// zoom: 1.3,
// itemStyle: {
// // borderWidth: 2, // 边框宽度
// // borderColor: '#000000', // 边框颜色
// // color: '#ffffff',
// // normal: { // 正常状态下的样式
// // color: function (params) {
// // // 定义颜色的逻辑
// // return '#ffffff';
// // }
// // },
// // emphasis: { // 高亮状态下的样式
// // color: function (params) {
// // // 鼠标悬停时的颜色逻辑
// // return 'red'; // 或者一个新的颜色值
// // }
// // }
// }
// // 使用encode来映射数据源的属性到热力图的x轴、y轴和值
// // encode: {
// // x: a, // 将x轴映射到city属性
// // y: 'hour', // 将y轴映射到hour属性
// // value: b // 将值映射到value属性
// // },
// // data: value.visualData,
// // // 维度标签
// // dimensionNames: dimension,
// // // 指标名称
// // metric: metrics
// }
// ]
// };
// //console.log(option, '------------------------65--');
// window.addEventListener("resize", function () {
// if (myChart) {
// var time = setInterval(() => {
// myChart.resize()
// }, 200);
// setTimeout(() => {
// clearInterval(time)
// }, 2000)
// }
// })
// if (getLanguage() == "english") {
// Object.entries(props.componentProps).forEach(([key, value]) => {
// //console.log(key, value, 'key, --------------------value', ChineseEnglishGlossary.value[value]);
// if (key === "columnLabel") {
// //console.log(key, value, 'key, -------123-------------value');
// props.componentProps[key] = ChineseEnglishGlossary.value[value]
// }
// })
// // //console.log(props.componentProps, '柱状图---asdaaaa-----784654------');
// }
// //console.log(option, '------------------------65--');
// option && myChart.setOption(option);
// }
// function groupBy(array, key) {
// return array.reduce((acc, item) => {
// // 使用对象的键来存储分组
// const groupKey = item[key];
// // 如果累加器中还没有这个键的分组,就初始化一个空数组
// if (!acc[groupKey]) {
// acc[groupKey] = [];
// }
// // 将当前项添加到对应分组的数组中
// acc[groupKey].push(item);
// return acc;
// }, {});
// }
// const propsFn = (value) => {
// if (value.visualData && value.visualData.length > 0) {
// // //console.log(props.componentProps.columnName, '热力图加载了几次?');
// echarts.registerMap(props.componentProps.id, props.componentProps.relitu);
// // planFn(props.componentProps)
// yName.value = []
// value.metrics.forEach(it => {
// yName.value.push({
// name: it.name,
// label: it.label
// })
// })
// if (value.dimensions.length > 1) {
// xName.value = value.dimensions[1].name
// objs.value = groupBy(value.visualData, value.dimensions[0].name)
// if (!selectId.value) {
// selectId.value = value.visualData[0][value.dimensions[0].name]//赋值初始化id
// }
// selectStatus.value = true
// planFn(objs.value[selectId.value], xName.value, yName.value, value)
// } else {
// xName.value = value.dimensions[0].name
// planFn(value.visualData, xName.value, yName.value, value)
// }
// }
// }
// const xName = ref('')
// const yName = ref([])
// const selectId = ref('')
// const objs = ref({})
// const selectStatus = ref(false)
// var option;
// var myChart
// var chartDom
// onMounted(() => {
// propsFn(props.componentProps)
// })
// onBeforeUnmount(() => {
// watchList.forEach(stop => stop())
// watchList.length = 0 // 清空数组
// if (myChart) {
// myChart.clear()
// }
// })
// </script>
// <style scoped>
// .main {
// width: 100%;
// height: 100%;
// overflow: hidden;
// border: 1px solid #3A4C5B;
// }
// </style> >-->
<template>
<!--热力图-->
<div style="height: 100%; background-color: #000;">
......@@ -346,7 +15,9 @@ export default {
<script setup>
import * as echarts from 'echarts';
import { onMounted, onUnmounted, ref } from 'vue';
import { nextTick, onMounted, onUnmounted, ref } from 'vue';
import { getGridInfoList } from '@/api/Zodiac';
import { cs } from 'element-plus/es/locales.mjs';
// 生成唯一的图表ID
const chartId = ref('hexagonHeatMap_' + Math.random().toString(36).substr(2, 9));
......@@ -358,49 +29,49 @@ function generateHexagonData() {
const data = [];
const centerX = 0;
const centerY = 0;
// 六边形排列参数
const radius = 15;
const startRadius = 10;
const endRadius = 40;
const angleStep = Math.PI / 3; // 60度
// 生成类似巴西地图轮廓的六边形点
for (let r = startRadius; r < endRadius; r += radius * 0.85) {
// 根据半径调整角度数量,形成不规则形状
const angleCount = Math.round(8 - r/10) + Math.floor(Math.random() * 2);
const angleCount = Math.round(8 - r / 10) + Math.floor(Math.random() * 2);
for (let i = 0; i < angleCount; i++) {
// 基础角度,添加偏移使形状更接近巴西地图
const baseAngle = angleStep * i;
let angleOffset = 0;
// 根据位置添加偏移,形成不规则形状
if (r < startRadius + 15) angleOffset = 0.3;
else if (r > endRadius - 15) angleOffset = -0.2;
else if (i > angleCount/2) angleOffset = 0.1;
else if (i > angleCount / 2) angleOffset = 0.1;
const angle = baseAngle + angleOffset;
// 计算坐标
const x = centerX + r * Math.cos(angle);
const y = centerY + r * Math.sin(angle);
// 根据位置生成不同的值(模拟容量)
let value;
// 左侧区域值较低(蓝色)
if (x < -10) {
value = Math.random() * 30;
}
}
// 右侧区域值较高(红色)
else if (x > 20) {
value = 70 + Math.random() * 30;
}
}
// 中间区域值中等(黄、绿)
else {
value = 30 + Math.random() * 40;
}
data.push({
value: [x, y, value],
itemStyle: {
......@@ -409,14 +80,14 @@ function generateHexagonData() {
});
}
}
// 添加东南部突出部分(类似巴西东南角)
for (let i = 0; i < 5; i++) {
const angle = -Math.PI/4 + i * angleStep/2;
const angle = -Math.PI / 4 + i * angleStep / 2;
const r = endRadius - 5 + i * 3;
const x = centerX + r * Math.cos(angle);
const y = centerY + r * Math.sin(angle);
data.push({
value: [x, y, 80 + Math.random() * 20],
itemStyle: {
......@@ -424,7 +95,7 @@ function generateHexagonData() {
}
});
}
return data;
}
......@@ -433,15 +104,15 @@ function initChart() {
const chartDom = document.getElementById(chartId.value);
chartDom.style.width = '100%';
chartDom.style.height = '150px';
if (!chartDom) return;
myChart = echarts.init(chartDom);
// 生成热力图数据
const data = generateHexagonData();
const option = {
backgroundColor: '#000',
title: {
......@@ -511,27 +182,30 @@ function initChart() {
}
}
},
],
tooltip: {
trigger: 'axis',
// 确保tooltip显示在最上层
extraCssText: 'z-index: 999999 !important;',
backgroundColor: 'rgba(50,50,50,0.8)',
borderColor: '#333',
borderWidth: 1,
padding: 10,
textStyle: {
color: '#fff',
fontSize: 12
},
// 固定tooltip位置在顶部
position: function (pos, params, dom, rect, size) {
return ['5%', pos[1]];
}
}
],
tooltip: {
trigger: 'axis',
// 确保tooltip显示在最上层
extraCssText: 'z-index: 999999 !important;',
backgroundColor: 'rgba(50,50,50,0.8)',
borderColor: '#333',
borderWidth: 1,
padding: 10,
textStyle: {
color: '#fff',
fontSize: 12
},
// 固定tooltip位置在顶部
position: function (pos, params, dom, rect, size) {
return ['5%', pos[1]];
}
}
};
myChart.setOption(option);
resizeChart();
}
......@@ -543,12 +217,19 @@ function resizeChart() {
}
}
onMounted(() => {
onMounted(async () => {
const ret = await getGridInfoList()
console.log('rethotMAP', ret)
// 确保DOM渲染完成后初始化图表
setTimeout(() => {
nextTick(() => {
initChart();
window.addEventListener('resize', resizeChart);
}, 0);
});
// setTimeout(() => {
// initChart();
// window.addEventListener('resize', resizeChart);
// }, 0);
});
onUnmounted(() => {
......
......@@ -7,28 +7,22 @@
<div class="divider-line"></div>
</div>
<div class="hot-title">
<div class="hot-title-text">巴西-最小值(热力图)</div>
<div class="hot-title-enlarge">
<svg class="rect" width="20" height="20" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="0.5" y="0.5" width="31" height="31" rx="3.5" stroke="white" stroke-opacity="0.65"/>
<path
class="arrow-path "
d="M23.5002 8.5V13.5H21.8335V11.4043L18.0119 15.2259L16.8334 14.0474L20.7141 10.1667H18.5002V8.5H23.5002Z"
fill="white"
fill-opacity="0.65"
/>
<path
class="arrow-path "
d="M8.5 23.5592V18.5592H10.1667V20.6549L13.9883 16.8333L15.1668 18.0118L11.2861 21.8925H13.5V23.5592H8.5Z"
fill="white"
fill-opacity="0.65"
/>
</svg>
</div>
<div class="hot-title-text">{{ props.title }}</div>
<div class="hot-title-enlarge">
<svg class="rect" width="20" height="20" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="0.5" y="0.5" width="31" height="31" rx="3.5" stroke="white" stroke-opacity="0.65" />
<path class="arrow-path "
d="M23.5002 8.5V13.5H21.8335V11.4043L18.0119 15.2259L16.8334 14.0474L20.7141 10.1667H18.5002V8.5H23.5002Z"
fill="white" fill-opacity="0.65" />
<path class="arrow-path "
d="M8.5 23.5592V18.5592H10.1667V20.6549L13.9883 16.8333L15.1668 18.0118L11.2861 21.8925H13.5V23.5592H8.5Z"
fill="white" fill-opacity="0.65" />
</svg>
</div>
</div>
<!-- 核心数据指标 -->
<div class="container">
<div class="flex-box flex-left" >
<div class="flex-box flex-left">
<hotMap :propsDate="componentProps"></hotMap>
</div>
<div class="flex-box flex-right">
......@@ -44,11 +38,17 @@ import { ref } from 'vue';
import hotMap from './hotMap.vue'
import testMap from './testMap.vue'
const props = defineProps({
title: {
type: String,
default: '默认未知'
}
})
const componentProps = ref({})
</script>
<style scoped>
/* 基础样式 */
.header-container {
display: flex;
......@@ -82,14 +82,17 @@ const componentProps = ref({})
display: flex;
align-items: center;
}
.hot-title {
.hot-title {
display: flex;
justify-content: space-between;
}
.hot-title-text{
font-weight: 700;
font-size: 13px;
.hot-title-text {
font-weight: 700;
font-size: 13px;
}
.hot-title-enlarge {
display: inline-flex;
align-items: center;
......@@ -99,11 +102,12 @@ const componentProps = ref({})
transition: all 0.3s ease;
}
.hot-title-enlarge:hover .rect {
/* transform: scaleX(1.1); */
.hot-title-enlarge:hover .rect {
/* transform: scaleX(1.1); */
transform-origin: center;
}
.hot-title-enlarge:hover .arrow-path {
.hot-title-enlarge:hover .arrow-path {
transform: scale(1.2) skew(-10deg, -10deg);
transform-origin: center;
transition: transform 0.3s ease;
......@@ -126,7 +130,8 @@ const componentProps = ref({})
align-items: center;
justify-content: center; */
}
.flex-left{
.flex-left {
background-color: rgb(180, 180, 180);
}
}
</style>
\ No newline at end of file
<template>
<!--热力图-->
<div style="height: 100%; background-color: blueviolet;">
<div style="width: 100%; height: 100%;">
<div id="main" class="main">
我是hotMap
</div>
<!--热力图-->
<div style="height: 100%; background-color: #000;">
<div style="width: 100%; height: 100%;">
<div :id="chartId" class="main"></div>
</div>
</div>
</div>
</template>
<script>
export default {
name: "hotMap_chart"
}
</script>
<script setup>
import * as echarts from 'echarts';
import { onMounted } from 'vue';
import { nextTick, onMounted, onUnmounted, ref } from 'vue';
import { getGridInfoList } from '@/api/Zodiac';
import { cs } from 'element-plus/es/locales.mjs';
onMounted(() => {
initChart();
});
// 生成唯一的图表ID
const chartId = ref('hexagonHeatMap_' + Math.random().toString(36).substr(2, 9));
let myChart = null;
function initChart() {
var ROOT_PATH = 'https://echarts.apache.org/examples';
var CDN_PATH = 'https://echarts.apache.org/zh/js/vendors/';
// 生成六边形热力图数据
function generateHexagonData() {
// 模拟巴西地图形状的六边形网格数据
const data = [];
const centerX = 0;
const centerY = 0;
var chartDom = document.getElementById('main');
if (!chartDom) return;
// 六边形排列参数
const radius = 15;
const startRadius = 10;
const endRadius = 40;
const angleStep = Math.PI / 3; // 60度
var myChart = echarts.init(chartDom);
// 生成类似巴西地图轮廓的六边形点
for (let r = startRadius; r < endRadius; r += radius * 0.85) {
// 根据半径调整角度数量,形成不规则形状
const angleCount = Math.round(8 - r / 10) + Math.floor(Math.random() * 2);
myChart.showLoading();
for (let i = 0; i < angleCount; i++) {
// 基础角度,添加偏移使形状更接近巴西地图
const baseAngle = angleStep * i;
let angleOffset = 0;
Promise.all([
fetch(ROOT_PATH + '/data/asset/geo/USA.json').then(res => res.json()),
loadScript(CDN_PATH + 'd3-array@2.8.0/dist/d3-array.js'),
loadScript(CDN_PATH + 'd3-geo@2.0.1/dist/d3-geo.js')
]).then(([usaJson]) => {
const projection = d3.geoAlbersUsa();
myChart.hideLoading();
echarts.registerMap('USA', usaJson);
//console.log("获得地图数据",usaJson);
const option = {
title: {
text: 'USA Population Estimates (2012)',
subtext: 'Data from www.census.gov',
left: 'right'
},
tooltip: {
trigger: 'item',
showDelay: 0,
transitionDuration: 0.2
},
visualMap: {
left: 'right',
min: 500000,
max: 38000000,
inRange: {
color: [
'#313695', '#4575b4', '#74add1', '#abd9e9', '#e0f3f8',
'#ffffbf', '#fee090', '#fdae61', '#f46d43', '#d73027', '#a50026'
]
},
text: ['High', 'Low'],
calculable: true
},
toolbox: {
show: true,
left: 'left',
top: 'top',
feature: {
dataView: { readOnly: false },
restore: {},
saveAsImage: {}
}
},
series: [
{
name: 'USA PopEstimates',
type: 'map',
map: 'USA',
projection: {
project: function (point) {
return projection(point);
},
unproject: function (point) {
return projection.invert(point);
// 根据位置添加偏移,形成不规则形状
if (r < startRadius + 15) angleOffset = 0.3;
else if (r > endRadius - 15) angleOffset = -0.2;
else if (i > angleCount / 2) angleOffset = 0.1;
const angle = baseAngle + angleOffset;
// 计算坐标
const x = centerX + r * Math.cos(angle);
const y = centerY + r * Math.sin(angle);
// 根据位置生成不同的值(模拟容量)
let value;
// 左侧区域值较低(蓝色)
if (x < -10) {
value = Math.random() * 30;
}
},
emphasis: {
label: {
show: true
// 右侧区域值较高(红色)
else if (x > 20) {
value = 70 + Math.random() * 30;
}
},
data: [
{ name: 'Alabama', value: 4822023 },
{ name: 'Alaska', value: 731449 },
{ name: 'Arizona', value: 6553255 },
// ...其他数据保持不变...
]
// 中间区域值中等(黄、绿)
else {
value = 30 + Math.random() * 40;
}
data.push({
value: [x, y, value],
itemStyle: {
opacity: 0.9
}
});
}
]
};
}
// 添加东南部突出部分(类似巴西东南角)
for (let i = 0; i < 5; i++) {
const angle = -Math.PI / 4 + i * angleStep / 2;
const r = endRadius - 5 + i * 3;
const x = centerX + r * Math.cos(angle);
const y = centerY + r * Math.sin(angle);
data.push({
value: [x, y, 80 + Math.random() * 20],
itemStyle: {
opacity: 0.9
}
});
}
return data;
}
// 初始化图表
function initChart() {
const chartDom = document.getElementById(chartId.value);
chartDom.style.width = '100%';
chartDom.style.height = '150px';
if (!chartDom) return;
myChart = echarts.init(chartDom);
// 生成热力图数据
const data = generateHexagonData();
// const option = {
// backgroundColor: '#000',
// title: {
// text: '链路层容量',
// left: 'center',
// // top: 10,
// left: 10,
// textStyle: {
// color: '#fff',
// fontSize: 10,
// fontWeight: 'normal'
// }
// },
// tooltip: {
// // formatter: function(params) {
// // return `容量: ${params.data[2].toFixed(1)}`;
// // },
// backgroundColor: 'rgba(0, 0, 0, 0.7)',
// borderColor: '#444',
// textStyle: {
// color: '#fff'
// }
// },
// visualMap: {
// min: 0,
// max: 100,
// calculable: true,
// orient: 'horizontal',
// left: 'center',
// // bottom: 30,
// inRange: {
// color: ['#0050b3', '#1890ff', '#7cb305', '#ffd500', '#ff4d4f']
// },
// textStyle: {
// color: '#fff'
// },
// // itemWidth: 40,
// // itemHeight: 10,
// borderWidth: 0
// },
// xAxis: {
// type: 'value',
// show: false,
// min: -50,
// max: 50
// },
// yAxis: {
// type: 'value',
// show: false,
// min: -50,
// max: 50
// },
// series: [{
// name: '链路容量',
// type: 'scatter',
// coordinateSystem: 'cartesian2d',
// symbol: 'path://M0,-10 L10,5 L10,15 L0,20 L-10,15 L-10,5 Z', // 六边形路径
// symbolSize: [15, 15], // 六边形大小
// data: data,
// label: {
// show: false
// },
// emphasis: {
// itemStyle: {
// shadowBlur: 10,
// shadowColor: 'rgba(0, 0, 0, 0.5)'
// }
// }
// },
// ],
// tooltip: {
// trigger: 'axis',
// // 确保tooltip显示在最上层
// extraCssText: 'z-index: 999999 !important;',
// backgroundColor: 'rgba(50,50,50,0.8)',
// borderColor: '#333',
// borderWidth: 1,
// padding: 10,
// textStyle: {
// color: '#fff',
// fontSize: 12
// },
// // 固定tooltip位置在顶部
// position: function (pos, params, dom, rect, size) {
// return ['5%', pos[1]];
// }
// }
// };
myChart.setOption(option);
resizeChart();
}
window.addEventListener('resize', function () {
myChart.resize();
});
});
function loadScript(src) {
return new Promise((resolve, reject) => {
const script = document.createElement('script');
script.src = src;
script.onload = resolve;
script.onerror = reject;
document.head.appendChild(script);
});
}
// 响应式调整图表大小
function resizeChart() {
if (myChart) {
myChart.resize();
}
}
onMounted(async () => {
const ret = await getGridInfoList()
console.log('ret', ret)
// 确保DOM渲染完成后初始化图表
nextTick(() => {
initChart();
window.addEventListener('resize', resizeChart);
});
// setTimeout(() => {
// initChart();
// window.addEventListener('resize', resizeChart);
// }, 0);
});
onUnmounted(() => {
if (myChart) {
window.removeEventListener('resize', resizeChart);
myChart.dispose();
myChart = null;
}
});
</script>
<style scoped>
.main {
width: 100%;
height: 100%;
overflow: hidden;
border: 1px solid #3A4C5B;
width: 100%;
height: 100%;
/* min-height: 400px; */
/* overflow: hidden; */
}
</style>
\ No newline at end of file
......@@ -3,32 +3,26 @@
<!-- 标题栏 -->
<div class="section-divider">
<div class="divider-line"></div>
<h3 class="section-title">可用性</h3>
<h3 class="section-title">网络时延/丢包</h3>
<div class="divider-line"></div>
</div>
<div class="hot-title">
<div class="hot-title-text">巴西-最小值(CDF曲线图)</div>
<div class="hot-title-enlarge">
<svg class="rect" width="20" height="20" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="0.5" y="0.5" width="31" height="31" rx="3.5" stroke="white" stroke-opacity="0.65"/>
<path
class="arrow-path "
d="M23.5002 8.5V13.5H21.8335V11.4043L18.0119 15.2259L16.8334 14.0474L20.7141 10.1667H18.5002V8.5H23.5002Z"
fill="white"
fill-opacity="0.65"
/>
<path
class="arrow-path "
d="M8.5 23.5592V18.5592H10.1667V20.6549L13.9883 16.8333L15.1668 18.0118L11.2861 21.8925H13.5V23.5592H8.5Z"
fill="white"
fill-opacity="0.65"
/>
</svg>
</div>
<div class="hot-title-text">巴西-最小值(PDF曲线图)</div>
<div class="hot-title-enlarge">
<svg class="rect" width="20" height="20" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="0.5" y="0.5" width="31" height="31" rx="3.5" stroke="white" stroke-opacity="0.65" />
<path class="arrow-path "
d="M23.5002 8.5V13.5H21.8335V11.4043L18.0119 15.2259L16.8334 14.0474L20.7141 10.1667H18.5002V8.5H23.5002Z"
fill="white" fill-opacity="0.65" />
<path class="arrow-path "
d="M8.5 23.5592V18.5592H10.1667V20.6549L13.9883 16.8333L15.1668 18.0118L11.2861 21.8925H13.5V23.5592H8.5Z"
fill="white" fill-opacity="0.65" />
</svg>
</div>
</div>
<!-- 核心数据指标 -->
<div class="container">
<div class="flex-box flex-left" >
<div class="flex-box flex-left">
<instantCurve :idName="`instant-left-${props.allIdName}`" :propsDate="componentProps"></instantCurve>
</div>
<div class="flex-box flex-right">
......@@ -40,20 +34,25 @@
<script setup>
import instantCurve from './instantCurve.vue'
const instantCurveRight = ref('instantCurve_chart-right');
const instantCurveLeft = ref('instantCurve_chart-left');
const instantCurveRight = ref('instantCurve_chart-right');
const instantCurveLeft = ref('instantCurve_chart-left');
const props = defineProps({
allIdName: {
type: String,
default: 'allIdName',
},
title: {
type: String,
default: '巴西-最小值(PDF曲线图)'
}
});
console.log('props.allIdName', props.allIdName)
const componentProps = ref({})
</script>
<style scoped>
/* 基础样式 */
.header-container {
display: flex;
......@@ -87,14 +86,17 @@ const componentProps = ref({})
display: flex;
align-items: center;
}
.hot-title {
.hot-title {
display: flex;
justify-content: space-between;
}
.hot-title-text{
font-weight: 700;
font-size: 13px;
.hot-title-text {
font-weight: 700;
font-size: 13px;
}
.hot-title-enlarge {
display: inline-flex;
align-items: center;
......@@ -105,8 +107,8 @@ const componentProps = ref({})
}
.hot-title-enlarge:hover .arrow-path {
transform: scale(1.2) skew(-10deg, -10deg);
.hot-title-enlarge:hover .arrow-path {
transform: scale(1.2) skew(-10deg, -10deg);
transform-origin: center;
transition: transform 0.3s ease;
}
......@@ -128,7 +130,8 @@ const componentProps = ref({})
align-items: center;
justify-content: center; */
}
.flex-left{
.flex-left {
/* background-color: rgb(180, 180, 180); */
}
}
</style>
\ No newline at end of file
......@@ -21,7 +21,7 @@
}" />
<div class="right_block">
<!--限制 :disabled="!is_flag_confirmAndPauseAndSpeed" -->
<el-button class="right_block_confirm_btn" size="mini"
<el-button class="right_block_confirm_btn" size="small"
@click="handleDateRangeChange(pick_select_date)">确认</el-button>
<div class="right_block_center">
<!-- <el-button circle class="right_block_conter_btn" @click="playBackward">
......
......@@ -2,25 +2,25 @@
<div class="templateCase_right_content">
<div class="templateCase_right_content_header">
<div class="templateCase_right_content_header_item">
<span>应用</span>
<img src="@/assets/images/headers/case_header.png" alt="" style="width: 20px; height: 20px;">
<!-- <span>模板名称</span> -->
<input ref="inputRef" class="templateName" v-model="templateName" :disabled="!isEdit"></input>
<img src="@/assets/images/headers/case_header.png" alt="" title="编辑"
style="width: 20px; height: 20px;cursor: pointer;" @click="handleEdit">
</div>
</div>
<div class="templateCase_right_content_body">
<div class="horizontal-sections">
<div class="section-item" v-for="(item, index) in titles" :key="index">
<div class="section-item" v-for="(el, index) in settingConfig" :key="index">
<div class="section-item_title">
<span>{{ item.title }}</span>
<span>{{ el.title }}</span>
</div>
<div class="section-item_content_up">
<!-- <div v-for="(element,index) in [template1Selector,template2Selector]" :key="index">
</div> -->
<div class="selector-item" v-for="(item, key) in item.templateSelector" :key="key">
<label class="selector-label">{{ item.label}}</label>
<div class="selector-item" v-for="(item, key) in el.templateSelector" :key="key">
<label class="selector-label">{{ item.label }}</label>
<div class="custom-select">
<!-- 粒度 -->
<el-select v-model="item.default" placeholder="" style="width: 100%;">
<el-select v-model="item.default" placeholder="" :disabled="!isEdit" style="width: 100%;"
@change="(val) => selectOnChange(val, index, key)">
<el-option v-for="itemele in item.options" :key="itemele.value" :label="itemele.label"
:value="itemele.value" />
</el-select>
......@@ -28,9 +28,10 @@
</div>
</div>
<div class="section-item_content_down">
<hotMapComponent v-if="index === 0"></hotMapComponent>
<cdf v-if="index === 1" :allIdName="allIdName[0]"></cdf>
<instant v-if="index === 2" :allIdName="allIdName[1]"></instant>
<KeepAlive>
<component :is="el.defaultComponent" :title="titleFn(el)"></component>
</KeepAlive>
</div>
</div>
......@@ -43,25 +44,42 @@
import hotMapComponent from '@/pages/all/components/hotMapComponents/index.vue';
import cdf from '@/pages/all/components/cdf/index.vue';
import instant from '@/pages/all/components/instant/index.vue';
import { markRaw, nextTick, watch } from 'vue'
const isEdit = ref(false)
const inputRef = ref()
const templateName = ref('默认模板名称')
const props = defineProps({
settingConfig: {
type: Object,
default: () => { }
}
})
const allIdName = ref([['allIdName11', 'allIdName12'], ['allIdName21', 'allIdName22']]);
// 组件逻辑可以在这里添加
const titles = [
const settingConfig = ref([
{
title: '容量/流量',
defaultComponent: markRaw(hotMapComponent),
templateSelector: [
{
label: '曲线类型',
default: '热力图',
type: 'capacityCurveType',
options: [
{
value: '瞬时值曲线',
label: '瞬时值曲线',
},
{
value: 'PDF曲线',
label: 'PDF曲线',
},
{
value: '热力图',
......@@ -72,6 +90,7 @@ const titles = [
{
label: '地理标准',
default: '最小值',
type: 'capacityGeoStandard',
options: [
{
value: '最小值',
......@@ -90,6 +109,7 @@ const titles = [
{
label: '时间标准',
default: '最小值',
type: 'capacityTimeStandard',
options: [
{
value: '最小值',
......@@ -109,10 +129,12 @@ const titles = [
},
{
title: '可用性',
templateSelector: [
defaultComponent: markRaw(cdf),
templateSelector: [
{
label: '曲线类型',
default: '热力图',
default: 'PDF曲线',
type: 'availabilityCurveType',
options: [
{
value: 'PDF曲线',
......@@ -127,6 +149,7 @@ const titles = [
{
label: '地理标准',
default: '最小值',
type: 'availabilityGeoStandard',
options: [
{
value: '最小值',
......@@ -145,6 +168,7 @@ const titles = [
{
label: '时间标准',
default: '最小值',
type: 'availabilityTimeStandard',
options: [
{
value: '最小值',
......@@ -164,10 +188,12 @@ const titles = [
},
{
title: '网络时延/丢包',
templateSelector: [
defaultComponent: markRaw(instant),
templateSelector: [
{
label: '时延曲线类型',
label: '曲线类型',
default: '瞬时值曲线',
type: 'latencyCurveType',
options: [
{
value: '瞬时值曲线',
......@@ -186,6 +212,7 @@ const titles = [
{
label: '地理标准',
default: '最小值',
type: 'latencyGeoStandard',
options: [
{
value: '最小值',
......@@ -204,6 +231,7 @@ const titles = [
{
label: '时间标准',
default: '最小值',
type: 'latencyTimeStandard',
options: [
{
value: '最小值',
......@@ -219,66 +247,140 @@ const titles = [
}
]
},
{
label: '丢包曲线类型',
default: '瞬时值曲线',
options: [
{
value: '最小值',
label: '最小值',
},
{
value: '最大值',
label: '最大值',
},
{
value: '平均值',
label: '平均值',
}
]
},
{
label: '地理标准',
default: '最小值',
options: [
{
value: '最小值',
label: '最小值',
},
{
value: '最大值',
label: '最大值',
},
{
value: '平均值',
label: '平均值',
}
]
},
{
label: '时间标准',
default: '最小值',
options: [
{
value: '最小值',
label: '最小值',
},
{
value: '最大值',
label: '最大值',
},
{
value: '平均值',
label: '平均值',
}
]
},
// {
// label: '丢包曲线类型',
// default: '瞬时值曲线',
// options: [
// {
// value: '最小值',
// label: '最小值',
// },
// {
// value: '最大值',
// label: '最大值',
// },
// {
// value: '平均值',
// label: '平均值',
// }
// ]
// },
// {
// label: '地理标准',
// default: '最小值',
// options: [
// {
// value: '最小值',
// label: '最小值',
// },
// {
// value: '最大值',
// label: '最大值',
// },
// {
// value: '平均值',
// label: '平均值',
// }
// ]
// },
// {
// label: '时间标准',
// default: '最小值',
// options: [
// {
// value: '最小值',
// label: '最小值',
// },
// {
// value: '最大值',
// label: '最大值',
// },
// {
// value: '平均值',
// label: '平均值',
// }
// ]
// },
]
}
];
]);
watch(() => props.settingConfig, (newVal) => {
// console.log('newVal', newVal)
if (!newVal) return
templateName.value = newVal.templateName
for (let attr in newVal) {
for (let item of settingConfig.value) {
const obj = item.templateSelector.find(el => el.type === attr)
if (!obj) continue
obj.default = newVal[attr]
switch (newVal[attr]) {
case '瞬时值曲线':
item.defaultComponent = markRaw(cdf);
break
case '热力图':
item.defaultComponent = markRaw(hotMapComponent)
break
case 'PDF曲线':
item.defaultComponent = markRaw(cdf)
break
default:
break
}
}
}
isEdit.value = false
}, { immediate: true })
const selectOnChange = (val, index, key) => {
console.log('row', val, index, key)
settingConfig.value[index].templateSelector.default = val
switch (val) {
case '瞬时值曲线':
settingConfig.value[index].defaultComponent = markRaw(cdf);
break
case '热力图':
settingConfig.value[index].defaultComponent = markRaw(hotMapComponent)
console.log('热力图', settingConfig.value[index].defaultComponent)
break
case 'PDF曲线':
settingConfig.value[index].defaultComponent = markRaw(cdf)
break
default:
break
}
}
const handleEdit = async () => {
isEdit.value = !isEdit.value
if (isEdit.value) {
await nextTick()
inputRef.value.focus()
}
}
const titleFn = (el) => {
const templateSelector = el.templateSelector
return `${templateSelector[0].default} - ${templateSelector[1].default} - ${templateSelector[2].default}`
}
defineExpose({
configData: () => {
const obj = {
templateName: templateName.value
}
settingConfig.value.forEach(item => {
item.templateSelector.forEach(el => {
obj[el.type] = el.default
})
})
return obj
}
})
const template2Selector = []
</script>
<style scoped>
......@@ -337,6 +439,19 @@ const template2Selector = []
justify-content: space-between;
}
.templateCase_right_content_header_item .templateName {
width: 160px;
background: transparent;
outline: none;
border: none;
color: white;
border-bottom: 1px solid transparent;
padding-bottom: 6px;
padding-right: 12px;
}
......
......@@ -69,20 +69,30 @@
<div class="selector-item">
<label class="selector-label">{{ templateSelector.label }}</label>
<label class="selector-label">模板列表</label>
<div class="custom-select">
<!-- 粒度 -->
<el-select v-model="templateSelector.value" placeholder="" style="width: 100%;">
<el-option v-for="item in templateSelector.options" :key="item.value" :label="item.label"
<el-select v-model="templateSelector" placeholder="请选择模板列表" @change="templateOnChange"
style="width: 100%;">
<el-option v-for="item in templateSelectorOptions" :key="item.value" :label="item.label"
:value="item.value" />
</el-select>
</div>
</div>
</div>
<div class="content-item">
<hotMapComponent></hotMapComponent>
<cdf :allIdName="allIdName[0]"></cdf>
<instant :allIdName="allIdName[1]"></instant>
<KeepAlive>
<component :is="currentComponent1"></component>
</KeepAlive>
<KeepAlive>
<component :is="currentComponent2"></component>
</KeepAlive>
<KeepAlive>
<component :is="currentComponent3"></component>
</KeepAlive>
</div>
<div>
<el-button type="primary" class="apply-btn apply-btn-right">应用</el-button>
......@@ -97,28 +107,29 @@
<div class="templateCase_left_header">
<div class="templateCase_left_header_item_title">模板列表</div>
<div class="templateCase_left_header_item_tool">
<el-icon @click="templateCasedeleteHandFn">
<el-icon @click="handleDelete" title="删除" style="cursor: pointer">
<Delete />
</el-icon>
<el-icon @click="templateCaseAddHandFn">
<el-icon @click="handleAdd" title="添加" style="cursor: pointer">
<Plus />
</el-icon>
</div>
</div>
<div class="templateCase_left_content">
<div class="templateCase_left_content_item" v-for="(item, index) in templateCaseList" :key="index"
@click="templateCaseClickFn(index)" :class="{ 'CaseClickActive': activeTemplateIndex === index }">
{{ item }}
<div class="templateCase_left_content_item" v-for="(item, index) in templateCaseList"
:key="item.templateId" @click="templateCaseClickFn(item, index)"
:class="{ 'CaseClickActive': activeTemplateIndex === index }">
{{ item.templateName }}
</div>
</div>
</div>
<div class="templateCase_right templateCase">
<screenConfigCase></screenConfigCase>
<screenConfigCase ref="screenConfigCaseRef" :settingConfig="currentSettingConfig"></screenConfigCase>
</div>
</div>
<el-button type="primary" class="apply-btn">应用</el-button>
<el-button type="primary" class="apply-btn" @click="handleApply">应用</el-button>
</div>
</div>
</main>
......@@ -127,23 +138,32 @@
<script setup>
import { ref, computed, onMounted, watch } from 'vue';
import { ref, computed, onMounted, watch, markRaw } from 'vue';
import hotMapComponent from '@/pages/all/components/hotMapComponents/index.vue';
import cdf from '@/pages/all/components/cdf/index.vue';
import instant from '@/pages/all/components/instant/index.vue';
import screenConfigCase from './screenConfigCase.vue';
import { getCapacityAndTraffic } from '@/api/Zodiac';
import { getCapacityAndTraffic, getAvailability, getTemplateList, templateAdd, templateDel, templateDetail, templateuUpdate } from '@/api/Zodiac';
import useAppStore from '@/store/module/app'
import { ElMessage, ElMessageBox } from 'element-plus'
let currentComponent1 = markRaw(hotMapComponent) //容量流量 热力图
let currentComponent2 = markRaw(cdf) //可用性 PDF曲线
let currentComponent3 = markRaw(instant) //let 瞬时值曲线组件 //前面那个人写的好乱更屎一样,我服了,在这里偷偷吐槽一下嘿嘿
const screenConfigCaseRef = ref()
const currentSettingConfig = ref()
const templateCaseList = ref([])
const appStore = useAppStore();
const emits = defineEmits(['handleScreenConfigeClose']);
const allIdName = ref([['allIdName11', 'allIdName12'], ['allIdName21', 'allIdName22']]);
// 标签页状态管理
const activeTab = ref('equipmentCount'); // 默认选中"地面站设备数量"
const keliSelector = ref(
{
label: '地面粒度',
......@@ -179,43 +199,78 @@ const childrenSelector = ref(
const isGlobal = computed(() => keliSelector.value.value === 'global');
const templateSelector = ref(
{
label: '模板选择',
value: '模板1',
options: [
{
value: '模板1',
label: '模板1',
},
{
value: '模板2',
label: '模板2',
},
{
value: '模板3',
label: '模板3',
},
{
value: '模板4',
}
]
}
)
const templateSelector = ref()
const templateSelectorOptions = ref([])
watch(() => templateCaseList.value, () => {
console.log('templateCaseList', templateCaseList.value)
templateSelectorOptions.value = templateCaseList.value.map(item => ({
label: item.templateName,
value: item.templateId
}))
})
const templateCaseList = ref(['模板1', '模板2'])
// 下拉选择器状态
const selectedValues = ref([]);
const dropdownVisible = ref([false, false, false]);
const templateCasedeleteHandFn = () => {
//console.log('templateCasedeleteHandFn');
const handleDelete = () => {
if (!currentSettingConfig.value.templateId) {
ElMessage({
type: 'error',
message: '请先选择一个模板',
})
return
}
ElMessageBox.confirm(
`您确定删除 [${currentSettingConfig.value.templateName}] ?`,
'Warning',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}
)
.then(async () => {
const ret = await templateDel({
templateId: currentSettingConfig.value.templateId // 模板id
})
if (ret.code == 200) {
await handleList() // 刷新列表获取最新数据
currentSettingConfig.value = templateCaseList.value[0] || {} // 删除后默认选中第一个模板
activeTemplateIndex.value = 0;
}
ElMessage({
type: 'success',
message: '删除成功',
})
})
};
const templateCaseAddHandFn = () => {
//console.log('templateCaseAddHandFn');
const handleAdd = () => {
// console.log('新增')
// const ret = await templateAdd({
// name: '模板1',
// settingConfig: {}
// })
const templateId = Date.now();
templateCaseList.value.push({
//新增模板
templateName: '新增模板', //模板名
templateId, // 模板id,后端生成的应该是uuid,这里先用时间戳模拟
})
currentSettingConfig.value = {
templateName: '新增模板',
templateAdd: true,
templateId,
}
activeTemplateIndex.value = templateCaseList.value.length - 1; // 设置当前选中为新增的模板
};
// 初始化选中值
onMounted(async () => {
await getCapacityAndTrafficData()
await handleList();
// await getCapacityAndTrafficData()
// await getAvailabilityData();
});
// 切换下拉菜单显示
......@@ -238,10 +293,23 @@ const getCapacityAndTrafficData = async () => {
console.log(res)
}
const getAvailabilityData = async () => {
const res = await getAvailability({
granularity: keliSelector.value.value,
startTime: '2025-11-03 00:00:00',
endTime: '2025-11-04 00:00:00',
regionName: '',
signalSiteCode: '',
gridCodes: ''
})
console.log('可用性', res)
}
const activeTemplateIndex = ref(0);
const templateCaseClickFn = (index) => {
const templateCaseClickFn = (item, index) => {
//console.log('templateCaseClickFn', index);
activeTemplateIndex.value = index;
currentSettingConfig.value = item;
};
// 点击外部关闭下拉菜单
document.addEventListener('click', (e) => {
......@@ -271,8 +339,69 @@ const keliOnChange = () => {
break;
}
}
watch(() => keliSelector.value.value, () => keliOnChange)
const handleApply = async () => {
let ret = null
if (currentSettingConfig.value.templateAdd) { // 如果是新增的模板,先调用新增接口,再调用修改接口
ret = await templateAdd({
// templateName: currentSettingConfig.value.templateName, // 模板名
...screenConfigCaseRef.value.configData() // 获取当前配置
})
} else {
ret = await templateuUpdate({
templateId: currentSettingConfig.value.templateId, // 模板id
...screenConfigCaseRef.value.configData() // 获取当前配置
})
}
if (ret.code == 200) {
await handleList() // 刷新列表获取最新数据
ElMessage({
message: '模板设置成功',
type: 'success',
})
}
}
const handleList = async () => {
const ret = await getTemplateList()
if (ret.code == 200) {
templateCaseList.value = ret.data;
currentSettingConfig.value = ret.data[activeTemplateIndex.value]
}
}
const templateOnChange = (val) => {
console.log('val', val)
const selectedTemplate = templateCaseList.value.find(item => item.templateId === val)
console.log('selectedTemplate', selectedTemplate)
// availabilityCurveType
// capacityCurveType
// latencyCurveType
const obj = {
'PDF曲线': markRaw(cdf),
'热力图': markRaw(hotMapComponent),
'瞬时值曲线': markRaw(instant)
}
currentComponent1 = obj[selectedTemplate.capacityCurveType]
currentComponent2 = obj[selectedTemplate.availabilityCurveType]
currentComponent3 = obj[selectedTemplate.latencyCurveType]
// if()
}
watch(() => keliSelector.value.value, keliOnChange)
</script>
......
......@@ -57,7 +57,7 @@
</div>
</div>
<el-dialog v-model="screenConfigeDialogVisible" :destroy-on-close="true" :modal="false" width="100"
<el-dialog v-model="screenConfigeDialogVisible" :destroy-on-close="false" :modal="false" width="100"
:show-close="false" :before-close="handleScreenConfigeClose"
style=" background-color: rgba(0, 0, 0,0);padding: 0;margin: 0;">
<screenConfige @handleScreenConfigeClose="handleScreenConfigeClose"></screenConfige>
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论