17 changed files with 2684 additions and 9 deletions
@ -0,0 +1,316 @@
@@ -0,0 +1,316 @@
|
||||
<template> |
||||
<!-- <div > --> |
||||
<div ref="ChartsTimeBarRef" style="height:100%;width: 100%;" class="chart-container"></div> |
||||
<!-- </div> --> |
||||
</template> |
||||
<script setup> |
||||
import * as echarts from "echarts"; |
||||
import dayjs from 'dayjs' |
||||
import { onMounted, nextTick, ref, onUnmounted } from "vue"; |
||||
const props = defineProps({ |
||||
legendData: { |
||||
type: Array, |
||||
default: () => [] |
||||
}, |
||||
xAxisData: { |
||||
type: Array, |
||||
default: () => [] |
||||
}, |
||||
seriesData: { |
||||
type: Array, |
||||
default: () => [] |
||||
}, |
||||
textTitle: { |
||||
type: String, |
||||
default: '' |
||||
}, |
||||
unit: { |
||||
type: String, |
||||
default: 'mm' |
||||
}, |
||||
echartType: { |
||||
type: String, |
||||
default: 'bar' |
||||
}, |
||||
grid: { |
||||
type: Object, |
||||
default: () => { |
||||
return { |
||||
left: "5%", |
||||
right: "5%", |
||||
bottom: "16%", |
||||
top: "16%", |
||||
containLabel: true, |
||||
} |
||||
} |
||||
}, |
||||
// Y轴name的位置 |
||||
YNameLocation: { |
||||
type: String, |
||||
default: "middle" |
||||
}, |
||||
colors: { |
||||
type: Array, |
||||
default: () => ['#248ff7', '#6f5be8'] |
||||
}, |
||||
BarFormatter: { |
||||
type: Boolean, |
||||
default: true |
||||
} |
||||
|
||||
|
||||
}) |
||||
const ChartsTimeBarRef = ref(null); |
||||
let echartsBar = null; |
||||
let resizeObserver = null; |
||||
// 监听props变化,确保数据更新后重新初始化图表 |
||||
watch([() => props.legendData, () => props.xAxisData, () => props.seriesData], () => { |
||||
if (echartsBar) { |
||||
updateChart(); |
||||
} |
||||
}, { deep: true }); |
||||
onMounted(() => { |
||||
initEcharts(); |
||||
window.addEventListener('resize', handleResize); |
||||
// 监听容器本身尺寸变化 |
||||
if (ChartsTimeBarRef.value) { |
||||
resizeObserver = new ResizeObserver(handleResize); |
||||
resizeObserver.observe(ChartsTimeBarRef.value); |
||||
} |
||||
}); |
||||
|
||||
onUnmounted(() => { |
||||
window.removeEventListener('resize', handleResize); |
||||
if (resizeObserver && ChartsTimeBarRef.value) { |
||||
resizeObserver.unobserve(ChartsTimeBarRef.value); |
||||
} |
||||
if (echartsBar) { |
||||
echartsBar.dispose(); |
||||
} |
||||
}); |
||||
const echartsLoading = ref(true); |
||||
const initEcharts = () => { |
||||
nextTick(() => { |
||||
if (ChartsTimeBarRef.value) { |
||||
echartsBar = echarts.init(ChartsTimeBarRef.value, "macarons"); |
||||
let option = getChartOption(); |
||||
echartsBar.setOption(option); |
||||
|
||||
// 确保图表尺寸适应容器 |
||||
const resizeChart = () => { |
||||
if (echartsBar && ChartsTimeBarRef.value) { |
||||
echartsBar.resize({ |
||||
width: ChartsTimeBarRef.value.clientWidth, |
||||
height: ChartsTimeBarRef.value.clientHeight |
||||
}); |
||||
} |
||||
}; |
||||
|
||||
// 初始化时调整一次大小 |
||||
resizeChart(); |
||||
} |
||||
}); |
||||
}; |
||||
const updateChart = () => { |
||||
nextTick(() => { |
||||
if (echartsBar) { |
||||
let option = getChartOption(); |
||||
echartsBar.setOption(option, true); // 使用notMerge:true来完全替换旧选项 |
||||
resizeChart(); |
||||
} |
||||
}); |
||||
}; |
||||
|
||||
const resizeChart = () => { |
||||
if (echartsBar && ChartsTimeBarRef.value) { |
||||
echartsBar.resize({ |
||||
width: ChartsTimeBarRef.value.clientWidth, |
||||
height: ChartsTimeBarRef.value.clientHeight |
||||
}); |
||||
} |
||||
}; |
||||
const handleResize = () => { |
||||
if (echartsBar && ChartsTimeBarRef.value) { |
||||
// 添加防抖机制,避免频繁触发 |
||||
clearTimeout(window.resizeTimer); |
||||
window.resizeTimer = setTimeout(() => { |
||||
if (echartsBar && ChartsTimeBarRef.value) { |
||||
echartsBar.resize({ |
||||
width: ChartsTimeBarRef.value.clientWidth, |
||||
height: ChartsTimeBarRef.value.clientHeight, |
||||
animation: { |
||||
duration: 300 |
||||
} |
||||
}); |
||||
} |
||||
}, 100); |
||||
} |
||||
}; |
||||
|
||||
|
||||
const getChartOption = () => { |
||||
const series = [ |
||||
{ |
||||
type: props.echartType, |
||||
barWidth: "20", |
||||
showSymbol: false, |
||||
itemStyle: { |
||||
// normal 已废弃,直接使用属性 |
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ |
||||
{ offset: 0, color: props.colors[0] }, |
||||
{ offset: 1, color: props.colors[1] } |
||||
]), |
||||
barBorderRadius: 12, |
||||
}, |
||||
data: props.seriesData, |
||||
} |
||||
|
||||
]; |
||||
let option = { |
||||
// backgroundColor: "#323a5e", |
||||
title: { |
||||
text: props.textTitle, |
||||
textStyle: { |
||||
align: "center", |
||||
color: "#000", |
||||
fontSize: 20, |
||||
}, |
||||
top: "3%", |
||||
left: "50%", |
||||
}, |
||||
tooltip: { |
||||
trigger: "axis", |
||||
axisPointer: { |
||||
// 坐标轴指示器,坐标轴触发有效 |
||||
type: "shadow", // 默认为直线,可选为:'line' | 'shadow' |
||||
}, |
||||
}, |
||||
grid: props.grid, |
||||
legend: { |
||||
data: props.legendData, |
||||
right: 10, |
||||
top: 12, |
||||
textStyle: { |
||||
color: "#000", |
||||
}, |
||||
itemWidth: 12, |
||||
itemHeight: 10, |
||||
// itemGap: 35 |
||||
}, |
||||
xAxis: { |
||||
type: "category", |
||||
data: props.xAxisData, |
||||
axisLabel: props.echartType === 'line' || props.BarFormatter ? { |
||||
// 折线图专用配置 - 时间格式化 |
||||
textStyle: { |
||||
fontFamily: "Microsoft YaHei", |
||||
}, |
||||
showMinLabel: true, // 强制显示最小值标签 |
||||
showMaxLabel: true, // 强制显示最大值标签 |
||||
formatter: function (value) { |
||||
let date = dayjs(value) |
||||
let yearMonthDay = date.format('YYYY-MM-DD') |
||||
let hourMinute = date.format('HH:mm') |
||||
let result = `{a|${hourMinute}}\n{b|${yearMonthDay}}`; |
||||
if (props.timeType == 'day') { |
||||
result = `{b|${yearMonthDay}}`; |
||||
} |
||||
return result |
||||
}, |
||||
rich: { |
||||
a: { |
||||
fontSize: 12, |
||||
color: '#000', |
||||
padding: [0, 0, 5, 0] |
||||
}, |
||||
b: { |
||||
fontSize: 12, |
||||
color: '#000' |
||||
} |
||||
} |
||||
} : { |
||||
// 柱状图专用配置 - 简单标签显示 |
||||
rotate: props.xAxisData.length > 10 ? 45 : 0, |
||||
interval: 0, |
||||
margin: 20, |
||||
textStyle: { |
||||
fontFamily: "Microsoft YaHei", |
||||
fontSize: 16 |
||||
} |
||||
} |
||||
}, |
||||
|
||||
yAxis: { |
||||
type: "value", |
||||
name: props.unit, |
||||
nameLocation: props.YNameLocation, |
||||
nameTextStyle: {//y轴上方单位的颜色 |
||||
color: '#464b50' |
||||
}, |
||||
nameGap: 50, |
||||
axisLine: { |
||||
show: true, |
||||
lineStyle: { |
||||
color: "#ccc", |
||||
}, |
||||
}, |
||||
|
||||
splitLine: { |
||||
show: true, |
||||
lineStyle: { |
||||
color: "#ccc", |
||||
}, |
||||
}, |
||||
axisLabel: { |
||||
textStyle: { |
||||
color: "#000000", |
||||
}, |
||||
// 添加formatter属性,保留两位小数 |
||||
formatter: function (value) { |
||||
return value.toFixed(2); |
||||
} |
||||
}, |
||||
}, |
||||
|
||||
series: series, |
||||
}; |
||||
|
||||
return option |
||||
// var app = { |
||||
// currentIndex: -1, |
||||
// }; |
||||
// setInterval(function () { |
||||
// var dataLen = option.series[0].data.length; |
||||
|
||||
// // 取消之前高亮的图形 |
||||
// myChart.dispatchAction({ |
||||
// type: "downplay", |
||||
// seriesIndex: 0, |
||||
// dataIndex: app.currentIndex, |
||||
// }); |
||||
// app.currentIndex = (app.currentIndex + 1) % dataLen; |
||||
// //console.log(app.currentIndex); |
||||
// // 高亮当前图形 |
||||
// myChart.dispatchAction({ |
||||
// type: "highlight", |
||||
// seriesIndex: 0, |
||||
// dataIndex: app.currentIndex, |
||||
// }); |
||||
// // 显示 tooltip |
||||
// myChart.dispatchAction({ |
||||
// type: "showTip", |
||||
// seriesIndex: 0, |
||||
// dataIndex: app.currentIndex, |
||||
// }); |
||||
// }, 1000); |
||||
|
||||
} |
||||
</script> |
||||
<style scoped> |
||||
.chart-container { |
||||
width: 100%; |
||||
height: 100%; |
||||
min-height: 300px; |
||||
position: relative; |
||||
} |
||||
</style> |
||||
@ -0,0 +1,310 @@
@@ -0,0 +1,310 @@
|
||||
<template> |
||||
<!-- <div > --> |
||||
<div ref="ChartsTimeBarRef" style="height:100%;width: 100%;" class="chart-container"></div> |
||||
<!-- </div> --> |
||||
</template> |
||||
<script setup> |
||||
import * as echarts from "echarts"; |
||||
import dayjs from 'dayjs' |
||||
import { onMounted, nextTick, ref, onUnmounted } from "vue"; |
||||
const props = defineProps({ |
||||
legendData: { |
||||
type: Array, |
||||
default: () => [] |
||||
}, |
||||
xAxisData: { |
||||
type: Array, |
||||
default: () => [] |
||||
}, |
||||
seriesData: { |
||||
type: Array, |
||||
default: () => [] |
||||
}, |
||||
textTitle: { |
||||
type: String, |
||||
default: '' |
||||
}, |
||||
unit: { |
||||
type: String, |
||||
default: 'mm' |
||||
}, |
||||
echartType: { |
||||
type: String, |
||||
default: 'bar' |
||||
}, |
||||
grid: { |
||||
type: Object, |
||||
default: () => { |
||||
return { |
||||
left: "5%", |
||||
right: "5%", |
||||
bottom: "16%", |
||||
top: "16%", |
||||
containLabel: true, |
||||
} |
||||
} |
||||
} |
||||
|
||||
}) |
||||
const ChartsTimeBarRef = ref(null); |
||||
let echartsBar = null; |
||||
let resizeObserver = null; |
||||
// 监听props变化,确保数据更新后重新初始化图表 |
||||
watch([() => props.legendData, () => props.xAxisData, () => props.seriesData], () => { |
||||
if (echartsBar) { |
||||
|
||||
updateChart(); |
||||
} |
||||
}, { deep: true }); |
||||
onMounted(() => { |
||||
|
||||
|
||||
initEcharts(); |
||||
window.addEventListener('resize', handleResize); |
||||
// 监听容器本身尺寸变化 |
||||
if (ChartsTimeBarRef.value) { |
||||
resizeObserver = new ResizeObserver(handleResize); |
||||
resizeObserver.observe(ChartsTimeBarRef.value); |
||||
} |
||||
}); |
||||
|
||||
onUnmounted(() => { |
||||
window.removeEventListener('resize', handleResize); |
||||
if (resizeObserver && ChartsTimeBarRef.value) { |
||||
resizeObserver.unobserve(ChartsTimeBarRef.value); |
||||
} |
||||
if (echartsBar) { |
||||
echartsBar.dispose(); |
||||
} |
||||
}); |
||||
const echartsLoading = ref(true); |
||||
const initEcharts = () => { |
||||
nextTick(() => { |
||||
if (ChartsTimeBarRef.value) { |
||||
echartsBar = echarts.init(ChartsTimeBarRef.value, "macarons"); |
||||
let option = getChartOption(); |
||||
echartsBar.setOption(option); |
||||
|
||||
// 确保图表尺寸适应容器 |
||||
const resizeChart = () => { |
||||
if (echartsBar && ChartsTimeBarRef.value) { |
||||
echartsBar.resize({ |
||||
width: ChartsTimeBarRef.value.clientWidth, |
||||
height: ChartsTimeBarRef.value.clientHeight |
||||
}); |
||||
} |
||||
}; |
||||
|
||||
// 初始化时调整一次大小 |
||||
resizeChart(); |
||||
} |
||||
}); |
||||
}; |
||||
const updateChart = () => { |
||||
nextTick(() => { |
||||
if (echartsBar) { |
||||
let option = getChartOption(); |
||||
echartsBar.setOption(option, true); // 使用notMerge:true来完全替换旧选项 |
||||
resizeChart(); |
||||
} |
||||
}); |
||||
}; |
||||
|
||||
const resizeChart = () => { |
||||
if (echartsBar && ChartsTimeBarRef.value) { |
||||
echartsBar.resize({ |
||||
width: ChartsTimeBarRef.value.clientWidth, |
||||
height: ChartsTimeBarRef.value.clientHeight |
||||
}); |
||||
} |
||||
}; |
||||
const handleResize = () => { |
||||
if (echartsBar && ChartsTimeBarRef.value) { |
||||
// 添加防抖机制,避免频繁触发 |
||||
clearTimeout(window.resizeTimer); |
||||
window.resizeTimer = setTimeout(() => { |
||||
echartsBar.resize({ |
||||
width: ChartsTimeBarRef.value.clientWidth, |
||||
height: ChartsTimeBarRef.value.clientHeight, |
||||
animation: { |
||||
duration: 300 |
||||
} |
||||
}); |
||||
}, 100); |
||||
} |
||||
}; |
||||
|
||||
|
||||
const getChartOption = () => { |
||||
console.log(props.legendData, props.xAxisData, props.seriesData) |
||||
const series = props.seriesData.map((item, index) => { |
||||
const colors = [ |
||||
["#248ff7", "#6f5be8"], |
||||
["#248ff7", "#6f5be8"], |
||||
|
||||
]; |
||||
const color = colors[index % colors.length]; |
||||
|
||||
return { |
||||
name: props.legendData[index], |
||||
type: item.type, |
||||
barWidth: "20", |
||||
yAxisIndex: index, |
||||
showSymbol: false, |
||||
itemStyle: { |
||||
normal: { |
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ |
||||
{ offset: 0, color: color[0] }, |
||||
{ offset: 1, color: color[1] } |
||||
]), |
||||
barBorderRadius: 12, |
||||
}, |
||||
}, |
||||
data: item.data?.map(val => val[1]), |
||||
}; |
||||
}); |
||||
let option = { |
||||
// backgroundColor: "#323a5e", |
||||
title: { |
||||
text: props.textTitle, |
||||
textStyle: { |
||||
align: "center", |
||||
color: "#000", |
||||
fontSize: 20, |
||||
}, |
||||
top: "3%", |
||||
left: "50%", |
||||
}, |
||||
tooltip: { |
||||
trigger: "axis", |
||||
axisPointer: { |
||||
// 坐标轴指示器,坐标轴触发有效 |
||||
type: "shadow", // 默认为直线,可选为:'line' | 'shadow' |
||||
}, |
||||
}, |
||||
grid: props.grid, |
||||
legend: { |
||||
data: props.legendData, |
||||
right: 10, |
||||
top: 12, |
||||
textStyle: { |
||||
color: "#000", |
||||
}, |
||||
itemWidth: 12, |
||||
itemHeight: 10, |
||||
// itemGap: 35 |
||||
}, |
||||
xAxis: { |
||||
type: "category", |
||||
data: props.xAxisData, |
||||
axisLine: { |
||||
lineStyle: { |
||||
color: "#ccc", |
||||
}, |
||||
}, |
||||
|
||||
axisLabel: { |
||||
textStyle: { |
||||
fontFamily: "Microsoft YaHei", |
||||
}, |
||||
showMinLabel: true, // 强制显示最小值标签 |
||||
showMaxLabel: true, // 强制显示最大值标签 |
||||
formatter: function (value) { |
||||
let date = dayjs(value) |
||||
let yearMonthDay = date.format('YYYY-MM-DD') |
||||
let hourMinute = date.format('HH:mm') |
||||
let result = `{a|${hourMinute}}\n{b|${yearMonthDay}}`; |
||||
if (props.timeType == 'day') { |
||||
result = `{b|${yearMonthDay}}`; |
||||
} |
||||
return result |
||||
}, |
||||
|
||||
rich: { |
||||
a: { |
||||
fontSize: 12, |
||||
color: '#000', |
||||
padding: [0, 0, 5, 0] // 上右下左的内边距,这里给上方小时分钟添加一点底部间距 |
||||
}, |
||||
b: { |
||||
fontSize: 12, |
||||
color: '#000' |
||||
} |
||||
} |
||||
}, |
||||
}, |
||||
|
||||
yAxis: [ |
||||
|
||||
{ // 水位 - 右侧 |
||||
type: 'value', |
||||
name: '水位 (m)', |
||||
axisLabel: { color: '#EE6666' }, |
||||
nameTextStyle: { color: '#EE6666', fontWeight: 'bold' }, |
||||
splitLine: { |
||||
show: true, |
||||
lineStyle: { |
||||
color: "#ccc", |
||||
}, |
||||
}, |
||||
}, |
||||
{ // 雨量 - 左侧 |
||||
type: 'value', |
||||
name: '雨量 (mm)', |
||||
inverse: true, |
||||
nameLocation: 'start', |
||||
position: 'right', |
||||
axisLabel: { color: '#1075FD' }, |
||||
nameTextStyle: { color: '#1075FD', fontWeight: 'bold' }, |
||||
splitLine: { |
||||
show: true, |
||||
lineStyle: { |
||||
color: "#ccc", |
||||
}, |
||||
}, |
||||
|
||||
} |
||||
], |
||||
|
||||
series: series, |
||||
}; |
||||
|
||||
return option |
||||
// var app = { |
||||
// currentIndex: -1, |
||||
// }; |
||||
// setInterval(function () { |
||||
// var dataLen = option.series[0].data.length; |
||||
|
||||
// // 取消之前高亮的图形 |
||||
// myChart.dispatchAction({ |
||||
// type: "downplay", |
||||
// seriesIndex: 0, |
||||
// dataIndex: app.currentIndex, |
||||
// }); |
||||
// app.currentIndex = (app.currentIndex + 1) % dataLen; |
||||
// //console.log(app.currentIndex); |
||||
// // 高亮当前图形 |
||||
// myChart.dispatchAction({ |
||||
// type: "highlight", |
||||
// seriesIndex: 0, |
||||
// dataIndex: app.currentIndex, |
||||
// }); |
||||
// // 显示 tooltip |
||||
// myChart.dispatchAction({ |
||||
// type: "showTip", |
||||
// seriesIndex: 0, |
||||
// dataIndex: app.currentIndex, |
||||
// }); |
||||
// }, 1000); |
||||
|
||||
} |
||||
</script> |
||||
<style scoped> |
||||
.chart-container { |
||||
width: 100%; |
||||
height: 100%; |
||||
min-height: 300px; |
||||
position: relative; |
||||
} |
||||
</style> |
||||
@ -0,0 +1,382 @@
@@ -0,0 +1,382 @@
|
||||
<template> |
||||
<!-- <div > --> |
||||
<div ref="ChartsTimeBarRef" style="height:100%;width: 100%;" class="chart-container"></div> |
||||
<!-- </div> --> |
||||
</template> |
||||
<script setup> |
||||
import * as echarts from "echarts"; |
||||
import dayjs from 'dayjs' |
||||
import { onMounted, nextTick, ref, onUnmounted } from "vue"; |
||||
const props = defineProps({ |
||||
legendData: { |
||||
type: Array, |
||||
default: () => [] |
||||
}, |
||||
xAxisData: { |
||||
type: Array, |
||||
default: () => [] |
||||
}, |
||||
seriesData: { |
||||
type: Array, |
||||
default: () => [] |
||||
}, |
||||
textTitle: { |
||||
type: String, |
||||
default: '' |
||||
}, |
||||
unit: { |
||||
type: String, |
||||
default: 'mm' |
||||
}, |
||||
echartType: { |
||||
type: String, |
||||
default: 'bar' |
||||
}, |
||||
timeType: { |
||||
type: String, |
||||
default: 'hour' |
||||
} |
||||
|
||||
}) |
||||
const ChartsTimeBarRef = ref(null); |
||||
let echartsBar = null; |
||||
let resizeObserver = null; |
||||
// 监听props变化,确保数据更新后重新初始化图表 |
||||
watch([() => props.legendData, () => props.xAxisData, () => props.seriesData], () => { |
||||
if (echartsBar) { |
||||
updateChart(); |
||||
} |
||||
}, { deep: true }); |
||||
onMounted(() => { |
||||
initEcharts(); |
||||
window.addEventListener('resize', handleResize); |
||||
// 监听容器本身尺寸变化 |
||||
if (ChartsTimeBarRef.value) { |
||||
resizeObserver = new ResizeObserver(handleResize); |
||||
resizeObserver.observe(ChartsTimeBarRef.value); |
||||
} |
||||
}); |
||||
|
||||
onUnmounted(() => { |
||||
window.removeEventListener('resize', handleResize); |
||||
if (resizeObserver && ChartsTimeBarRef.value) { |
||||
resizeObserver.unobserve(ChartsTimeBarRef.value); |
||||
} |
||||
if (echartsBar) { |
||||
echartsBar.dispose(); |
||||
} |
||||
}); |
||||
const echartsLoading = ref(true); |
||||
const initEcharts = () => { |
||||
nextTick(() => { |
||||
if (ChartsTimeBarRef.value) { |
||||
echartsBar = echarts.init(ChartsTimeBarRef.value, "macarons"); |
||||
let option = getChartOption(); |
||||
echartsBar.setOption(option); |
||||
|
||||
// 确保图表尺寸适应容器 |
||||
const resizeChart = () => { |
||||
if (echartsBar && ChartsTimeBarRef.value) { |
||||
echartsBar.resize({ |
||||
width: ChartsTimeBarRef.value.clientWidth, |
||||
height: ChartsTimeBarRef.value.clientHeight |
||||
}); |
||||
} |
||||
}; |
||||
|
||||
// 初始化时调整一次大小 |
||||
resizeChart(); |
||||
} |
||||
}); |
||||
}; |
||||
const updateChart = () => { |
||||
nextTick(() => { |
||||
if (echartsBar) { |
||||
let option = getChartOption(); |
||||
echartsBar.setOption(option, true); // 使用notMerge:true来完全替换旧选项 |
||||
resizeChart(); |
||||
} |
||||
}); |
||||
}; |
||||
|
||||
const resizeChart = () => { |
||||
if (echartsBar && ChartsTimeBarRef.value) { |
||||
echartsBar.resize({ |
||||
width: ChartsTimeBarRef.value.clientWidth, |
||||
height: ChartsTimeBarRef.value.clientHeight |
||||
}); |
||||
} |
||||
}; |
||||
const handleResize = () => { |
||||
if (echartsBar && ChartsTimeBarRef.value) { |
||||
// 添加防抖机制,避免频繁触发 |
||||
clearTimeout(window.resizeTimer); |
||||
window.resizeTimer = setTimeout(() => { |
||||
echartsBar.resize({ |
||||
width: ChartsTimeBarRef.value.clientWidth, |
||||
height: ChartsTimeBarRef.value.clientHeight, |
||||
animation: { |
||||
duration: 300 |
||||
} |
||||
}); |
||||
}, 100); |
||||
} |
||||
}; |
||||
|
||||
|
||||
const getChartOption = () => { |
||||
let allData = []; |
||||
props.seriesData.forEach(series => { |
||||
if (series.data) { |
||||
series.data.forEach(item => { |
||||
// 根据数据结构获取数值,可能是 [time, value] 或 {tm: time, value: value} 格式 |
||||
const value = Array.isArray(item) ? item[1] : (item.value !== undefined ? item.value : item); |
||||
if (value !== undefined && value !== null && !isNaN(value)) { |
||||
allData.push(Number(value)); |
||||
} |
||||
}); |
||||
} |
||||
}); |
||||
|
||||
// 计算y轴范围 |
||||
let yAxisMin = 0; |
||||
let yAxisMax = 100; |
||||
|
||||
if (allData.length > 0) { |
||||
const minVal = Math.min(...allData); |
||||
const maxVal = Math.max(...allData); |
||||
|
||||
// 添加一些边距,使图表更美观 |
||||
const range = maxVal - minVal; |
||||
const margin = range > 0 ? range * 0.1 : 1; |
||||
|
||||
yAxisMin = Math.floor((minVal - margin) * 100) / 100; // 保留两位小数 |
||||
yAxisMax = Math.ceil((maxVal + margin) * 100) / 100; // 保留两位小数 |
||||
|
||||
// 如果数据值都很小,确保最小值不为负数(可根据实际需求调整) |
||||
if (minVal >= 0 && yAxisMin < 0) { |
||||
yAxisMin = 0; |
||||
} |
||||
} |
||||
const series = props.seriesData.map((item, index) => { |
||||
const colors = [ |
||||
["#FA8072", "#B22222"], |
||||
["#0fec7d", "#04793e"], |
||||
["#ab36fc", "#7705a4"], |
||||
["#ffbd89", "#8a5c08"], |
||||
["#CD9B1D", "#8B6914"], |
||||
["#ff7f50", "#a93f07"], |
||||
["#EE1289", "#8B0A50"], |
||||
["#ee9c9c", "#755252"], |
||||
["#06e8d6", "#078075"], |
||||
["#de79c2", "#cd5c5c"], |
||||
["#00F5FF", "#6b8e23"], |
||||
["#f50000", "#590404"], |
||||
["#00CD66", "#3cb371"], |
||||
["#c400fa", "#48005d"], |
||||
["#eca86c", "#5e4310"], |
||||
["#bebe0f", "#8B8B00"] |
||||
]; |
||||
const color = colors[index % colors.length]; |
||||
|
||||
return { |
||||
name: props.legendData[index], |
||||
type: props.echartType, |
||||
barWidth: "20", |
||||
showSymbol: false, |
||||
itemStyle: { |
||||
normal: { |
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ |
||||
{ offset: 0, color: color[0] }, |
||||
{ offset: 1, color: color[1] } |
||||
]), |
||||
barBorderRadius: 12, |
||||
}, |
||||
}, |
||||
data: item.data?.map(val => val[1]), |
||||
}; |
||||
}); |
||||
let option = { |
||||
// backgroundColor: "#323a5e", |
||||
title: { |
||||
text: props.textTitle, |
||||
textStyle: { |
||||
align: "center", |
||||
color: "#000", |
||||
fontSize: 20, |
||||
}, |
||||
top: "3%", |
||||
left: "50%", |
||||
}, |
||||
tooltip: { |
||||
trigger: "axis", |
||||
axisPointer: { |
||||
// 坐标轴指示器,坐标轴触发有效 |
||||
type: "shadow", // 默认为直线,可选为:'line' | 'shadow' |
||||
}, |
||||
}, |
||||
grid: { |
||||
left: "5%", |
||||
right: "5%", |
||||
bottom: "16%", |
||||
top: "16%", |
||||
containLabel: true, |
||||
}, |
||||
legend: { |
||||
data: props.legendData, |
||||
right: 10, |
||||
top: 12, |
||||
textStyle: { |
||||
color: "#000", |
||||
}, |
||||
itemWidth: 12, |
||||
itemHeight: 10, |
||||
// itemGap: 35 |
||||
}, |
||||
xAxis: { |
||||
type: "category", |
||||
data: props.xAxisData, |
||||
axisLine: { |
||||
lineStyle: { |
||||
color: "#ccc", |
||||
}, |
||||
}, |
||||
|
||||
axisLabel: { |
||||
textStyle: { |
||||
fontFamily: "Microsoft YaHei", |
||||
}, |
||||
showMinLabel: true, // 强制显示最小值标签 |
||||
showMaxLabel: true, // 强制显示最大值标签 |
||||
formatter: function (value) { |
||||
let date = dayjs(value) |
||||
let yearMonthDay = date.format('YYYY-MM-DD') |
||||
let hourMinute = date.format('HH:mm') |
||||
let result = `{a|${hourMinute}}\n{b|${yearMonthDay}}`; |
||||
if (props.timeType == 'day') { |
||||
result = `{b|${yearMonthDay}}`; |
||||
} |
||||
return result |
||||
}, |
||||
rich: { |
||||
a: { |
||||
fontSize: 12, |
||||
color: '#000', |
||||
padding: [0, 0, 5, 0] // 上右下左的内边距,这里给上方小时分钟添加一点底部间距 |
||||
}, |
||||
b: { |
||||
fontSize: 12, |
||||
color: '#000' |
||||
} |
||||
} |
||||
}, |
||||
}, |
||||
|
||||
yAxis: { |
||||
type: "value", |
||||
min: yAxisMin, |
||||
max: yAxisMax, |
||||
name: props.unit, |
||||
nameLocation: 'middle', |
||||
nameTextStyle: {//y轴上方单位的颜色 |
||||
color: '#464b50' |
||||
}, |
||||
nameGap: 50, |
||||
axisLine: { |
||||
show: true, |
||||
lineStyle: { |
||||
color: "#ccc", |
||||
}, |
||||
}, |
||||
|
||||
splitLine: { |
||||
show: true, |
||||
lineStyle: { |
||||
color: "#ccc", |
||||
}, |
||||
}, |
||||
axisLabel: { |
||||
textStyle: { |
||||
color: "#000000", |
||||
}, |
||||
// 添加formatter属性,保留两位小数 |
||||
formatter: function (value) { |
||||
return value.toFixed(2); |
||||
} |
||||
}, |
||||
}, |
||||
dataZoom: [ |
||||
{ |
||||
show: true, |
||||
// height: 15, |
||||
xAxisIndex: [0], |
||||
bottom: "8%", |
||||
start: 0, |
||||
end: 100, |
||||
handleIcon: |
||||
"path://M306.1,413c0,2.2-1.8,4-4,4h-59.8c-2.2,0-4-1.8-4-4V200.8c0-2.2,1.8-4,4-4h59.8c2.2,0,4,1.8,4,4V413z", |
||||
handleSize: "110%", |
||||
handleStyle: { |
||||
color: "#d3dee5", |
||||
}, |
||||
textStyle: { |
||||
color: "#000", |
||||
}, |
||||
borderColor: "#90979c", |
||||
}, |
||||
{ |
||||
type: "inside", |
||||
show: true, |
||||
// height: 15, |
||||
start: 1, |
||||
end: 35, |
||||
}, |
||||
// { |
||||
// type: 'inside' |
||||
// }, |
||||
// { |
||||
// type: 'slider' |
||||
// } |
||||
], |
||||
series: series, |
||||
}; |
||||
|
||||
return option |
||||
// var app = { |
||||
// currentIndex: -1, |
||||
// }; |
||||
// setInterval(function () { |
||||
// var dataLen = option.series[0].data.length; |
||||
|
||||
// // 取消之前高亮的图形 |
||||
// myChart.dispatchAction({ |
||||
// type: "downplay", |
||||
// seriesIndex: 0, |
||||
// dataIndex: app.currentIndex, |
||||
// }); |
||||
// app.currentIndex = (app.currentIndex + 1) % dataLen; |
||||
// //console.log(app.currentIndex); |
||||
// // 高亮当前图形 |
||||
// myChart.dispatchAction({ |
||||
// type: "highlight", |
||||
// seriesIndex: 0, |
||||
// dataIndex: app.currentIndex, |
||||
// }); |
||||
// // 显示 tooltip |
||||
// myChart.dispatchAction({ |
||||
// type: "showTip", |
||||
// seriesIndex: 0, |
||||
// dataIndex: app.currentIndex, |
||||
// }); |
||||
// }, 1000); |
||||
|
||||
} |
||||
</script> |
||||
<style scoped> |
||||
.chart-container { |
||||
width: 100%; |
||||
height: 100%; |
||||
min-height: 300px; |
||||
position: relative; |
||||
} |
||||
</style> |
||||
@ -0,0 +1,369 @@
@@ -0,0 +1,369 @@
|
||||
<template> |
||||
<!-- <div > --> |
||||
<div ref="ChartsTimeBarRef" style="height:100%;width: 100%;" class="chart-container"></div> |
||||
<!-- </div> --> |
||||
</template> |
||||
<script setup> |
||||
import * as echarts from "echarts"; |
||||
import dayjs from 'dayjs' |
||||
import { onMounted, nextTick, ref, onUnmounted } from "vue"; |
||||
const props = defineProps({ |
||||
legendData: { |
||||
type: Array, |
||||
default: () => [] |
||||
}, |
||||
xAxisData: { |
||||
type: Array, |
||||
default: () => [] |
||||
}, |
||||
seriesData: { |
||||
type: Array, |
||||
default: () => [] |
||||
}, |
||||
textTitle: { |
||||
type: String, |
||||
default: '' |
||||
}, |
||||
unit: { |
||||
type: String, |
||||
default: 'mm' |
||||
}, |
||||
grid: { |
||||
type: Object, |
||||
default: () => { |
||||
return { |
||||
left: "5%", |
||||
right: "5%", |
||||
bottom: "16%", |
||||
top: "16%", |
||||
containLabel: true, |
||||
} |
||||
} |
||||
} |
||||
}) |
||||
const ChartsTimeBarRef = ref(null); |
||||
let echartsBar = null; |
||||
let resizeObserver = null; |
||||
// 监听props变化,确保数据更新后重新初始化图表 |
||||
watch([() => props.legendData, () => props.xAxisData, () => props.seriesData], () => { |
||||
if (echartsBar) { |
||||
updateChart(); |
||||
} |
||||
}, { deep: true }); |
||||
onMounted(() => { |
||||
console.log(props.xAxisData, '======xAxisData') |
||||
initEcharts(); |
||||
window.addEventListener('resize', handleResize); |
||||
// 监听容器本身尺寸变化 |
||||
if (ChartsTimeBarRef.value) { |
||||
resizeObserver = new ResizeObserver(handleResize); |
||||
resizeObserver.observe(ChartsTimeBarRef.value); |
||||
} |
||||
}); |
||||
|
||||
onUnmounted(() => { |
||||
window.removeEventListener('resize', handleResize); |
||||
if (resizeObserver && ChartsTimeBarRef.value) { |
||||
resizeObserver.unobserve(ChartsTimeBarRef.value); |
||||
} |
||||
if (echartsBar) { |
||||
echartsBar.dispose(); |
||||
} |
||||
}); |
||||
const echartsLoading = ref(true); |
||||
const initEcharts = () => { |
||||
nextTick(() => { |
||||
if (ChartsTimeBarRef.value) { |
||||
echartsBar = echarts.init(ChartsTimeBarRef.value, "macarons"); |
||||
let option = getChartOption(); |
||||
echartsBar.setOption(option); |
||||
|
||||
// 确保图表尺寸适应容器 |
||||
const resizeChart = () => { |
||||
if (echartsBar && ChartsTimeBarRef.value) { |
||||
echartsBar.resize({ |
||||
width: ChartsTimeBarRef.value.clientWidth, |
||||
height: ChartsTimeBarRef.value.clientHeight |
||||
}); |
||||
} |
||||
}; |
||||
|
||||
// 初始化时调整一次大小 |
||||
resizeChart(); |
||||
} |
||||
}); |
||||
}; |
||||
const updateChart = () => { |
||||
nextTick(() => { |
||||
if (echartsBar) { |
||||
let option = getChartOption(); |
||||
echartsBar.setOption(option, true); // 使用notMerge:true来完全替换旧选项 |
||||
resizeChart(); |
||||
} |
||||
}); |
||||
}; |
||||
|
||||
const resizeChart = () => { |
||||
if (echartsBar && ChartsTimeBarRef.value) { |
||||
echartsBar.resize({ |
||||
width: ChartsTimeBarRef.value.clientWidth, |
||||
height: ChartsTimeBarRef.value.clientHeight |
||||
}); |
||||
} |
||||
}; |
||||
const handleResize = () => { |
||||
if (echartsBar && ChartsTimeBarRef.value) { |
||||
// 添加防抖机制,避免频繁触发 |
||||
clearTimeout(window.resizeTimer); |
||||
window.resizeTimer = setTimeout(() => { |
||||
echartsBar.resize({ |
||||
width: ChartsTimeBarRef.value.clientWidth, |
||||
height: ChartsTimeBarRef.value.clientHeight, |
||||
animation: { |
||||
duration: 300 |
||||
} |
||||
}); |
||||
}, 100); |
||||
} |
||||
}; |
||||
|
||||
|
||||
const getChartOption = () => { |
||||
let allData = []; |
||||
props.seriesData.forEach(series => { |
||||
if (series.data) { |
||||
series.data.forEach(item => { |
||||
// 根据数据结构获取数值,可能是 [time, value] 或 {tm: time, value: value} 格式 |
||||
const value = Array.isArray(item) ? item[1] : (item.value !== undefined ? item.value : item); |
||||
if (value !== undefined && value !== null && !isNaN(value)) { |
||||
allData.push(Number(value)); |
||||
} |
||||
}); |
||||
} |
||||
}); |
||||
|
||||
// 计算y轴范围 |
||||
let yAxisMin = 0; |
||||
let yAxisMax = 100; |
||||
|
||||
if (allData.length > 0) { |
||||
const minVal = Math.min(...allData); |
||||
const maxVal = Math.max(...allData); |
||||
|
||||
// 添加一些边距,使图表更美观 |
||||
const range = maxVal - minVal; |
||||
const margin = range > 0 ? range * 0.1 : 1; |
||||
|
||||
yAxisMin = Math.floor((minVal - margin) * 100) / 100; // 保留两位小数 |
||||
yAxisMax = Math.ceil((maxVal + margin) * 100) / 100; // 保留两位小数 |
||||
|
||||
// 如果数据值都很小,确保最小值不为负数(可根据实际需求调整) |
||||
if (minVal >= 0 && yAxisMin < 0) { |
||||
yAxisMin = 0; |
||||
} |
||||
} |
||||
const series = props.seriesData.map((item, index) => { |
||||
const colors = [ |
||||
["#fccb05", "#f5804d"], |
||||
["#8bd46e", "#09bcb7"], |
||||
["#248ff7", "#6851f1"] |
||||
]; |
||||
const color = colors[index % colors.length]; |
||||
|
||||
return { |
||||
name: props.legendData[index], |
||||
type: "line", |
||||
barWidth: "20", |
||||
showSymbol: false, |
||||
itemStyle: { |
||||
normal: { |
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ |
||||
{ offset: 0, color: color[0] }, |
||||
{ offset: 1, color: color[1] } |
||||
]), |
||||
barBorderRadius: 12, |
||||
}, |
||||
}, |
||||
data: item.data.map(val => val[1]), |
||||
}; |
||||
}); |
||||
let option = { |
||||
// backgroundColor: "#323a5e", |
||||
title: { |
||||
text: props.textTitle, |
||||
textStyle: { |
||||
align: "center", |
||||
color: "#000", |
||||
fontSize: 20, |
||||
}, |
||||
top: "3%", |
||||
left: "50%", |
||||
}, |
||||
tooltip: { |
||||
trigger: "axis", |
||||
axisPointer: { |
||||
// 坐标轴指示器,坐标轴触发有效 |
||||
type: "shadow", // 默认为直线,可选为:'line' | 'shadow' |
||||
}, |
||||
}, |
||||
grid: props.grid, |
||||
legend: { |
||||
data: props.legendData, |
||||
right: 10, |
||||
top: 12, |
||||
textStyle: { |
||||
color: "#000", |
||||
}, |
||||
itemWidth: 12, |
||||
itemHeight: 10, |
||||
// itemGap: 35 |
||||
}, |
||||
xAxis: { |
||||
type: "category", |
||||
data: props.xAxisData, |
||||
axisLine: { |
||||
lineStyle: { |
||||
color: "#ccc", |
||||
}, |
||||
}, |
||||
|
||||
axisLabel: { |
||||
textStyle: { |
||||
fontFamily: "Microsoft YaHei", |
||||
}, |
||||
showMinLabel: true, // 强制显示最小值标签 |
||||
showMaxLabel: true, // 强制显示最大值标签 |
||||
formatter: function (value) { |
||||
let date = dayjs(value) |
||||
let yearMonthDay = date.format('YYYY-MM-DD') |
||||
let hourMinute = date.format('HH:mm') |
||||
let result = `{a|${hourMinute}}\n{b|${yearMonthDay}}`; |
||||
if (props.timeType == 'day') { |
||||
result = `{b|${yearMonthDay}}`; |
||||
} |
||||
return result |
||||
}, |
||||
rich: { |
||||
a: { |
||||
fontSize: 12, |
||||
color: '#000', |
||||
padding: [0, 0, 5, 0] // 上右下左的内边距,这里给上方小时分钟添加一点底部间距 |
||||
}, |
||||
b: { |
||||
fontSize: 12, |
||||
color: '#000' |
||||
} |
||||
} |
||||
}, |
||||
}, |
||||
|
||||
yAxis: { |
||||
type: "value", |
||||
min: yAxisMin, |
||||
max: yAxisMax, |
||||
|
||||
name: 'mm', |
||||
nameLocation: 'middle', |
||||
|
||||
nameTextStyle: {//y轴上方单位的颜色 |
||||
color: '#464b50' |
||||
}, |
||||
nameGap: 50, |
||||
axisLine: { |
||||
show: true, |
||||
lineStyle: { |
||||
color: "#ccc", |
||||
}, |
||||
}, |
||||
|
||||
splitLine: { |
||||
show: true, |
||||
lineStyle: { |
||||
color: "#ccc", |
||||
}, |
||||
}, |
||||
axisLabel: { |
||||
textStyle: { |
||||
color: "#000000", |
||||
}, |
||||
// 添加formatter属性,保留两位小数 |
||||
formatter: function (value) { |
||||
return value.toFixed(2); |
||||
} |
||||
}, |
||||
}, |
||||
dataZoom: [ |
||||
{ |
||||
show: true, |
||||
// height: 15, |
||||
xAxisIndex: [0], |
||||
bottom: "8%", |
||||
start: 0, |
||||
end: 100, |
||||
handleIcon: |
||||
"path://M306.1,413c0,2.2-1.8,4-4,4h-59.8c-2.2,0-4-1.8-4-4V200.8c0-2.2,1.8-4,4-4h59.8c2.2,0,4,1.8,4,4V413z", |
||||
handleSize: "110%", |
||||
handleStyle: { |
||||
color: "#d3dee5", |
||||
}, |
||||
textStyle: { |
||||
color: "#000", |
||||
}, |
||||
borderColor: "#90979c", |
||||
}, |
||||
{ |
||||
type: "inside", |
||||
show: true, |
||||
// height: 15, |
||||
start: 1, |
||||
end: 35, |
||||
}, |
||||
// { |
||||
// type: 'inside' |
||||
// }, |
||||
// { |
||||
// type: 'slider' |
||||
// } |
||||
], |
||||
series: series, |
||||
}; |
||||
|
||||
return option |
||||
// var app = { |
||||
// currentIndex: -1, |
||||
// }; |
||||
// setInterval(function () { |
||||
// var dataLen = option.series[0].data.length; |
||||
|
||||
// // 取消之前高亮的图形 |
||||
// myChart.dispatchAction({ |
||||
// type: "downplay", |
||||
// seriesIndex: 0, |
||||
// dataIndex: app.currentIndex, |
||||
// }); |
||||
// app.currentIndex = (app.currentIndex + 1) % dataLen; |
||||
// //console.log(app.currentIndex); |
||||
// // 高亮当前图形 |
||||
// myChart.dispatchAction({ |
||||
// type: "highlight", |
||||
// seriesIndex: 0, |
||||
// dataIndex: app.currentIndex, |
||||
// }); |
||||
// // 显示 tooltip |
||||
// myChart.dispatchAction({ |
||||
// type: "showTip", |
||||
// seriesIndex: 0, |
||||
// dataIndex: app.currentIndex, |
||||
// }); |
||||
// }, 1000); |
||||
|
||||
} |
||||
</script> |
||||
<style scoped> |
||||
.chart-container { |
||||
width: 100%; |
||||
height: 100%; |
||||
min-height: 300px; |
||||
position: relative; |
||||
} |
||||
</style> |
||||
@ -0,0 +1,81 @@
@@ -0,0 +1,81 @@
|
||||
<!-- |
||||
* @Author: waibao2 1@qq.com |
||||
* @Date: 2025-10-13 14:12:37 |
||||
* @LastEditors: waibao2 1@qq.com |
||||
* @LastEditTime: 2025-10-23 11:27:53 |
||||
* @FilePath: \qingtian-report-ui\src\components\ESelectSingle\index.vue |
||||
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE |
||||
--> |
||||
<template> |
||||
<div class="cascader-container"> |
||||
<el-select-v2 filterable v-model="stnmId" :options="options" :props="defaultProps" placeholder="请选择" @change="handleChange" style="width: 240px" /> |
||||
</div> |
||||
</template> |
||||
|
||||
<script setup> |
||||
import { ref } from 'vue' |
||||
const props = defineProps({ |
||||
stationType: { |
||||
type: String, |
||||
default: 'A' |
||||
}, |
||||
requestPrefix: { |
||||
type: String, |
||||
default: '/basic/station/queryStnmandStnmId' |
||||
}, |
||||
defaultProps: { |
||||
type: Object, |
||||
default: () => { |
||||
return { |
||||
label: 'stnm', |
||||
value: 'stnmId', |
||||
} |
||||
} |
||||
}, |
||||
type: { |
||||
type: String, |
||||
default: 'radio' |
||||
} |
||||
|
||||
}) |
||||
// 定义 emit 事件 |
||||
const emit = defineEmits(['stationChange', 'loadingChange']) |
||||
|
||||
const { proxy } = getCurrentInstance() |
||||
|
||||
const stnmId = ref('') |
||||
|
||||
const handleChange = (val) => { |
||||
stnmId.value = val |
||||
emit('stationChange', val, options.value, stnm.value) |
||||
emit('loadingChange', false); |
||||
} |
||||
|
||||
const options = ref([]) |
||||
const stnm = ref('') |
||||
const getSingleStation = async () => { |
||||
emit('loadingChange', true); |
||||
try { |
||||
let res = await proxy.axiosGet(props.requestPrefix, { 'type': proxy.stationType, isState: '0' }); |
||||
|
||||
if (props.type == 'radio') { |
||||
options.value = res.data; |
||||
} else { |
||||
options.value = res.data[0].children; |
||||
} |
||||
await nextTick() |
||||
stnmId.value = options.value[0].stnmId |
||||
stnm.value = options.value[0].stnm |
||||
|
||||
emit('stationChange', stnmId.value, options.value, stnm.value) |
||||
|
||||
} catch (error) { |
||||
|
||||
} finally { |
||||
emit('loadingChange', false); |
||||
} |
||||
} |
||||
onMounted(() => { |
||||
getSingleStation() |
||||
}) |
||||
</script> |
||||
@ -0,0 +1,138 @@
@@ -0,0 +1,138 @@
|
||||
|
||||
<template> |
||||
<el-radio-group class="pad10" v-model="dataType" @change="handleChange" v-if="showRadioGroup"> |
||||
<el-radio v-for="item in radioGroup" :value="item.value">{{item.label}}</el-radio> |
||||
</el-radio-group> |
||||
<el-input v-model="filterText" clearable class="w-60 mb10" placeholder="请选择" @input="onQueryChanged" /> |
||||
<el-tree-v2 :height="700" v-load='treeLoading' ref="treeRef" style="max-width: 600px;" class="filter-tree flex-tree" show-checkbox :data="treeData" :props="defaultProps" node-key="id" :default-expanded-keys="defaultExpandedKeys" :default-checked-keys="defaultCheckedKeys " :filter-method="filterMethod" @node-click="handleNodeClick" @check-change="handleCheckChange" /> |
||||
</template> |
||||
|
||||
<script setup> |
||||
import { ref, reactive, watch, onMounted } from 'vue' |
||||
const { proxy } = getCurrentInstance() |
||||
const props = defineProps({ |
||||
stationType: { |
||||
type: String, |
||||
default: 'A' |
||||
}, |
||||
showRadioGroup: { |
||||
type: Boolean, |
||||
default: false |
||||
}, |
||||
radioGroup: { |
||||
type: Object, |
||||
default: () => ( |
||||
[ |
||||
{ |
||||
label: "区域", |
||||
value: 0 |
||||
}, |
||||
{ |
||||
label: "流域", |
||||
value: 1 |
||||
} |
||||
] |
||||
) |
||||
}, |
||||
|
||||
}) |
||||
|
||||
// 定义 emit 事件 |
||||
const emit = defineEmits(['stationChange', 'loadingChange']) |
||||
|
||||
// 添加缺失的响应式变量 |
||||
const dataType = ref(0) // 单选框绑定值 |
||||
let url = '/basic/stype/getTreeStationType/' // 请求URL |
||||
|
||||
const treeRef = ref() |
||||
const filterText = ref('') |
||||
|
||||
const onQueryChanged = () => { |
||||
|
||||
} |
||||
|
||||
|
||||
const filterMethod = (value, data) => { |
||||
if (!value) return true |
||||
// 根据 defaultProps.label 的配置来访问节点标签 |
||||
return data[defaultProps.label].includes(value) |
||||
} |
||||
// 监听 filterText 变化并触发过滤 |
||||
watch(filterText, (val) => { |
||||
if (treeRef.value) { |
||||
treeRef.value.filter(val) |
||||
} |
||||
}) |
||||
const defaultProps = { |
||||
children: 'children', |
||||
label: 'name', |
||||
} |
||||
// 改变单选框数据 |
||||
const handleChange = (val) => { |
||||
if (val == 0) { |
||||
url = '/basic/stype/getTreeAreaStationByType/'; |
||||
} else { |
||||
url = '/basic/stype/getTreeBasinStationByType/'; |
||||
} |
||||
getTreeStation(); |
||||
} |
||||
|
||||
// 添加节点点击事件处理函数 |
||||
const handleNodeClick = (data, node) => { |
||||
// 点击任意节点时,清空所有展开的节点 |
||||
if (treeRef.value) { |
||||
treeRef.value.setExpandedKeys([]) |
||||
} |
||||
} |
||||
// 新增处理复选框变化的函数 |
||||
const handleCheckChange = (data, checked, indeterminate) => { |
||||
// 获取当前所有选中的节点 |
||||
const checkedNodes = treeRef.value.getCheckedNodes(false, true); |
||||
// 提取选中节点的ID列表 |
||||
const checkedKeys = checkedNodes.map(node => node.id); |
||||
// 发送事件给父组件 |
||||
emit('stationChange', checkedKeys); |
||||
emit('loadingChange', true); |
||||
} |
||||
|
||||
// 改变单选框后请求接口 |
||||
let treeData = ref([]) |
||||
const defaultExpandedKeys = ref([]) |
||||
const defaultCheckedKeys = ref([]) |
||||
const treeLoading = ref(false) |
||||
const getTreeStation = async () => { |
||||
treeLoading.value = true; |
||||
emit('loadingChange', true); |
||||
try { |
||||
let res = await proxy.axiosGet(url + proxy.stationType); |
||||
if (res.code == 0) { |
||||
treeData.value = res.data |
||||
defaultCheckedKeys.value = res.defaultOption || res.checkedList |
||||
defaultExpandedKeys.value = res.exList |
||||
await nextTick() |
||||
if (treeRef.value) { |
||||
treeRef.value.setExpandedKeys(defaultExpandedKeys.value) |
||||
} |
||||
emit('stationChange', defaultCheckedKeys.value) |
||||
} |
||||
} catch (error) { |
||||
|
||||
} finally { |
||||
emit('loadingChange', false); |
||||
treeLoading.value = false; |
||||
} |
||||
} |
||||
onMounted(() => { |
||||
getTreeStation() |
||||
}) |
||||
|
||||
</script> |
||||
|
||||
<style scoped> |
||||
.demo-tabs>.el-tabs__content { |
||||
padding: 32px; |
||||
color: #6b778c; |
||||
font-size: 32px; |
||||
font-weight: 600; |
||||
} |
||||
</style> |
||||
@ -0,0 +1,54 @@
@@ -0,0 +1,54 @@
|
||||
<template> |
||||
<div class="cascader-container"> |
||||
<el-cascader v-model="defaultOption" :options="options" :props="cascaderProps" @change="handleChange" filterable class="w320" /> |
||||
</div> |
||||
</template> |
||||
|
||||
<script setup> |
||||
import { ref } from 'vue' |
||||
const props = defineProps({ |
||||
stationType: { |
||||
type: String, |
||||
default: 'A' |
||||
}, |
||||
|
||||
}) |
||||
// 定义 emit 事件 |
||||
const emit = defineEmits(['stationChange', 'loadingChange']) |
||||
|
||||
const { proxy } = getCurrentInstance() |
||||
const defaultOption = ref([]) |
||||
|
||||
const cascaderProps = { |
||||
expandTrigger: 'click', |
||||
} |
||||
|
||||
const handleChange = (val) => { |
||||
defaultOption.value = val |
||||
let stnmId = defaultOption.value[2] |
||||
emit('stationChange', stnmId) |
||||
emit('loadingChange', true); |
||||
} |
||||
|
||||
const options = ref([]) |
||||
const getSingleStation = async () => { |
||||
emit('loadingChange', true); |
||||
try { |
||||
let res = await proxy.axiosGet('/basic/stype/getTreeStation2New/' + proxy.stationType); |
||||
if (res.code === 0) { |
||||
options.value = res.data; |
||||
defaultOption.value = res.defaultOption; |
||||
await nextTick() |
||||
let stnmId = defaultOption.value[2] |
||||
emit('stationChange', stnmId) |
||||
} |
||||
} catch (error) { |
||||
|
||||
} finally { |
||||
emit('loadingChange', false); |
||||
} |
||||
} |
||||
onMounted(() => { |
||||
getSingleStation() |
||||
}) |
||||
</script> |
||||
@ -0,0 +1,156 @@
@@ -0,0 +1,156 @@
|
||||
<template> |
||||
<div class="el-tree-select"> |
||||
<el-select |
||||
style="width: 100%" |
||||
v-model="valueId" |
||||
ref="treeSelect" |
||||
:filterable="true" |
||||
:clearable="true" |
||||
@clear="clearHandle" |
||||
:filter-method="selectFilterData" |
||||
:placeholder="placeholder" |
||||
> |
||||
<el-option :value="valueId" :label="valueTitle"> |
||||
<el-tree |
||||
id="tree-option" |
||||
ref="selectTree" |
||||
:accordion="accordion" |
||||
:data="options" |
||||
:props="objMap" |
||||
:node-key="objMap.value" |
||||
:expand-on-click-node="false" |
||||
:default-expanded-keys="defaultExpandedKey" |
||||
:filter-node-method="filterNode" |
||||
@node-click="handleNodeClick" |
||||
></el-tree> |
||||
</el-option> |
||||
</el-select> |
||||
</div> |
||||
</template> |
||||
|
||||
<script setup> |
||||
|
||||
const { proxy } = getCurrentInstance(); |
||||
|
||||
const props = defineProps({ |
||||
/* 配置项 */ |
||||
objMap: { |
||||
type: Object, |
||||
default: () => { |
||||
return { |
||||
value: 'id', // ID字段名 |
||||
label: 'label', // 显示名称 |
||||
children: 'children' // 子级字段名 |
||||
} |
||||
} |
||||
}, |
||||
/* 自动收起 */ |
||||
accordion: { |
||||
type: Boolean, |
||||
default: () => { |
||||
return false |
||||
} |
||||
}, |
||||
/**当前双向数据绑定的值 */ |
||||
value: { |
||||
type: [String, Number], |
||||
default: '' |
||||
}, |
||||
/**当前的数据 */ |
||||
options: { |
||||
type: Array, |
||||
default: () => [] |
||||
}, |
||||
/**输入框内部的文字 */ |
||||
placeholder: { |
||||
type: String, |
||||
default: '' |
||||
} |
||||
}) |
||||
|
||||
const emit = defineEmits(['update:value']); |
||||
|
||||
const valueId = computed({ |
||||
get: () => props.value, |
||||
set: (val) => { |
||||
emit('update:value', val) |
||||
} |
||||
}); |
||||
const valueTitle = ref(''); |
||||
const defaultExpandedKey = ref([]); |
||||
|
||||
function initHandle() { |
||||
nextTick(() => { |
||||
const selectedValue = valueId.value; |
||||
if(selectedValue !== null && typeof (selectedValue) !== 'undefined') { |
||||
const node = proxy.$refs.selectTree.getNode(selectedValue) |
||||
if (node) { |
||||
valueTitle.value = node.data[props.objMap.label] |
||||
proxy.$refs.selectTree.setCurrentKey(selectedValue) // 设置默认选中 |
||||
defaultExpandedKey.value = [selectedValue] // 设置默认展开 |
||||
} |
||||
} else { |
||||
clearHandle() |
||||
} |
||||
}) |
||||
} |
||||
function handleNodeClick(node) { |
||||
valueTitle.value = node[props.objMap.label] |
||||
valueId.value = node[props.objMap.value]; |
||||
defaultExpandedKey.value = []; |
||||
proxy.$refs.treeSelect.blur() |
||||
selectFilterData('') |
||||
} |
||||
function selectFilterData(val) { |
||||
proxy.$refs.selectTree.filter(val) |
||||
} |
||||
function filterNode(value, data) { |
||||
if (!value) return true |
||||
return data[props.objMap['label']].indexOf(value) !== -1 |
||||
} |
||||
function clearHandle() { |
||||
valueTitle.value = '' |
||||
valueId.value = '' |
||||
defaultExpandedKey.value = []; |
||||
clearSelected() |
||||
} |
||||
function clearSelected() { |
||||
const allNode = document.querySelectorAll('#tree-option .el-tree-node') |
||||
allNode.forEach((element) => element.classList.remove('is-current')) |
||||
} |
||||
|
||||
onMounted(() => { |
||||
initHandle() |
||||
}) |
||||
|
||||
watch(valueId, () => { |
||||
initHandle(); |
||||
}) |
||||
</script> |
||||
|
||||
<style lang='scss' scoped> |
||||
@import "@/assets/styles/variables.module.scss"; |
||||
.el-scrollbar .el-scrollbar__view .el-select-dropdown__item { |
||||
padding: 0; |
||||
background-color: #fff; |
||||
height: auto; |
||||
} |
||||
|
||||
.el-select-dropdown__item.selected { |
||||
font-weight: normal; |
||||
} |
||||
|
||||
ul li .el-tree .el-tree-node__content { |
||||
height: auto; |
||||
padding: 0 20px; |
||||
box-sizing: border-box; |
||||
} |
||||
|
||||
:deep(.el-tree-node__content:hover), |
||||
:deep(.el-tree-node__content:active), |
||||
:deep(.is-current > div:first-child), |
||||
:deep(.el-tree-node__content:focus) { |
||||
background-color: mix(#fff, $--color-primary, 90%); |
||||
color: $--color-primary; |
||||
} |
||||
</style> |
||||
@ -0,0 +1,244 @@
@@ -0,0 +1,244 @@
|
||||
<template> |
||||
<div class="app-container app-container-bg"> |
||||
<el-card class="first-card" ref='firstCard' shadow="always"> |
||||
<el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" @submit.native.prevent> |
||||
<el-form-item label="测站名称" prop="stnm"> |
||||
<el-input v-model="queryParams.stnm" placeholder="请输入测站名称" clearable @keyup.enter.native="handleQuery" /> |
||||
</el-form-item> |
||||
<el-form-item label="开始时间"> |
||||
<el-date-picker v-model="queryParams.startTime" type="datetime" value-format="YYYY-MM-DD HH:mm:ss" format="YYYY-MM-DD HH:mm:ss" placeholder="选择开始时间" :disabled-date="disabledStartDate"> |
||||
</el-date-picker> |
||||
</el-form-item> |
||||
<el-form-item label="结束时间"> |
||||
<el-date-picker v-model="queryParams.endTime" type="datetime" value-format="YYYY-MM-DD HH:mm:ss" placeholder="选择结束时间" :disabled-date="disabledEndDate"> |
||||
</el-date-picker> |
||||
</el-form-item> |
||||
<el-form-item label="预警类型" prop="alarmType"> |
||||
<el-select v-model="queryParams.alarmType" placeholder="请选择预警类型" style="width:100%"> |
||||
<el-option v-for="dict in alarm_type" :key="dict.value" :label="dict.label" :value="dict.value"></el-option> |
||||
</el-select> |
||||
</el-form-item> |
||||
<el-form-item label="处理状态" prop="isDeal"> |
||||
<el-select v-model="queryParams.isDeal" placeholder="请选择处理状态" style="width:100%"> |
||||
<el-option v-for="dict in alarm_isDeals" :key="dict.value" :label="dict.label" :value="dict.value"></el-option> |
||||
</el-select> |
||||
</el-form-item> |
||||
<el-form-item label="遥测类型" prop="type"> |
||||
<el-select v-model="queryParams.type" placeholder="请选择遥测类型" style="width:100%"> |
||||
<el-option v-for="dict in alarm_data_type" :key="dict.value" :label="dict.label" :value="dict.value"></el-option> |
||||
</el-select> |
||||
</el-form-item> |
||||
<el-form-item> |
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button> |
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button> |
||||
</el-form-item> |
||||
</el-form> |
||||
<el-row :gutter="10" class="mb8"> |
||||
<!-- <el-col :span="1.5"> |
||||
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete" v-hasPermi="['message:value:remove']">删除</el-button> |
||||
</el-col> --> |
||||
<el-col :span="1.5"> |
||||
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['message:value:export']">导出</el-button> |
||||
</el-col> |
||||
|
||||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar> |
||||
</el-row> |
||||
</el-card> |
||||
<div class="el-card-p card-shadow carder-border mt10 pad10 "> |
||||
<el-table class="table-box" v-table-height v-loading="loading" :data="alarmList" @selection-change="handleSelectionChange"> |
||||
<el-table-column type="selection" width="55" :align="alignment" /> |
||||
<el-table-column type="index" width="55" :align="alignment" label="序号" /> |
||||
<el-table-column label="测站名称" :align="alignment" prop="stnm" sortable /> |
||||
<el-table-column label="遥测类型" :align="alignment" prop="type" /> |
||||
<el-table-column label="预警类型" :align="alignment" prop="alarmType" /> |
||||
<el-table-column label="预警内容" :align="alignment" prop="alarmContent" /> |
||||
<el-table-column label="预警时间" :align="alignment" prop="alarmTime" sortable /> |
||||
<el-table-column label="预警时长" :align="alignment" prop="alarmTotalLength" sortable /> |
||||
<el-table-column label="预警状态" :align="alignment" prop="flag"> |
||||
<template #default="scope"> |
||||
<span v-if="scope.row.flag == 0" class="red">预警中</span> |
||||
<span v-if="scope.row.flag == 1" class="green">预警结束</span> |
||||
</template> |
||||
</el-table-column> |
||||
<el-table-column label="短信" :align="alignment" prop="isSms"> |
||||
<template #default="scope"> |
||||
<span v-if="scope.row.isSms == 'n'" class="red">未生成</span> |
||||
<span v-if="scope.row.isSms == 'y'" class="green">已生成</span> |
||||
</template> |
||||
</el-table-column> |
||||
|
||||
</el-table> |
||||
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.page" v-model:limit="queryParams.limit" @pagination="getList" /> |
||||
</div> |
||||
|
||||
</div> |
||||
</template> |
||||
<script setup > |
||||
import { ref, reactive, onMounted, } from 'vue' |
||||
import dayjs from 'dayjs' |
||||
const { proxy } = getCurrentInstance() |
||||
const alignment = 'center' |
||||
const showSearch = ref(true) |
||||
const queryParams = reactive({ |
||||
page: 1, |
||||
limit: 10, |
||||
stnm: '', |
||||
isDeal: "", |
||||
type: "", |
||||
alarmType: "", |
||||
startTime: dayjs().subtract(1, 'day').format('YYYY-MM-DD HH:mm:ss'), |
||||
endTime: dayjs().format('YYYY-MM-DD HH:mm:ss') |
||||
}) |
||||
|
||||
const loading = ref(false) |
||||
const total = ref(0) |
||||
const alarmList = ref([]) |
||||
const getList = async () => { |
||||
loading.value = true; |
||||
try { |
||||
const res = await proxy.axiosGet('/alarm/alarm/list2', queryParams) |
||||
if (res.code == 0) { |
||||
alarmList.value = res.rows |
||||
total.value = res.count |
||||
} |
||||
} catch (error) { |
||||
|
||||
} finally { |
||||
loading.value = false |
||||
} |
||||
} |
||||
|
||||
/** 搜索按钮操作 */ |
||||
const handleQuery = () => { |
||||
queryParams.pageNum = 1 |
||||
getList() |
||||
} |
||||
const resetQuery = () => { |
||||
proxy.resetForm("queryForm"); |
||||
handleQuery(); |
||||
} |
||||
|
||||
// 多选框选中数据 |
||||
let ids = ref([]) |
||||
let names = ref([]) |
||||
let single = ref(true) |
||||
let multiple = ref(true) |
||||
const handleSelectionChange = (selection) => { |
||||
ids.value = selection.map(item => item.id) |
||||
names.value = selection.map(item => item.stnm) |
||||
single.value = selection.length !== 1 |
||||
multiple.value = !selection.length |
||||
} |
||||
|
||||
|
||||
/** 删除按钮操作 */ |
||||
const handleDelete = (row) => { |
||||
let stnms = row.stnm || names.value |
||||
const stnmIds = row.id || ids.value; |
||||
proxy.$modal.confirm('是否确认删除测站名称为"' + stnms + '"的数据项?').then(async function () { |
||||
let res = await proxy.axiosDelete('/alarm/alarm/delete/' + stnmIds); |
||||
if (res.code === 200) { |
||||
proxy.$modal.msgSuccess("删除成功"); |
||||
getList(); |
||||
} |
||||
}) |
||||
} |
||||
/** 导出按钮操作 */ |
||||
const handleExport = async () => { |
||||
let p = JSON.parse(JSON.stringify(queryParams)) |
||||
param.page = 1; |
||||
param.limit = 9999; |
||||
let res = await proxy.axiosGet('/alarm/alarm/list', p); |
||||
if (res.code === 0) { |
||||
let table = []; |
||||
table.push({ |
||||
A: "测站名称", |
||||
B: "数据类型", |
||||
C: "预警类型", |
||||
D: "预警级别", |
||||
E: "预警内容", |
||||
F: "预警时间", |
||||
G: "预警时长(时)", |
||||
H: "预警状态", |
||||
I: "短信是否处理", |
||||
}); |
||||
res.rows.forEach(d => { |
||||
let row = { |
||||
A: d.stnm, |
||||
B: d.type, |
||||
C: d.alarmType, |
||||
D: d.alarmLevel, |
||||
E: d.alarmContent, |
||||
F: d.alarmTime, |
||||
G: (d.alarmTotalLength / 60.0 / 60.0).toFixed(1), |
||||
H: d.flag === 0 ? "预警中" : "预警结束", |
||||
I: d.isDeal === 'n' ? "否" : "是", |
||||
}; |
||||
table.push(row); |
||||
}); |
||||
let header = ["A", "B", "C", "D", "E", "F", "G", "H", "I"]; |
||||
let fileName = "预警信息"; |
||||
proxy.exportExcel(header, table, fileName); |
||||
} |
||||
} |
||||
|
||||
onMounted(() => { |
||||
getList() |
||||
}) |
||||
const alarm_flags = ref([ |
||||
{ |
||||
label: "预警中", |
||||
value: 0 |
||||
}, |
||||
{ |
||||
label: "预警结束", |
||||
value: 1 |
||||
}, |
||||
]) |
||||
const alarm_isDeals = ref([ |
||||
{ |
||||
label: "是", |
||||
value: 'y' |
||||
}, |
||||
{ |
||||
label: "否", |
||||
value: 'n' |
||||
}, |
||||
]) |
||||
const alarm_data_type = ref([ |
||||
{ |
||||
value: 'A', |
||||
label: '雨量' |
||||
}, |
||||
{ |
||||
value: 'B', |
||||
label: '河道' |
||||
}, |
||||
{ |
||||
value: 'C', |
||||
label: '水库' |
||||
}, |
||||
{ |
||||
value: 'D', |
||||
label: '潮位' |
||||
}, |
||||
{ |
||||
value: 'E', |
||||
label: '流量' |
||||
}, |
||||
{ |
||||
value: 'V', |
||||
label: '电量' |
||||
} |
||||
]) |
||||
const alarm_type = ref([ |
||||
{ |
||||
label: "设备故障", |
||||
value: '设备故障' |
||||
}, |
||||
{ |
||||
label: "数据异常", |
||||
value: '数据异常' |
||||
}, |
||||
]) |
||||
</script> |
||||
@ -0,0 +1,259 @@
@@ -0,0 +1,259 @@
|
||||
/* |
||||
* @Description: 预警短信 |
||||
*/ |
||||
<template> |
||||
<div class="app-container app-container-bg"> |
||||
<el-card class="first-card" ref='firstCard' shadow="always"> |
||||
<el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" @submit.native.prevent> |
||||
<el-form-item label="手机号" prop="phones"> |
||||
<el-input v-model="queryParams.phones" placeholder="请输入手机号" clearable @keyup.enter.native="handleQuery" /> |
||||
</el-form-item> |
||||
<el-form-item label="开始时间"> |
||||
<el-date-picker v-model="queryParams.startTime" type="datetime" value-format="yyyy-MM-dd HH:mm:ss" placeholder="选择开始时间"> |
||||
</el-date-picker> |
||||
</el-form-item> |
||||
<el-form-item label="结束时间"> |
||||
<el-date-picker v-model="queryParams.endTime" type="datetime" value-format="yyyy-MM-dd HH:mm:ss" placeholder="选择结束时间"> |
||||
</el-date-picker> |
||||
</el-form-item> |
||||
<el-form-item> |
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button> |
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button> |
||||
</el-form-item> |
||||
</el-form> |
||||
|
||||
<el-row :gutter="10" class="mb8"> |
||||
<el-col :span="1.5"> |
||||
<el-button type="danger" plain icon="el-icon-delete" :disabled="multiple" @click="handleDelete" v-hasPermi="['alarm:sms:remove']">删除</el-button> |
||||
</el-col> |
||||
<el-col :span="1.5"> |
||||
<el-button type="warning" plain icon="el-icon-download" @click="handleExport" v-hasPermi="['alarm:sms:export']">导出</el-button> |
||||
</el-col> |
||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar> |
||||
</el-row> |
||||
</el-card> |
||||
<div class="el-card-p card-shadow carder-border mt10 pad10"> |
||||
<el-table v-loading="loading" :data="smsList" @selection-change="handleSelectionChange"> |
||||
<el-table-column type="selection" width="55" :align="alignment" /> |
||||
<el-table-column type="index" width="55" :align="alignment" label="序号"></el-table-column> |
||||
<el-table-column label="短信推送人员" :align="alignment" prop="employees" width="120" /> |
||||
<el-table-column label="人员手机号" :align="alignment" prop="phones" width="120" /> |
||||
<el-table-column label="告警内容" prop="smsContent" /> |
||||
<el-table-column label="推送时间" :align="alignment" prop="createTime" width="180" /> |
||||
<el-table-column label="发送类型" :align="alignment" prop="sendType" width="120"> |
||||
<template #default="scope"> |
||||
<span v-if="scope.row.sendType == 0 ">自动</span> |
||||
<span v-if="scope.row.sendType == 1 ">手动</span> |
||||
</template> |
||||
</el-table-column> |
||||
<el-table-column label="操作" :align="alignment" class-name="small-padding fixed-width" width="120"> |
||||
<template #default="scope"> |
||||
<el-button text type="danger" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['alarm:sms:remove']">删除</el-button> |
||||
</template> |
||||
</el-table-column> |
||||
</el-table> |
||||
|
||||
<pagination v-show="total>0" :total="total" :page.sync="queryParams.page" :limit.sync="queryParams.limit" @pagination="getList" /> |
||||
</div> |
||||
<!-- 添加或修改短信预警信息对话框 --> |
||||
<el-dialog class="custom-dialog" :title="title" v-model="open" width="500px" append-to-body> |
||||
<el-form ref="formRef" :model="form" :rules="rules" label-width="auto"> |
||||
<el-form-item label="预警信息" prop="alarmInfoId"> |
||||
<el-input v-model="form.alarmInfoId" placeholder="请输入预警信息" /> |
||||
</el-form-item> |
||||
<el-form-item label="短信推送人员" prop="employees"> |
||||
<el-input v-model="form.employees" type="textarea" placeholder="请输入内容" /> |
||||
</el-form-item> |
||||
<el-form-item label="手机号" prop="phones"> |
||||
<el-input v-model="form.phones" type="textarea" placeholder="请输入内容" /> |
||||
</el-form-item> |
||||
<el-form-item label="预警短信内容"> |
||||
<editor v-model="form.smsContent" :min-height="192" /> |
||||
</el-form-item> |
||||
<el-form-item label="是否推送" prop="isSend"> |
||||
<el-input v-model="form.isSend" placeholder="请输入是否推送,n 未推送,y 已推送" /> |
||||
</el-form-item> |
||||
<el-form-item label="短信发送时间" prop="sendTime"> |
||||
<el-date-picker clearable v-model="form.sendTime" type="date" value-format="yyyy-MM-dd" placeholder="请选择短信发送时间"> |
||||
</el-date-picker> |
||||
</el-form-item> |
||||
</el-form> |
||||
<template #footer> |
||||
<div class="dialog-footer"> |
||||
<el-button type="primary" @click="submitForm(formRef)" v-loading='btnLoading'>确 定</el-button> |
||||
<el-button @click="cancel">取 消</el-button> |
||||
</div> |
||||
</template> |
||||
</el-dialog> |
||||
</div> |
||||
</template> |
||||
<script setup > |
||||
import dayjs from 'dayjs' |
||||
import { ref, reactive, onMounted } from 'vue' |
||||
const { proxy } = getCurrentInstance() |
||||
const alignment = 'center' |
||||
const loading = ref(false) |
||||
const ids = ref([]) |
||||
const names = ref([]) |
||||
const single = ref(true) |
||||
const multiple = ref(true) |
||||
const showSearch = ref(true) |
||||
const total = ref(0) |
||||
const smsList = ref([]) |
||||
const title = ref('') |
||||
const open = ref(false) |
||||
const queryParams = reactive({ |
||||
page: 1, |
||||
limit: 10, |
||||
alarmInfoId: null, |
||||
employees: null, |
||||
phones: null, |
||||
smsContent: null, |
||||
isSend: null, |
||||
sendType: null, |
||||
sendTime: null, |
||||
stnm: null, |
||||
startTime: dayjs().subtract(1, 'day').format('YYYY-MM-DD HH:mm:ss'), |
||||
endTime: dayjs().format('YYYY-MM-DD HH:mm:ss'), |
||||
}) |
||||
const form = reactive({}) |
||||
const rules = ref({}) |
||||
async function getList() { |
||||
loading.value = true; |
||||
try { |
||||
let res = await proxy.axiosGet('/alarm/sms/list', queryParams); |
||||
if (res.code === 0) { |
||||
smsList.value = res.data; |
||||
total.value = res.count; |
||||
} |
||||
} catch (error) { |
||||
|
||||
} finally { |
||||
loading.value = false; |
||||
} |
||||
} |
||||
/** 搜索按钮操作 */ |
||||
const handleQuery = () => { |
||||
queryParams.page = 1; |
||||
getList(); |
||||
} |
||||
/** 重置按钮操作 */ |
||||
const resetQuery = () => { |
||||
proxy.resetForm("queryForm"); |
||||
handleQuery(); |
||||
} |
||||
// 多选框选中数据 |
||||
const handleSelectionChange = (selection) => { |
||||
ids.value = selection.map(item => item.id) |
||||
names.value = selection.map(item => item.name) |
||||
single.value = selection.length !== 1 |
||||
multiple.value = !selection.length |
||||
} |
||||
/** 新增按钮操作 */ |
||||
const handleAdd = () => { |
||||
reset(); |
||||
open.value = true; |
||||
title.value = "添加短信预警信息"; |
||||
} |
||||
/** 修改按钮操作 */ |
||||
const handleUpdate = async (row) => { |
||||
reset(); |
||||
const id = row.id || ids.value |
||||
let res = await proxy.axiosGet('/alarm/sms/info/' + id); |
||||
if (res.code === 0) { |
||||
Object.assign(form, res.data); |
||||
open.value = true; |
||||
title.value = "修改短信预警信息"; |
||||
} |
||||
} |
||||
const addMethod = async () => { |
||||
let res = await proxy.axiosPost('/alarm/sms/add', form); |
||||
if (res.code === 0) { |
||||
proxy.$modal.msgSuccess("新增成功"); |
||||
open.value = false; |
||||
getList(); |
||||
} |
||||
} |
||||
const editMethod = async () => { |
||||
let res = await proxy.axiosPost('/alarm/sms/edit', form); |
||||
if (res.code === 0) { |
||||
proxy.$modal.msgSuccess("修改成功"); |
||||
open.value = false; |
||||
getList(); |
||||
} |
||||
} |
||||
let formRef = ref(null) |
||||
/** 提交按钮 */ |
||||
const submitForm = async (formEl) => { |
||||
if (!formEl) return |
||||
await formEl.validate((valid, fields) => { |
||||
if (valid) { |
||||
if (form.id != null) { |
||||
editMethod(); |
||||
} else { |
||||
addMethod(); |
||||
} |
||||
} |
||||
}) |
||||
} |
||||
|
||||
/** 删除按钮操作 */ |
||||
const handleDelete = async (row) => { |
||||
let stnms = row.name || names.value |
||||
const stnmIds = row.id || ids.value; |
||||
proxy.$modal.confirm('是否确认删除站点名称为"' + stnms + '"的数据项?').then(async function () { |
||||
let res = await proxy.axiosDelete('/alarm/sms/delete/' + stnmIds); |
||||
if (res.code === 0) { |
||||
proxy.$modal.msgSuccess("删除成功"); |
||||
getList(); |
||||
} |
||||
}) |
||||
} |
||||
|
||||
const handleExport = async () => { |
||||
let param = queryParams; |
||||
param.page = 1; |
||||
param.limit = 9999; |
||||
let res = await proxy.axiosGet('/alarm/sms/list', param); |
||||
if (res.code === 0) { |
||||
let table = []; |
||||
table.push({ |
||||
A: "短信推送人员", |
||||
B: "人员手机号", |
||||
C: "短信内容", |
||||
D: "短信生成时间", |
||||
E: "是否推送", |
||||
F: "发送类型", |
||||
G: "短信发送时间", |
||||
}); |
||||
res.data.forEach(d => { |
||||
var row = { |
||||
A: d.employees, |
||||
B: d.phones, |
||||
C: d.smsContent, |
||||
D: d.createTime, |
||||
E: d.isSend === 'n' ? "未推送" : "已推送", |
||||
F: d.sendType === 0 ? "自动" : "手动", |
||||
G: d.sendTime, |
||||
}; |
||||
table.push(row); |
||||
}); |
||||
var header = ["A", "B", "C", "D", "E", "F", "G"]; |
||||
var fileName = "预警短信"; |
||||
proxy.exportExcel(header, table, fileName); |
||||
} |
||||
} |
||||
const cancel = () => { |
||||
open.value = false; |
||||
reset(); |
||||
} |
||||
const reset = () => { |
||||
Object.assign(form, {}); |
||||
proxy.resetForm("formRef"); |
||||
} |
||||
onMounted(() => { |
||||
getList() |
||||
|
||||
}) |
||||
|
||||
</script> |
||||
@ -0,0 +1,297 @@
@@ -0,0 +1,297 @@
|
||||
/* |
||||
* @Description: 预警信息 |
||||
*/ |
||||
<template> |
||||
<div class="app-container app-container-bg"> |
||||
<el-card class="first-card" ref='firstCard' shadow="always"> |
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" @submit.native.prevent> |
||||
<el-form-item label="测站名称" prop="stnm"> |
||||
<el-input v-model="queryParams.stnm" placeholder="请输入测站名称" clearable @keyup.enter.native="handleQuery" /> |
||||
</el-form-item> |
||||
<el-form-item label="开始时间"> |
||||
<el-date-picker v-model="queryParams.startTime" type="datetime" value-format="yyyy-MM-dd HH:mm:ss" placeholder="选择开始时间" class="w200"> |
||||
</el-date-picker> |
||||
</el-form-item> |
||||
<el-form-item label="结束时间"> |
||||
<el-date-picker v-model="queryParams.endTime" type="datetime" value-format="yyyy-MM-dd HH:mm:ss" placeholder="选择结束时间" class="w200"> |
||||
</el-date-picker> |
||||
</el-form-item> |
||||
|
||||
<el-form-item label="预警状态"> |
||||
<el-select clearable v-model="queryParams.flag" placeholder="请选择" @change="handleQuery"> |
||||
<el-option v-for="item in flagS" :key="item.value" :label="item.name" :value="item.value"> |
||||
</el-option> |
||||
</el-select> |
||||
</el-form-item> |
||||
<el-form-item label="处理状态"> |
||||
<el-select clearable v-model="queryParams.isDeal" placeholder="请选择" @change="handleQuery"> |
||||
<el-option v-for="item in isDeals" :key="item.value" :label="item.name" :value="item.value"> |
||||
</el-option> |
||||
</el-select> |
||||
</el-form-item> |
||||
<el-form-item label="数据类型" prop="type"> |
||||
<el-select clearable v-model="queryParams.type" placeholder="请选择数据类型" @change="handleQuery"> |
||||
<el-option v-for="d in sTypes" :key="d.key" :label="d.label" :value="d.key"></el-option> |
||||
</el-select> |
||||
</el-form-item> |
||||
|
||||
<el-form-item label="预警类型" prop="alarmType"> |
||||
<el-select clearable v-model="queryParams.alarmType" placeholder="请选择预警类型" @change="handleQuery"> |
||||
<el-option v-for="d in alarmTypes" :key="d.key" :label="d.label" :value="d.key"></el-option> |
||||
</el-select> |
||||
</el-form-item> |
||||
|
||||
<el-form-item> |
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button> |
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button> |
||||
</el-form-item> |
||||
</el-form> |
||||
|
||||
<el-row :gutter="10" class="mb8"> |
||||
<el-col :span="1.5"> |
||||
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete" v-hasPermi="['alarm:alarm:remove']">删除</el-button> |
||||
</el-col> |
||||
<el-col :span="1.5"> |
||||
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['alarm:alarm:export']">导出</el-button> |
||||
</el-col> |
||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar> |
||||
</el-row> |
||||
</el-card> |
||||
<div class="el-card-p card-shadow carder-border mt10 pad10"> |
||||
<el-table v-loading="loading" :data="alarmList" @selection-change="handleSelectionChange"> |
||||
<el-table-column type="selection" width="55" align="center" /> |
||||
<el-table-column type="index" width="60" align="center" label="序号"></el-table-column> |
||||
<el-table-column label="测站名称" align="center" prop="stnm" width="150" sortable /> |
||||
<el-table-column label="遥测类型" align="center" prop="type" width="80" /> |
||||
<el-table-column label="预警类型" align="center" prop="alarmType" width="80" /> |
||||
<el-table-column label="预警内容" align="center" prop="alarmContent" /> |
||||
<el-table-column label="预警时间" align="center" prop="alarmTime" width="180" sortable /> |
||||
<el-table-column label="预警时长" align="center" prop="alarmTotalLength" width="130" sortable /> |
||||
<el-table-column label="预警状态" align="center" prop="flag" width="80"> |
||||
|
||||
<template #default="scope"> |
||||
<span v-if="scope.row.flag == 0 " class="red">预警中</span> |
||||
<span v-if="scope.row.flag == 1 " class="green">预警结束</span> |
||||
</template> |
||||
</el-table-column> |
||||
<el-table-column label="短信是否生成" align="center" prop="isSms" width="80"> |
||||
<template #default="scope"> |
||||
<span v-if="scope.row.isSms == 'n' " class="red">否</span> |
||||
<span v-if="scope.row.isSms == 'y' " class="green">是</span> |
||||
</template> |
||||
</el-table-column> |
||||
|
||||
</el-table> |
||||
|
||||
<pagination v-show="total>0" :total="total" :page.sync="queryParams.page" :limit.sync="queryParams.limit" @pagination="getList" /> |
||||
</div> |
||||
<!-- 添加或修改预警信息对话框 --> |
||||
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body> |
||||
|
||||
</el-dialog> |
||||
</div> |
||||
</template> |
||||
<script setup> |
||||
import dayjs from 'dayjs' |
||||
import { ref, reactive, onMounted } from "vue"; |
||||
const { proxy } = getCurrentInstance() |
||||
const sTypes = ref([ |
||||
{ |
||||
key: 'A', |
||||
label: '雨量' |
||||
}, |
||||
{ |
||||
key: 'B', |
||||
label: '河道' |
||||
}, |
||||
{ |
||||
key: 'C', |
||||
label: '水库' |
||||
}, |
||||
{ |
||||
key: 'D', |
||||
label: '潮位' |
||||
}, |
||||
{ |
||||
key: 'E', |
||||
label: '流量' |
||||
}, |
||||
{ |
||||
key: 'V', |
||||
label: '电量' |
||||
} |
||||
]) |
||||
const alarmTypes = ref([ |
||||
{ |
||||
key: '设备故障', |
||||
label: '设备故障' |
||||
}, |
||||
{ |
||||
key: '数据异常', |
||||
label: '数据异常' |
||||
} |
||||
]) |
||||
const loading = ref(false) |
||||
const ids = ref([]) |
||||
const single = ref(true) |
||||
const multiple = ref(true) |
||||
const showSearch = ref(true) |
||||
const total = ref(0) |
||||
const alarmList = ref([]) |
||||
const title = ref('') |
||||
const open = ref(false) |
||||
const queryParams = reactive({ |
||||
page: 1, |
||||
limit: 10, |
||||
stnmId: null, |
||||
stnm: null, |
||||
stcd: null, |
||||
type: null, |
||||
alarmType: null, |
||||
alarmLevel: null, |
||||
alarmContent: null, |
||||
alarmItem: null, |
||||
alarmTime: null, |
||||
alarmTotalLength: null, |
||||
flag: null, |
||||
isDeal: null, |
||||
isSms: null, |
||||
deleted: null, |
||||
startTime: dayjs().subtract(1, 'day').format('YYYY-MM-DD HH:mm:ss'), |
||||
endTime: dayjs().format('YYYY-MM-DD HH:mm:ss'), |
||||
|
||||
}) |
||||
const form = reactive({}) |
||||
const rules = ref({}) |
||||
const flagS = ref([ |
||||
{ |
||||
name: '预警中', |
||||
value: 0 |
||||
}, |
||||
{ |
||||
name: '预警结束', |
||||
value: 1 |
||||
} |
||||
]) |
||||
const isDeals = ref([{ |
||||
name: '是', |
||||
value: 'y' |
||||
}, |
||||
{ |
||||
name: '否', |
||||
value: 'n' |
||||
}]) |
||||
onMounted(() => { |
||||
getList() |
||||
}) |
||||
const getList = async () => { |
||||
loading.value = true; |
||||
let res = await proxy.axiosGet('/alarm/alarm/list', queryParams); |
||||
if (res.code === 0) { |
||||
alarmList.value = res.data; |
||||
total.value = res.count; |
||||
for (var i = 0; i < alarmList.value.length; i++) { |
||||
alarmList.value[i].alarmTotalLength = convertSeconds(alarmList.value[i].alarmTotalLength); |
||||
} |
||||
} |
||||
loading.value = false; |
||||
} |
||||
const convertSeconds = (seconds) => { |
||||
const days = Math.floor(seconds / 86400); // 1天=86400秒 |
||||
let remaining = seconds % 86400; |
||||
|
||||
const hours = Math.floor(remaining / 3600); // 1小时=3600秒 |
||||
remaining = remaining % 3600; |
||||
|
||||
const minutes = Math.floor(remaining / 60); // 1分钟=60秒 |
||||
|
||||
// 拼接非零单位 |
||||
const result = []; |
||||
if (days > 0) result.push(`${days}日`); |
||||
if (hours > 0) result.push(`${hours}时`); |
||||
if (minutes > 0) result.push(`${minutes}分`); |
||||
|
||||
// 处理全零情况(如输入0秒) |
||||
return result.length ? result.join('') : '0分'; |
||||
} |
||||
const cancel = () => { |
||||
open.value = false; |
||||
reset(); |
||||
} |
||||
const reset = () => { |
||||
Object.assign(form, {}); |
||||
proxy.resetForm("formRef"); |
||||
} |
||||
/** 搜索按钮操作 */ |
||||
const handleQuery = () => { |
||||
queryParams.page = 1; |
||||
getList(); |
||||
} |
||||
/** 重置按钮操作 */ |
||||
const resetQuery = () => { |
||||
proxy.resetForm("queryForm"); |
||||
handleQuery(); |
||||
} |
||||
// 多选框选中数据 |
||||
const handleSelectionChange = (selection) => { |
||||
ids.value = selection.map(item => item.id) |
||||
names.value = selection.map(item => item.stnm) |
||||
single.value = selection.length !== 1 |
||||
multiple.value = !selection.length |
||||
} |
||||
/** 新增按钮操作 */ |
||||
const handleAdd = () => { |
||||
reset(); |
||||
open.value = true; |
||||
title.value = "添加预警信息"; |
||||
} |
||||
/** 删除按钮操作 */ |
||||
const handleDelete = async (row) => { |
||||
let stnms = row.name || names.value |
||||
const stnmIds = row.id || ids.value; |
||||
proxy.$modal.confirm('是否确认删除站点名称为"' + stnms + '"的数据项?').then(async function () { |
||||
let res = await proxy.axiosDelete('/alarm/alarm/delete/' + stnmIds); |
||||
if (res.code === 0) { |
||||
proxy.$modal.msgSuccess("删除成功"); |
||||
getList(); |
||||
} |
||||
}) |
||||
} |
||||
const handleExport = async () => { |
||||
let param = queryParams; |
||||
param.page = 1; |
||||
param.limit = 9999; |
||||
let res = await proxy.axiosGet('/alarm/sms/list', param); |
||||
if (res.code === 0) { |
||||
let table = []; |
||||
table.push({ |
||||
A: "测站名称", |
||||
B: "数据类型", |
||||
C: "预警类型", |
||||
D: "预警级别", |
||||
E: "预警内容", |
||||
F: "预警时间", |
||||
G: "预警时长(时)", |
||||
H: "预警状态", |
||||
I: "短信是否处理", |
||||
}); |
||||
res.data.forEach(d => { |
||||
var row = { |
||||
A: d.stnm, |
||||
B: d.type, |
||||
C: d.alarmType, |
||||
D: d.alarmLevel, |
||||
E: d.alarmContent, |
||||
F: d.alarmTime, |
||||
G: (d.alarmTotalLength / 60.0 / 60.0).toFixed(1), |
||||
H: d.flag === 0 ? "预警中" : "预警结束", |
||||
I: d.isDeal === 'n' ? "否" : "是", |
||||
}; |
||||
table.push(row); |
||||
}); |
||||
var header = ["A", "B", "C", "D", "E", "F", "G", "H", "I"]; |
||||
var fileName = "预警信息"; |
||||
proxy.exportExcel(header, table, fileName); |
||||
} |
||||
} |
||||
</script> |
||||
Loading…
Reference in new issue