feat:save

master
guoyixuan 2 months ago
parent 9b1e2c8995
commit b0972b6421

@ -72,13 +72,11 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
} }
.top-selector { .top-timer {
border-bottom: 1px solid #e8e8e8; padding-bottom: 18px;
padding: 0 24px 12px 0;
text-align: right;
} }
.top-selector .custom-date-picker { .top-timer .custom-date-picker {
width: 360px !important; width: 280px !important;
} }
.trend-graph-container { .trend-graph-container {
flex: 1; flex: 1;
@ -86,18 +84,14 @@
overflow-y: auto; overflow-y: auto;
overflow-x: hidden; overflow-x: hidden;
} }
.trend-graph-container .trend-item { .graph-container {
border-bottom: 1px dashed #e8e8e8;
height: 240px; height: 240px;
border-bottom: 1px dashed #e8e8e8;
width: 100%;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
} }
.trend-graph-container .trend-item .title { .graph-container .line-graph {
padding: 12px 0 0 12px;
font-size: 14px;
font-weight: 700;
}
.trend-graph-container .trend-item .line-graph {
flex: 1; flex: 1;
min-height: 0; min-height: 0;
} }
@ -199,3 +193,19 @@
.prps-and-prpd-container .switcher .active-span { .prps-and-prpd-container .switcher .active-span {
color: #1890ff; color: #1890ff;
} }
.init-warning-tips {
font-size: 12px;
color: red;
}
.event-count-box {
height: 100%;
width: 100%;
display: flex;
flex-direction: column;
}
.top-box {
display: flex;
align-items: center;
justify-content: space-between;
padding-right: 8px;
}

