Files
emsfront/src/views/ems/search/index.vue
2025-12-10 15:00:12 +08:00

573 lines
17 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div
v-loading="loading"
class="ems-dashboard-editor-container"
style="background-color: #ffffff"
>
<el-form ref="form" :model="form" label-position="top">
<el-form-item
label="站点"
prop="siteIds"
:rules="[{ required: true, message: '请选择站点' }]"
>
<el-radio-group v-model="form.siteIds" @input="changeSiteIds">
<el-radio
v-for="(item, index) in siteList"
:key="index + 'zdListSearch'"
:label="item.siteId"
>
{{ item.siteName }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item
label="设备"
prop="deviceCategory"
:rules="[{ required: true, message: '请选择设备' }]"
>
<el-radio-group v-model="form.deviceCategory" @input="changeSiteIds">
<el-radio
v-for="(item, index) in deviceCategoryList"
:key="index + 'sbListSearch'"
:label="item.code"
>
{{ item.name }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item
v-if="isDtdc"
label="单体电池(不超过5个)"
prop="child"
:rules="[{ required: true, message: '请选择单体电池' }]"
>
<el-cascader
v-model="form.child"
style="width: 400px"
:props="{ multiple: true }"
:show-all-levels="false"
:options="childOptions"
@change="handleChildChange"
></el-cascader>
</el-form-item>
<div style="display: flex">
<el-form-item
label="点位"
prop="pointName"
:rules="[{ required: true, message: '请输入点位' }]"
style="margin-right: 50px"
>
<el-autocomplete
v-model="form.pointName"
placeholder="请输入点位"
clearable
:fetch-suggestions="querySearchAsync"
@select="handleSelect"
></el-autocomplete>
</el-form-item>
</div>
<el-form-item>
<el-button type="primary" @click="submitForm">生成图表</el-button>
</el-form-item>
</el-form>
<el-card
shadow="always"
class="common-card-container common-card-container-body-no-padding time-range-card"
>
<div slot="header" class="time-range-header">
<span class="card-title">
<el-radio-group v-model="form.dataUnit">
<el-radio :label="1">分钟</el-radio>
<el-radio :label="2">小时</el-radio>
<el-radio :label="3"></el-radio>
</el-radio-group>
</span>
<date-time-select
ref="dateTimeSelect"
:data-unit="form.dataUnit"
@initDate="(e) => (form.dataRange = e || [])"
@updateDate="updateDate"
/>
</div>
<div style="height: 350px" id="searchChart"></div>
</el-card>
</div>
</template>
<script>
import * as echarts from "echarts";
import resize from "@/mixins/ems/resize";
import {getAllSites} from "@/api/ems/zddt";
import {getAllBatteryIdsBySites, getAllDeviceCategory, getPointValueList, pointFuzzyQuery,} from "@/api/ems/search";
import DateTimeSelect from "./DateTimeSelect.vue";
export default {
name: "Search",
mixins: [resize],
components: {DateTimeSelect},
computed: {
isDtdc() {
return this.form.deviceCategory === "BATTERY";
},
},
watch: {
"form.siteIds": {
handler(newVal) {
newVal && this.isDtdc && this.getChildList();
},
},
isDtdc: {
handler(newVal) {
newVal && this.form.siteIds && this.getChildList();
!newVal && (this.form.child = []);
},
},
"form.dataUnit": {
handler(newVal, oldVal) {
console.log("wacth到了dataUnit的变化", newVal, oldVal);
this.$nextTick(() => {
this.$refs.dateTimeSelect.init();
this.getDate();
});
},
},
},
data() {
return {
chart: null,
deviceCategoryList: [], //设备列表
siteList: [], //站点列表
childOptions: [], //二级设备列表
form: {
dataRange: [], //时间选择范围
child: [],
siteIds: "", //当前选中的站点id 默认选中第一个站点
deviceCategory: "", //设备
pointName: "", //点位
dataUnit: 1, //横坐标
},
loading: false,
};
},
methods: {
changeSiteIds(val) {
console.log("切换站点或设备清空点位", val);
val && (this.form.pointName = "");
},
getChildList() {
this.childOptions = [];
this.form.child = [];
const {siteIds} = this.form;
getAllBatteryIdsBySites([siteIds]).then((response) => {
const data = response?.data || {};
const base = 50;
let options = [];
Object.entries(data).forEach(([key, value], index) => {
if (!value) value = [];
options.push({
value: key,
label: this.siteList.find((s) => s.siteId === key)?.siteName || "",
children: [],
});
const length = value.length;
const num = Math.ceil(length / base);
if (num === 0) return;
for (let i = 1; i <= num; i++) {
const start = (i - 1) * base + 1,
end = i * base;
options[index].children.push({
value: i,
label: `${start}-${end}`,
children: [],
});
for (let s = start; s <= Math.min(length, end); s++) {
options[index].children[i - 1].children.push({
value: value[s - 1],
label: value[s - 1],
});
}
}
});
console.log("二级设备options", options);
this.childOptions = options;
});
},
handleChildChange(data) {
console.log("选择二级设备", data);
this.form.child = data;
},
showLoading() {
this.chart && this.chart.showLoading();
},
hideLoading() {
this.chart && this.chart.hideLoading();
},
initChart() {
this.chart = echarts.init(document.querySelector("#searchChart"));
},
updateDate(val) {
this.form.dataRange = val || [];
this.getDate();
},
setOption(data) {
// 点位类型 dataType 1-瞬时值 2-累计值 仅当值为2展示差值
// 图表类型 chartType 1-曲线图2-箱线图
if (!this.chart) return;
this.chart.clear();
console.log("返回的数据", data);
if (!data || data.length <= 0) {
this.$message.warning("暂无数据");
}
console.log('展示的图表类型chartType', data[0].chartType)
if (data[0].chartType === 2) {
// 箱型图
this.setBoxOption(data)
} else {
//折线图
this.setLineOption(data)
}
},
setLineOption(data) {
let dataset = [];
data.forEach((item, index) => {
item.deviceList.forEach((inner) => {
dataset.push({
name: `${
this.isDtdc
? `${inner.parentDeviceId ? inner.parentDeviceId + "-" : ""}`
: ""
}${inner.deviceId}`,
type: "line",
markPoint: {
symbolSize: 30,
emphasis: {
disabled: false//打开 鼠标高亮
},
data: [//最大值、最小值
{
// type: 'max',
name: `最大值`,
coord: [inner.maxDate, inner.maxValue],
relativeTo: 'coordinate',
label: {
position: "top",
formatter: item.dataType === 2 ? ([
`最大值:${inner.maxValue}`,
// `平均值:${inner.avgValue}`,
`差值:${inner.diffValue}`,
]).join('\n') : ([
`最大值:${inner.maxValue}`,
// `平均值:${inner.avgValue}`,
]).join('\n'),
},
},
{
// type: 'min',
name: `最小值`,
coord: [inner.minDate, inner.minValue],
relativeTo: 'coordinate',
label: {
position: "top",
formatter: item.dataType === 2 ? ([
`最小值:${inner.minValue}`,
// `平均值:${inner.avgValue}`,
`差值:${inner.diffValue}`,
]).join('\n') : ([
`最小值:${inner.minValue}`,
// `平均值:${inner.avgValue}`,
]).join('\n'),
}
}
]
},
xdata: [],
data: [],
});
const length = dataset.length;
inner.pointValueList.forEach((value) => {
dataset[length - 1].xdata.push(value.valueDate);
dataset[length - 1].data.push(value.pointValue);
});
});
});
console.log("折线图图表数据", dataset);
this.chart.setOption({
legend: {
// left: 'center',
// top: '10',
},
grid: {
containLabel: true,
},
tooltip: {
trigger: "axis",
axisPointer: {
type: 'cross',
},
// axisPointer: {
// // 坐标轴指示器,坐标轴触发有效
// type: "shadow", // 默认为直线,可选为:'line' | 'shadow'
// },
},
textStyle: {
color: "#333333",
},
xAxis: {type: "category", data: dataset?.[0]?.xdata || []},
yAxis: {
type: "value",
},
dataZoom: [
{
type: "inside",
start: 0,
end: 100,
},
{
start: 0,
end: 100,
},
],
series: dataset,
});
},
setBoxOption(data) {
let dataset = [];
data.forEach((item, index) => {
item.deviceList.forEach((inner) => {
dataset.push({
name: `${
this.isDtdc
? `${inner.parentDeviceId ? inner.parentDeviceId + "-" : ""}`
: ""
}${inner.deviceId}`,
type: "boxplot",
// markPoint: {
// symbolSize: 30,
// emphasis: {
// disabled: false//打开 鼠标高亮
// },
// data: [//最大值、最小值
// {
// // type: 'max',
// name: `最大值`,
// coord: [inner.maxDate, inner.maxValue],
// relativeTo: 'coordinate',
// label: {
// position: "top",
// formatter: item.dataType === 2 ? ([
// `最大值:${inner.maxValue}`,
// // `平均值:${inner.avgValue}`,
// `差值:${inner.diffValue}`,
// ]).join('\n') : ([
// `最大值:${inner.maxValue}`,
// // `平均值:${inner.avgValue}`,
// ]).join('\n'),
// },
// },
// {
// // type: 'min',
// name: `最小值`,
// coord: [inner.minDate, inner.minValue],
// relativeTo: 'coordinate',
// label: {
// position: "top",
// formatter: item.dataType === 2 ? ([
// `最小值:${inner.minValue}`,
// // `平均值:${inner.avgValue}`,
// `差值:${inner.diffValue}`,
// ]).join('\n') : ([
// `最小值:${inner.minValue}`,
// // `平均值:${inner.avgValue}`,
// ]).join('\n'),
// }
// }
// ]
// },
xdata: [],
data: [],
});
const length = dataset.length;
inner.pointValueList.forEach((value) => {
const {valueDate, min, q1, median, q3, max} = value
dataset[length - 1].xdata.push(valueDate);
dataset[length - 1].data.push([min, q1, median, q3, max]);
});
});
});
console.log("箱型图图表数据", dataset);
this.chart.setOption({
legend: {
// left: 'center',
// top: '10',
},
grid: {
containLabel: true,
},
tooltip: {
trigger: 'item',
formatter: function (params) {
let data = params.data;
let result = params.marker + params.name + ' ' + params.seriesName + '<br/>';
result += '最小值: ' + data[1] + '<br/>';
result += '平均值: ' + data[3] + '<br/>';
result += '最大值: ' + data[5];
return result;
}
// trigger: "axis",
// axisPointer: {
// type: 'cross',
// },
// axisPointer: {
// // 坐标轴指示器,坐标轴触发有效
// type: "shadow", // 默认为直线,可选为:'line' | 'shadow'
// },
},
textStyle: {
color: "#333333",
},
xAxis: {type: "category", data: dataset?.[0]?.xdata || []},
yAxis: {
type: "value",
},
dataZoom: [
{
type: "inside",
start: 0,
end: 100,
},
{
start: 0,
end: 100,
},
],
series: dataset,
});
},
submitForm() {
this.getDate();
},
handleSelect(data) {
this.form.pointName = data.value;
},
querySearchAsync(query, cb) {
console.log("查询数据", query);
if (!this.form.siteIds || !this.form.deviceCategory) {
this.$message({
type: "warning",
message: "请先选择站点和设备",
});
return cb([]);
}
pointFuzzyQuery({
siteIds: [this.form.siteIds],
deviceCategory: this.form.deviceCategory,
pointName: query,
}).then((response) => {
const data = response?.data || [];
cb(
data.map((item) => {
return {name: item, value: item};
})
);
});
},
// 获取所有设备
getDeviceCategory() {
return getAllDeviceCategory().then((response) => {
this.deviceCategoryList = response?.data || [];
});
},
getZdList() {
return getAllSites()
.then((response) => {
this.siteList = response.data || [];
})
.finally(() => {
});
},
getDate() {
this.$refs.form.validate((valid) => {
if (valid) {
if (!this.form.pointName) {
return this.$message.error("请输入正确的点位");
}
if (
this.isDtdc &&
(this.form.child.length === 0 || this.form.child.length > 5)
) {
return this.$message.error("请选择单体电池且不能超过5个");
}
const {
siteIds,
dataUnit,
deviceCategory,
pointName,
dataRange: [start = "", end = ""],
child,
} = this.form;
if (!start || !end) return this.$message.error("请选择时间");
let siteDeviceMap = {};
child.forEach(([first, second, third]) => {
if (siteDeviceMap[first]) {
siteDeviceMap[first].push(third);
} else {
siteDeviceMap[first] = [];
siteDeviceMap[first].push(third);
}
});
let startDate, endDate;
if (start && dataUnit === 3) {
// startDate= start + `${dataUnit === 2 ? ':00' : ' 00:00:00'}`
startDate = start + " 00:00:00";
} else {
startDate = start;
}
if (end && dataUnit === 3) {
// endDate= end + `${dataUnit === 2 ? ':00' : ' 00:00:00'}`
endDate = end + " 00:00:00";
} else {
endDate = end;
}
this.loading = true;
getPointValueList({
siteIds: [siteIds],
dataUnit,
deviceCategory,
pointName,
startDate,
endDate,
siteDeviceMap,
})
.then((response) => {
this.setOption(response?.data || []);
})
.finally(() => {
this.loading = false;
});
}
});
},
},
beforeDestroy() {
if (!this.chart) {
return;
}
this.chart.dispose();
this.chart = null;
},
mounted() {
this.loading = true;
this.$nextTick(() => {
this.initChart();
this.$refs.dateTimeSelect.init();
Promise.all([this.getDeviceCategory(), this.getZdList()]).finally(
() => (this.loading = false)
);
});
},
};
</script>
<style lang="scss" scoped></style>