由于不同监测点有不同的传感器,每个传感器数据对应不同的数据表,有包含不同字段的 SQL 查询语句,因此,需要动态加载传感器并生成查询的 SQL 语句
- 效果图:
- 初始界面:
- 新增界面:
- 展示子组件 SQL 编辑器界面:
- 具体代码:
- 列表展示:warn/warnSetting.vue
<template>
<div class="app-container">
<!--工具栏-->
<div class="head-container">
<eHeader
:permission="permission"
:sensorTypeCodes="dict.device_type_codes"
:warnLevels="dict.warn_levels"
/>
<crudOperation :permission="permission" />
</div>
<!--表格渲染-->
<el-table
ref="table"
v-loading="crud.loading"
:stripe="true"
:data="crud.data"
style="width: 100%;"
@selection-change="crud.selectionChangeHandler"
>
<el-table-column type="selection" width="55" />
<el-table-column
prop="monitorPointCode"
label="监测点编号"
width="160px"
/>
<el-table-column
prop="warnLevel"
label="预警级别"
:formatter="warnLevelFormatter"
align="center"
width="100px"
/>
<el-table-column
prop="sensorTypeCode"
label="传感器类型"
:formatter="deviceTypeCodeFormatter"
align="center"
width="100px"
/>
<el-table-column
prop="warnName"
label="报警名称"
width="240px"
/>
<el-table-column prop="enable" label="有效" width="100px">
<template slot-scope="scope">
{{ scope.row.enable ? '有效' : '无效' }}
</template>
</el-table-column>
<el-table-column prop="sqlStatement" label="预警SQL" />
<el-table-column prop="sensorCodeList" label="应用传感器编号" />
<!-- 编辑与删除 -->
<el-table-column
v-if="checkPer(['admin', 'warnSetting:edit', 'warnSetting:del'])"
label="操作"
width="130px"
align="center"
fixed="right"
>
<template slot-scope="scope">
<udOperation
:data="scope.row"
:permission="permission"
/>
</template>
</el-table-column>
</el-table>
<!--分页组件-->
<pagination />
<!--表单渲染-->
<eForm
:sensorTypeCodes="dict.device_type_codes"
:warnLevels="dict.warn_levels"
/>
</div>
</template>
<script>
import crudWarnSetting from '@/api/dz/warnSetting'
import eHeader from './module/header'
import eForm from './module/form'
import CRUD, { presenter } from '@crud/crud'
import crudOperation from '@crud/CRUD.operation'
import pagination from '@crud/Pagination'
import udOperation from '@crud/UD.operation'
export default {
name: 'WarnSetting',
components: {
eHeader,
eForm,
crudOperation,
pagination,
udOperation,
},
mounted() {
// console.log(this.dict.warn_levels)
},
cruds() {
return CRUD({
title: '报警设置信息',
url: '/api/warn/setting',
crudMethod: { ...crudWarnSetting },
})
},
mixins: [presenter()],
// 数据字典
dicts: ['device_type_codes', 'warn_levels'],
data() {
return {
permission: {
add: ['admin', 'warnSetting:add'],
edit: ['admin', 'warnSetting:edit'],
del: ['admin', 'warnSetting:del'],
},
}
},
methods: {
deviceTypeCodeFormatter(row, column) {
let deviceTypeCodeName = ''
this.dict.device_type_codes.forEach((item) => {
if (item.value === row.sensorTypeCode) {
deviceTypeCodeName = item.label
}
})
return deviceTypeCodeName
},
warnLevelFormatter(row, column) {
let warnLevelName = ''
this.dict.warn_levels.forEach((item) => {
if (item.value === row.warnLevel) {
warnLevelName = item.label
}
})
return warnLevelName
},
},
}
</script>
- 头部查询: warn/module/header.vue
<template>
<div v-if="crud.props.searchToggle">
<el-input
v-model="query.monitorPointCode"
clearable
size="small"
placeholder="监测点编码搜索"
style="width: 200px;"
class="filter-item"
@keyup.enter.native="crud.toQuery"
/>
<el-select
v-model="query.warnLevel"
clearable
size="small"
placeholder="报警级别搜索"
class="filter-item"
style="width: 90px"
@change="crud.toQuery"
>
<el-option
v-for="item in warnLevels"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
<el-select
v-model="query.sensorTypeCode"
clearable
size="small"
placeholder="传感器类型搜索"
class="filter-item"
style="width: 90px"
@change="crud.toQuery"
>
<el-option
v-for="item in sensorTypeCodes"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
<rrOperation />
</div>
</template>
<script>
import { header } from '@crud/crud'
import rrOperation from '@crud/RR.operation'
export default {
components: { rrOperation },
mixins: [header()],
props: {
warnLevels: {
type: Array,
required: true,
},
sensorTypeCodes: {
type: Array,
required: true,
},
permission: {
type: Object,
required: true,
},
},
}
</script>
- 新增界面:warn/module/form.vue
<template>
<el-dialog
append-to-body
:close-on-click-modal="false"
:before-close="crud.cancelCU"
:visible="crud.status.cu > 0"
:title="crud.status.title"
width="1200px"
>
<el-form
ref="form"
:model="form"
:rules="rules"
size="small"
label-width="120px"
>
<el-row>
<el-col :span="8">
<el-form-item label="预警名称:" prop="warnName">
<el-input
v-model="form.warnName"
placeholder="请输入预警名称"
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item
label="预警级别:"
prop="warnLevel"
label-width="120px"
>
<el-select
v-model="form.warnLevel"
clearable
size="small"
placeholder="请选预警级别"
class="filter-item"
style="width: 238px"
>
<el-option
v-for="item in warnLevels"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item
label="预警是否有效:"
prop="enable"
label-width="120px"
>
<el-select
v-model="form.enable"
clearable
size="small"
placeholder="请选预警是否有效"
class="filter-item"
style="width: 238px"
>
<el-option
v-for="item in enables"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="8">
<el-form-item label="绑定监测点:">
<el-button
type="warning"
icon="el-icon-edit"
@click="showMonitorPointSelector"
>选择监测点</el-button
>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="选择的监测点:">
<el-tag type="warning"
>{{ selectedMonitorPointName }}</el-tag
>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item
label="传感器类型:"
prop="sensorTypeCode"
label-width="120px"
>
<el-select
v-model="form.sensorTypeCode"
clearable
size="small"
placeholder="请选传感器类型"
class="filter-item"
style="width: 238px"
@change="selectSensors"
>
<el-option
v-for="item in monitorPointSensorTypeCodes"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="16">
<el-form-item label="预警SQL:" prop="sqlStatement">
<el-input
type="textarea"
:rows="4"
v-model="form.sqlStatement"
>
</el-input>
<el-button
type="success"
icon="el-icon-edit"
@click="showCriteriaEditor"
>SQL编辑器</el-button
>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="预警传感器:" prop="sensorCodeList">
<el-checkbox-group v-model="form.sensorCodeList">
<el-checkbox
v-for="sensorCode in sensorCodes"
:key="sensorCode"
:label="sensorCode"
></el-checkbox>
</el-checkbox-group>
</el-form-item>
</el-col>
</el-row>
</el-form>
<!-- 引用组件,响应自定义事件并设置处理程序 -->
<MonitorPointDialogSelector
ref="monitorPointSelector"
@rowSelected="setMonitorPointData"
/>
<CriteriaEditor
ref="criteriaEditor"
@confirm="setCriteriaEditorData"
/>
<div slot="footer" class="dialog-footer">
<el-button type="text" @click="crud.cancelCU"> 取消 </el-button>
<el-button
:loading="crud.status.cu === 2"
type="primary"
@click="crud.submitCU"
>
确认
</el-button>
</div>
</el-dialog>
</template>
<script>
import { getSensorInfosByMonitorPoint } from '@/api/dz/sensorInfo'
import { form } from '@crud/crud'
// 导入自定义组件
import MonitorPointDialogSelector from '../../../../components/selector/MonitorPointDialogSelector'
import CriteriaEditor from '../../../../components/CriteriaEditor'
const defaultForm = {
id: '',
warnName: '',
warnLevel: '1',
sensorTypeCode: '',
monitorPointCode: '',
sqlStatement: '',
description: '',
enable: true,
sensorCodeList: [],
}
export default {
// 注册导入的组件
components: {
MonitorPointDialogSelector,
CriteriaEditor,
},
mixins: [form(defaultForm)],
props: {
warnLevels: {
type: Array,
required: true,
},
sensorTypeCodes: {
type: Array,
required: true,
},
},
data() {
return {
enables: [
{ label: '有效', value: true },
{ label: '无效', value: false },
],
selectedMonitorPointName: '',
monitorPointSensors: [],
monitorPointSensorTypeCodes: [],
sensorCodes: [],
rules: {
warnName: [
{
required: true,
message: '预警名称不能为空',
trigger: 'blur',
},
],
sqlStatement: [
{
required: true,
message: '预警SQL不能为空',
trigger: 'blur',
},
],
},
}
},
methods: {
// 显示监测点选择对话框
showMonitorPointSelector() {
this.selectedMonitorPointName = ''
this.$refs.monitorPointSelector.$data.dialogVisible = true
},
// 依据获取的监测点选择数据设置字段值
setMonitorPointData(rowData) {
// 将获取的数据设置到模型字段中
this.$refs.form.model.monitorPointCode = rowData.id
this.selectedMonitorPointName = rowData.monitorPointName
// 获取选中监测点绑定的传感器信息
getSensorInfosByMonitorPoint(rowData.id).then((res) => {
// console.log(res)
this.monitorPointSensors = res
this.resetSensorTypeData()
})
},
// 重新设置传感器类型
resetSensorTypeData() {
if (this.monitorPointSensors.length > 0) {
// 清空
this.monitorPointSensorTypeCodes = []
// 获取数据
let tmp = []
this.monitorPointSensors.forEach((item) => {
tmp.push(item.deviceTypeCode)
})
// 去重
let tmpNoRepeat = [...new Set(tmp)]
// 赋值
tmpNoRepeat.forEach((item) => {
this.monitorPointSensorTypeCodes.push({
label: this.getSensorTypeName(item),
value: item,
})
})
// 设置默认值
this.$refs.form.model.sensorTypeCode = tmpNoRepeat[0]
// 更新子组件数据
this.$refs.criteriaEditor.updateSensorTypeCode(
tmpNoRepeat[0]
)
}
},
getSensorTypeName(sensorTypeCode) {
let sensorTypeName = ''
this.sensorTypeCodes.forEach((item) => {
if (item.value === sensorTypeCode) {
sensorTypeName = item.label
}
})
return sensorTypeName
},
// 依据选中的传感器类型,获取具体的传感器编号
selectSensors(value) {
if (this.monitorPointSensors.length > 0) {
this.sensorCodes = []
// 依据选中的传感器类型动态更新子组件的数据(props只支持第一次加载时获取数据)
// 调用子组件的方法进行更新
this.$refs.criteriaEditor.updateSensorTypeCode(value)
this.monitorPointSensors.forEach((item) => {
if (item.deviceTypeCode === value) {
this.sensorCodes.push(item.id)
}
})
}
},
// 显示SQL编辑器
showCriteriaEditor() {
this.$refs.criteriaEditor.$data.dialogVisible = true
},
setCriteriaEditorData(data) {
this.$refs.form.model.sqlStatement = data
},
},
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
::v-deep .el-input-number .el-input__inner {
text-align: left;
}
</style>
- 子组件 SQL 编辑器:components/CriteriaEditor.vue
<template>
<el-dialog
append-to-body
title="SQL编辑器"
:visible.sync="dialogVisible"
width="60%"
>
<!-- 对话框主要内容 -->
<el-container>
<el-aside width="400px" style="background-color: #fff;">
<el-tree
:data="treeData"
node-key="id"
default-expand-all
:expand-on-click-node="false"
>
<span
class="custom-tree-node"
slot-scope="{ node, data }"
>
<span>{{ node.label }}</span>
<span v-if="node.id > 1">
<el-button
type="text"
size="mini"
@click="() => remove(node, data)"
>
删除
</el-button>
</span>
</span>
</el-tree>
</el-aside>
<el-container>
<el-header style="padding: 0; margin: 0;">
<el-tag
size="medium"
type="success"
style="font-size: 16px;"
>{{ '传感器类型:' + sensorTypeName }}</el-tag
>
</el-header>
<el-main>
<el-row :gutter="10">
<el-divider content-position="right">
<el-button
type="success"
size="small"
round
icon="el-icon-plus"
@click="addSubItem"
>添加条件项</el-button
>
</el-divider>
</el-row>
<el-row
:gutter="10"
v-for="(criteria, index) in criterias"
:key="index"
style="padding-bottom: 10px;"
>
<el-col :span="3" v-if="index > 0">
<el-select v-model="criteria.relation">
<el-option
v-for="item in relationSymbols"
:key="item.key"
:label="item.value"
:value="item.key"
>
</el-option>
</el-select>
</el-col>
<el-col :span="3" v-else> </el-col>
<el-col :span="6">
<el-select v-model="criteria.item.property">
<el-option
v-for="item in properties"
:key="item.property"
:label="item.label"
:value="item.property"
>
</el-option>
</el-select>
</el-col>
<el-col :span="4">
<el-select v-model="criteria.compare">
<el-option
v-for="item in compareSymbols"
:key="item.key"
:label="item.value"
:value="item.key"
>
</el-option>
</el-select>
</el-col>
<el-col :span="5">
<el-input-number
v-model="criteria.value"
:precision="2"
:step="0.1"
style="width: 100%;"
></el-input-number>
</el-col>
<el-col :span="1" v-if="index > 0">
<el-button
type="danger"
circle
icon="el-icon-delete"
@click="delSubItem(index)"
></el-button>
</el-col>
</el-row>
<el-divider content-position="left">
<el-button type="primary" @click="addCriteriaItem"
>添加子条件</el-button
>
</el-divider>
</el-main>
</el-container>
</el-container>
<!-- 对话框底部内容 -->
<span slot="footer" class="dialog-footer">
<!-- 确认并关闭对话框,同时,传出选择的行数据 -->
<el-button type="primary" @click="selectedAndClose"
>确 定</el-button
>
</span>
</el-dialog>
</template>
<script>
let id = 1000
export default {
data() {
return {
dialogVisible: false,
sensorTypeCode: '',
sensorTypeName: '',
treeData: [{ id: 1, label: 'SQL预警条件', children: [] }],
criterias: [
{
relation: 'and',
item: { property: '', label: '' },
compare: '>',
value: 0,
},
],
properties: [],
// 比较符号数组
compareSymbols: [
{ key: '=', value: '等于' },
{ key: '>', value: '大于' },
{ key: '>=', value: '大于等于' },
{ key: '<', value: '小于' },
{ key: '<=', value: '小于等于' },
],
// 关系符号数组
relationSymbols: [
{ key: 'and', value: '且' },
{ key: 'or', value: '或' },
],
// 传感器候选项
sensorCandidates: [
{
label: '雨量',
value: '01',
children: [
{ property: 'value', label: '降雨量' },
{
property: 'totalValue',
label: '当日雨量累计',
},
],
},
{
label: '气温',
value: '03',
children: [{ property: 'value', label: '温度' }],
},
{
label: '土压力',
value: '04',
children: [{ property: 'value', label: '压力' }],
},
{
label: '地表位移',
value: '07',
children: [
{ property: 'gpsTotalX', label: 'X方向位移' },
{ property: 'gpsTotalY', label: 'Y方向位移' },
{ property: 'gpsTotalZ', label: 'Z方向位移' },
],
},
{
label: '土壤温度',
value: '09',
children: [{ property: 'value', label: '温度' }],
},
{
label: '泥水位',
value: '10',
children: [
{ property: 'value', label: '泥水位数据' },
],
},
{
label: '孔隙水压力',
value: '11',
children: [
{ property: 'value', label: '孔隙水压力' },
{ property: 'temp', label: '孔隙水水温' },
],
},
{
label: '土壤含水率',
value: '12',
children: [{ property: 'value', label: '含水率' }],
},
{
label: '次声',
value: '16',
children: [
{ property: 'osp', label: '原始声压' },
{ property: 'vsp', label: '有效声压' },
{ property: 'freq', label: '频率' },
{ property: 'wave', label: '波形' },
],
},
{
label: '裂缝',
value: '17',
children: [{ property: 'value', label: '张开度' }],
},
{
label: '深部位移',
value: '22',
children: [
{
property: 'dispsX',
label: '顺滑动方向累计变形量',
},
{
property: 'dispsY',
label: '垂直坡面方向累计变形量',
},
],
},
{
label: '倾角',
value: '23',
children: [
{ property: 'x', label: 'X轴方向倾角' },
{ property: 'y', label: 'Y轴方向倾角' },
{ property: 'z', label: 'Z轴方向倾角' },
{
property: 'angle',
label: 'XY轴合成方位倾角',
},
{
property: 'trend',
label: 'XY轴合成方位角度',
},
],
},
{
label: '沉降',
value: '28',
children: [
{ property: 'value', label: '单次变化量' },
],
},
{
label: '加速度',
value: '30',
children: [
{ property: 'gX', label: 'X轴方向加速度' },
{ property: 'gY', label: 'Y轴方向加速度' },
{ property: 'gZ', label: 'Z轴方向加速度' },
],
},
{
label: '振动',
value: '34',
children: [
{ property: 'plX', label: 'X向振动频率' },
{ property: 'plY', label: 'Y向振动频率' },
{ property: 'plZ', label: 'Z向振动频率' },
{ property: 'value', label: '振动幅度' },
{ property: 'sjX', label: 'X向瞬间位移' },
{ property: 'sjY', label: 'Y向瞬间位移' },
{ property: 'sjZ', label: 'Z向瞬间位移' },
{
property: 'sjValue',
label: '合方向瞬间位移',
},
],
},
{
label: '地声',
value: '36',
children: [
{ property: 'osp', label: '原始声压' },
{ property: 'vsp', label: '有效声压' },
{ property: 'freq', label: '频率' },
{ property: 'wave', label: '波形' },
],
},
{
label: '地表水位 ',
value: '38',
children: [
{ property: 'value', label: '地表水压力' },
{ property: 'temp', label: '地表水水温' },
],
},
{
label: '地下水位 ',
value: '39',
children: [
{ property: 'value', label: '地下水压力' },
{ property: 'temp', label: '地下水水温' },
],
},
{
label: '雷达 ',
value: '42',
children: [
{ property: 'x', label: 'X向坐标' },
{ property: 'y', label: 'Y向坐标' },
{ property: 'z', label: 'Z向坐标' },
{ property: 'speed', label: '移动速度' },
],
},
],
}
},
methods: {
// props只支持第一次加载这个组件的时候获取父组件的值,后续修改父组件的值得时候子组件并不会动态的更改。
// 动态更新sensorTypeCode数据,该方法被父组件调用
updateSensorTypeCode(value) {
this.sensorTypeCode = value
console.log('传感器类型:' + this.sensorTypeCode)
if (this.sensorTypeCode != '') {
this.sensorTypeName = this.sensorCandidates.filter(
(item) => item.value === this.sensorTypeCode
)[0].label
this.properties = this.sensorCandidates.filter(
(item) => item.value === this.sensorTypeCode
)[0].children
}
},
selectedAndClose() {
// 激发自定义事件,并传递选中数据
this.$emit('confirm', this.buildSql())
// 清空
this.sensorTypeCode = ''
this.treeData[0].children = []
// 关闭对话框
this.dialogVisible = false
},
remove(node, data) {
if (node.id == 1) {
return
}
const parent = node.parent
const children = parent.data.children || parent.data
const index = children.findIndex((d) => d.id === data.id)
children.splice(index, 1)
},
addSubItem() {
this.criterias.push({
relation: 'and',
item: { property: '', label: '' },
compare: '>',
value: 0,
})
},
delSubItem(index) {
this.criterias.splice(index, 1)
},
addCriteriaItem() {
let where = ''
for (let i = 0; i < this.criterias.length; i++) {
let criteria = this.criterias[i]
let item =
criteria.item.property +
' ' +
criteria.compare +
' ' +
criteria.value
if (i == 0) {
where = item
continue
}
where += ' ' + criteria.relation + ' ' + item
}
this.treeData[0].children.push({ id: id++, label: where })
},
buildSql() {
let sql = ''
for (let i = 0; i < this.treeData[0].children.length; i++) {
let item = this.treeData[0].children[i]
if (i == 0) {
sql = '( ' + item.label + ' )'
continue
}
sql += ' and ( ' + item.label + ' )'
}
return (
'select * from ' +
this.getTable() +
' where ( ' +
sql +
' )'
)
},
getTable() {
switch (this.sensorTypeCode) {
case '01':
return 'tbl_jc_yl'
case '03':
return 'tbl_jc_qw'
case '04':
return 'tbl_jc_ytyl'
case '07':
return 'tbl_jc_gnss'
case '09':
return 'tbl_jc_tw'
case '10':
return 'tbl_jc_nsw'
case '11':
return 'tbl_jc_kxsyl'
case '12':
return 'tbl_jc_trhsl'
case '16':
return 'tbl_jc_cs'
case '17':
return 'tbl_jc_lfwy'
case '22':
return 'tbl_jc_sbwycx'
case '23':
return 'tbl_jc_qj'
case '28':
return 'tbl_jc_cj'
case '30':
return 'tbl_jc_jsd'
case '34':
return 'tbl_jc_zd'
case '36':
return 'tbl_jc_ds'
case '38':
return 'tbl_jc_dbsw'
case '39':
return 'tbl_jc_dxsw'
case '42':
return 'tbl_jc_ld'
default:
return ''
}
},
},
}
</script>
<style>
.custom-tree-node {
flex: 1;
display: flex;
align-items: center;
justify-content: space-between;
font-size: 14px;
padding-right: 8px;
}
</style>