@ -60,24 +60,21 @@
</div> </div>
<div class="right-content"> <div class="right-content">
<!-- tab切换 --> <!-- tab切换 -->
<a-tabs v-model:activeKey="activeKey" @change="(key)=>activeKey=key"> <div class="top-box">
<a-tab-pane v-for="item in tabsArr" :key="item.key" :tab="item.tab"></a-tab-pane> <a-tabs v-model:activeKey="activeKey" @change="(key)=>activeKey=key">
</a-tabs> <a-tab-pane v-for="item in tabsArr" :key="item.key" :tab="item.tab"></a-tab-pane>
</a-tabs>
<div class="top-timer" v-if="timeSelectArr.includes(activeKey)">
<el-date-picker v-model="dateRange" style="width: 260px" class="custom-date-picker"
value-format="YYYY-MM-DD HH:mm:ss" type="datetimerange" range-separator="至"
start-placeholder="开始日期" end-placeholder="结束日期" />
</div>
</div>
<div class="panel-views"> <div class="panel-views">
<div v-if="activeKey === 'historyTrend'" key="historyTrend" class="sub-content history-trend"> <div v-if="activeKey === 'historyTrend'" key="historyTrend" class="sub-content history-trend">
<!-- 历史趋势 -->
<div class="top-selector">
<el-date-picker v-model="dateRange" style="width: 360px" class="custom-date-picker"
value-format="YYYY-MM-DD HH:mm:ss" type="datetimerange" range-separator="至"
start-placeholder="开始日期" end-placeholder="结束日期" />
</div>
<div class="trend-graph-container"> <div class="trend-graph-container">
<div class="trend-item" v-for="item in trendGraphData" :key="item.id"> <history-trend-graph v-for="(item,index) in trendGraphData" :key="item.key"
<div class="title"> :ref="el => { if (el) trendGraphRefs[index] = el }" :data="item" />
监测点:{{ item.id }}
</div>
<div class="line-graph" :ref="(el) => trendGraphRefs(el, item.id)"></div>
</div>
</div> </div>
</div> </div>
@ -139,7 +136,7 @@
</div> </div>
<div v-if="activeKey === 'eventCount'" key="eventCount" class="sub-content"> <div v-if="activeKey === 'eventCount'" key="eventCount" class="sub-content">
<!-- 事件统计 --> <!-- 事件统计 -->
事件统计 <event-count :time="dateRange" :selected-key="selectedKey" :tree-data="fullTreeData" />
</div> </div>
<div v-if="activeKey === 'config'" key="config" class="sub-content config-page"> <div v-if="activeKey === 'config'" key="config" class="sub-content config-page">
<!-- 配置页面 --> <!-- 配置页面 -->
@ -179,7 +176,13 @@
</div> </div>
<a-button size="small" type="primary" style="margin-right: 4px;" <a-button size="small" type="primary" style="margin-right: 4px;"
@click="savePathConfig(item)">保存</a-button> @click="savePathConfig(item)">保存</a-button>
<a-button v-if="item.key==='MERGIN_ROOT_PATH'" size="small" type="primary"
style="margin-right: 4px;" :loading="isCurrentInit"
@click="initCatalogue">初始化</a-button>
<a-button size="small" @click="resetPathConfig(item.key)">重置</a-button> <a-button size="small" @click="resetPathConfig(item.key)">重置</a-button>
<p v-if="item.key==='MERGIN_ROOT_PATH'&&isCurrentInit" class="init-warning-tips">
当前初始化进度:{{initTips}}
</p>
</div> </div>
</div> </div>
</div> </div>
@ -264,8 +267,16 @@
</a-modal> </a-modal>
</div> </div>
<script> <script>
const { createApp, ref, onMounted, unref, watch, reactive, computed, nextTick, onUnmounted, components } = Vue;
const { message } = antd;
axios.defaults.baseURL = 'http://192.168.1.198:9501'; // 临时服务地址 axios.defaults.baseURL = 'http://192.168.1.198:9501'; // 临时服务地址
axios.defaults.timeout = 10000; axios.defaults.timeout = 10000;
const HISTORY_TREND_LEGEND = [
{ id: 'avg', name: '平均值' },
{ id: 'maxValue', name: '最大值' },
{ id: 'plusAvg', name: '脉冲平均值' },
{ id: 'plusCount', name: '脉冲次数' },
]
const findParentByKey = (tree, targetKey) => { const findParentByKey = (tree, targetKey) => {
for (const parent of tree) { for (const parent of tree) {
// 检查子节点 // 检查子节点
@ -308,8 +319,158 @@
} }
return arr; return arr;
} }
const { createApp, ref, onMounted, unref, watch, reactive, computed, nextTick, onUnmounted, components } = Vue; const findMonitorNameByKey = (arr, key) => {
const { message } = antd; let result = [];
arr.forEach(item => {
if (item?.children?.length) {
result = [...result, ...item.children]
}
});
return result.find(item => item.key === key)?.name
}
// 历史趋势图组件
const trendGraphRefs = ref([]);
watch(trendGraphRefs, (arr) => {
// 此处定时器解决异步
setTimeout(() => {
arr.forEach((el) => {
el.getChart().group = 'trend-group';
});
}, 300);
}, {
immediate: true,
deep: true,
},);
// 历史趋势图谱
const historyTrendGraph = {
template: `<div class='graph-container'>
<div class="title">
监测点:{{ data?.label||'' }}
</div>
<div ref='graphRef' class="line-graph"></div>
</div>`,
data() {
return {
resizeObserver: null
}
},
props: {
data: {
type: Object,
default: () => { }
}
},
// 定义常量不定义在data减少性能开销
created() {
this.chart = null
},
watch: {
data: {
async handler(val) {
const { startTime, endTime, key } = val
if (!key || !startTime) return
const params = {
current: 1,
startTime,
endTime,
monitorKey: key,
pageSize: 99999
}
const { data: { result: { result: { result } } } } = await axios.get(`/ldpdtools/trendData/listByParam`, {
params,
})
this.initTrendGraph(result || [])
},
deep: true,
immediate: true
}
},
async mounted() {
this.resizeObserver = new ResizeObserver(() => {
this.chart?.resize();
});
this.resizeObserver.observe(this.$refs.graphRef);
},
methods: {
initTrendGraph(data) {
// 图数据
const series = HISTORY_TREND_LEGEND.map((item, index) => ({
name: item.name,
type: 'line',
yAxisIndex: index,
data: data.map(el => el[item.id]),
smooth: true,
showSymbol: false,
lineStyle: {
width: 2,
},
}));
const legend = {
data: HISTORY_TREND_LEGEND.map(s => s.name), // 图例数据
top: 0,
};
const xAxis = {
type: 'category',
data: data.map(el => el.time.replace(' ', '\n')),
axisLine: {
onZero: false, // 禁止轴线对齐到0刻度强制固定在底部
},
name: '时间',
axisLabel: {
fontSize: 10,
},
};
const yAxis = HISTORY_TREND_LEGEND.map((item, index) => {
return {
type: 'value',
name: item.name,
nameTextStyle: {
color: '#666',
fontSize: 10,
padding: [0, 0, 0, 30], // 上、右、下、左左间距30px
},
triggerEvent: true, // 关键配置
axisLabel: {
formatter: '{value}',
fontSize: 10,
},
position: 'right',
offset: (index + 1) * 48, // 控制 Y 轴横向间距
splitLine: {
show: false, // 仅第一个 Y 轴显示网格线
},
splitNumber: 3, // Y轴仅显示3个刻度线
};
});
this.chart = echarts.init(this.$refs.graphRef);
const option = {
// 设置响应式
grid: {
top: 30,
right: 230,
bottom: 30,
left: 30
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'cross'
}
},
legend,
xAxis,
yAxis,
series,
}
// 3. 设置配置项并渲染图表
this.chart.setOption(option);
},
getChart() {
return this.chart;
}
},
}
// 累计prpd组件 // 累计prpd组件
const totalPrpd = { const totalPrpd = {
template: `<div ref="prpdBoxRef" style="width: 100%; height: 100%;"></div>`, template: `<div ref="prpdBoxRef" style="width: 100%; height: 100%;"></div>`,
@ -846,9 +1007,36 @@
} }
} }
} }
// 事件统计组件
const eventCount = {
components: {
historyTrendGraph,
},
template: `<div class='event-count-box'>
<history-trend-graph :data='graphInfo' />
</div>`,
data() {
return {
}
},
props: ['time', 'selectedKey', 'treeData'],
computed: {
graphInfo() {
return {
key: this.selectedKey,
label: findMonitorNameByKey(this.treeData, this.selectedKey),
startTime: this.time[0],
endTime: this.time[1],
};
}
},
methods: {
}
}
createApp({ createApp({
setup() { setup() {
const typeOfCheckable = ['historyTrend', 'countPRPD', 'prpdAndPrps'] const typeOfCheckable = ['historyTrend', 'countPRPD', 'prpdAndPrps']
const timeSelectArr = ['historyTrend', 'countPRPD', 'prpdAndPrps', 'eventCount'] // 含有时间选择的组件
const treeData = ref([]) const treeData = ref([])
const fullTreeData = ref([]) // 懒加载后的完整菜单数据 const fullTreeData = ref([]) // 懒加载后的完整菜单数据
const checkedKeys1 = ref([]); // 勾选 const checkedKeys1 = ref([]); // 勾选
@ -1102,165 +1290,19 @@
}) })
// 历史趋势部分逻辑 // 历史趋势部分逻辑
const trendGraphData = ref([]) const trendGraphData = ref([])
const trendGraphRefs = (el, id) => {
if (el) childRefs.value[id] = el;
};
const childRefs = ref({});
const dateRange = ref([]); const dateRange = ref([]);
const fetchHistoryTrendData = async (id, time) => {
try {
const params = {
current: 1,
startTime: time[0],
endTime: time[1],
monitorKey: id,
pageSize: 99999
}
const { data: { result: { result: { result } } } } = await axios.get(`/ldpdtools/trendData/listByParam`, {
params,
})
return {
id,
// label:
data: result || []
}
} catch (error) {
console.error(`请求 ${id} 时出错:`, error);
return null;
}
}
// 初始化趋势echart
const initTrendGraph = (el, data) => {
console.log(data, 'data');
const legendData = [
{ id: 'avg', name: '平均值' },
{ id: 'maxValue', name: '最大值' },
{ id: 'plusAvg', name: '脉冲平均值' },
{ id: 'plusCount', name: '脉冲次数' },
]
// 图数据
const series = legendData.map((item, index) => ({
name: item.name,
type: 'line',
yAxisIndex: index,
data: data.data.map(el => el[item.id]),
smooth: true,
showSymbol: false,
lineStyle: {
width: 2,
},
}));
const legend = {
data: legendData.map(s => s.name), // 图例数据
top: 0,
};
const xAxis = {
type: 'category',
data: data.data.map(el => el.time.replace(' ', '\n')),
axisLine: {
onZero: false, // 禁止轴线对齐到0刻度强制固定在底部
},
name: '时间',
axisLabel: {
fontSize: 10,
},
};
const yAxis = legendData.map((item, index) => {
return {
type: 'value',
name: item.name,
nameTextStyle: {
color: '#666',
fontSize: 10,
padding: [0, 0, 0, 30], // 上、右、下、左左间距30px
},
triggerEvent: true, // 关键配置
axisLabel: {
formatter: '{value}',
fontSize: 10,
},
position: 'right',
offset: (index + 1) * 48, // 控制 Y 轴横向间距
splitLine: {
show: false, // 仅第一个 Y 轴显示网格线
},
splitNumber: 3, // Y轴仅显示3个刻度线
};
});
const chart = echarts.init(el);
const option = {
// 设置响应式
grid: {
top: 30,
right: 230,
bottom: 30,
left: 30
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'cross'
}
},
legend,
xAxis,
yAxis,
series
};
// 3. 设置配置项并渲染图表
chart.setOption(option);
// 4. 窗口大小改变时重新调整图表大小
const resizeHandler = () => chart.resize();
window.addEventListener('resize', resizeHandler);
// 返回图表实例和清理函数
return {
instance: chart,
dispose: () => {
window.removeEventListener('resize', resizeHandler);
chart.dispose();
}
};
}
const chartGroups = ref({});
// tab在历史趋势监听勾选节点 // tab在历史趋势监听勾选节点
watch([checkedKeys1, dateRange], async ([keys, time]) => { watch([checkedKeys1, dateRange], async ([keys, time]) => {
const [starTime, endTime] = time || []; const [startTime, endTime] = time || [];
if (unref(activeKey) !== 'historyTrend' || !starTime || !endTime) return; if (unref(activeKey) !== 'historyTrend' || !startTime || !endTime) return;
// 先清理之前的图表 echarts.connect('trend-group'); // 通过分组连接
Object.values(chartGroups.value).forEach(group => { trendGraphData.value = keys.map((key) => {
group.instances.forEach(chart => chart.dispose()); return {
}); key,
chartGroups.value = {}; label: findMonitorNameByKey(unref(fullTreeData), key),
const promises = keys.map((key) => fetchHistoryTrendData(key, time)); startTime,
trendGraphData.value = await Promise.all(promises); endTime,
nextTick(() => { };
// 按分组存储图表实例
const groupMap = {};
Object.keys(childRefs.value).forEach((id) => {
const childEl = childRefs.value[id];
const childData = trendGraphData.value.find((item) => item.id === id);
if (childEl && childData) {
const { instance, dispose } = initTrendGraph(childEl, childData);
// 假设你的数据中有 groupId 字段表示分组
const groupId = childData.groupId || 'default';
if (!groupMap[groupId]) {
groupMap[groupId] = [];
}
groupMap[groupId].push(instance);
}
});
// 连接同一组的图表
Object.keys(groupMap).forEach(groupId => {
if (groupMap[groupId].length > 1) {
echarts.connect(groupMap[groupId]);
}
// 存储分组信息
chartGroups.value[groupId] = {
instances: groupMap[groupId],
// 可以存储其他分组相关信息
};
});
}); });
}); });
// 告警信息 // 告警信息
@ -1473,6 +1515,8 @@
} }
} }
const allPathCongfigs = ref([]) // 所有路径配置 const allPathCongfigs = ref([]) // 所有路径配置
const isCurrentInit = ref(false) // 是否正在初始化
const initTips = ref('')
// 获取所有配置项 // 获取所有配置项
const getAllConfigs = async () => { const getAllConfigs = async () => {
allPathCongfigs.value = [] allPathCongfigs.value = []
@ -1489,6 +1533,13 @@
allPathCongfigs.value.forEach(item => { allPathCongfigs.value.forEach(item => {
item.value = paths[item.key] || '' item.value = paths[item.key] || ''
}) })
// 排序
const itemIdToMove = "MERGIN_ROOT_PATH";
const itemToMove = unref(allPathCongfigs).find(item => item.key === itemIdToMove);
const remainingItems = unref(allPathCongfigs).filter(item => item.key !== itemIdToMove);
if (itemToMove) {
allPathCongfigs.value = [...remainingItems, itemToMove];
}
} }
// 保存路径 // 保存路径
const savePathConfig = async (data) => { const savePathConfig = async (data) => {
@ -1508,17 +1559,33 @@
message.success('重置成功!') message.success('重置成功!')
getAllConfigs() getAllConfigs()
} }
// 获取初始化进度
const getInitPrgress = async () => {
const { data: { message } } = await axios.get('/ldpdtools/config/getInitProgress')
isCurrentInit.value = message.includes('/')
initTips.value = message
}
// 初始化目录
const initCatalogue = async () => {
const { data: { code } } = await axios.get('/ldpdtools/config/initConfig')
if (code === 200) {
setTimeout(() => {
getInitPrgress()
}, 1000) // 后台有一定延迟
}
}
// 监听tab切换 // 监听tab切换
watch(activeKey, (tabKey, oldKey) => { watch(activeKey, (tabKey, oldKey) => {
checkedKeys1.value = [] // 先注释切换tab不清空树选中状态
treeRef.value.setCheckedKeys([]); // checkedKeys1.value = []
selectedKey.value = '' // treeRef.value.setCheckedKeys([]);
treeRef.value.setCurrentKey(null); // selectedKey.value = ''
// treeRef.value.setCurrentKey(null);
if (oldKey === 'historyTrend') { if (oldKey === 'historyTrend') {
// 销毁历史趋势数据 // 销毁历史趋势数据
trendGraphData.value = [] // trendGraphData.value = []
childRefs.value = {} // dateRange.value = []
dateRange.value = []
} }
if (oldKey === 'alarmConfig') { if (oldKey === 'alarmConfig') {
// 销毁告警管理 // 销毁告警管理
@ -1542,6 +1609,7 @@
onMounted(() => { onMounted(() => {
initTreeData() initTreeData()
getAllConfigs() getAllConfigs()
getInitPrgress()
document.addEventListener('click', handleClickOutside); document.addEventListener('click', handleClickOutside);
}) })
onUnmounted(() => { onUnmounted(() => {
@ -1583,9 +1651,8 @@
dateRange, dateRange,
checkTree, checkTree,
trendGraphData, trendGraphData,
trendGraphRefs,
chartGroups,
typeOfCheckable, typeOfCheckable,
timeSelectArr,
columns, columns,
dataSource, dataSource,
pagination, pagination,
@ -1608,12 +1675,18 @@
allPathCongfigs, allPathCongfigs,
savePathConfig, savePathConfig,
resetPathConfig, resetPathConfig,
prpdAndPrpsRefs prpdAndPrpsRefs,
initCatalogue,
isCurrentInit,
initTips,
trendGraphRefs
}; };
}, },
components: { components: {
totalPrpd, totalPrpd,
prpdAndPrps prpdAndPrps,
eventCount,
historyTrendGraph
} }
}) })
.use(antd) .use(antd)

Loading…
Cancel
Save