573 lines
17 KiB
Vue
573 lines
17 KiB
Vue
<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>
|