<template>
|
<div class="app-container">
|
<!-- 统计卡片 -->
|
<el-row :gutter="20" class="stat-row">
|
<el-col :span="6">
|
<el-statistic title="报警点位总数" :value="statistics.totalPoints" />
|
</el-col>
|
<el-col :span="6">
|
<el-statistic title="已连接设备" :value="statistics.connectedDevices" />
|
</el-col>
|
<el-col :span="6">
|
<el-statistic title="今日数据量(万条)" :value="statistics.todayDataVolume" />
|
</el-col>
|
<el-col :span="6">
|
<el-statistic title="解析成功率(%)" :value="statistics.parseSuccessRate" />
|
</el-col>
|
</el-row>
|
|
<!-- 报警点位台账 -->
|
<el-card class="box-card">
|
<template #header>
|
<div class="card-header">
|
<span>报警点位台账</span>
|
<div>
|
<el-button type="primary" size="small" icon="Plus" @click="handleAdd">新增</el-button>
|
<!-- <el-button size="small" icon="Download" @click="handleExport">导出</el-button> -->
|
</div>
|
</div>
|
</template>
|
|
<el-form :inline="true" :model="queryParams">
|
<el-form-item label="点位名称">
|
<el-input v-model="queryParams.pointName" placeholder="请输入" clearable />
|
</el-form-item>
|
<el-form-item label="设备类型">
|
<el-select v-model="queryParams.deviceType" placeholder="请选择" clearable style="width: 150px">
|
<el-option label="温度传感器" value="温度传感器" />
|
<el-option label="压力传感器" value="压力传感器" />
|
<el-option label="流量传感器" value="流量传感器" />
|
<el-option label="液位传感器" value="液位传感器" />
|
</el-select>
|
</el-form-item>
|
<el-form-item>
|
<el-button type="primary" icon="Search" @click="getList">查询</el-button>
|
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
</el-form-item>
|
</el-form>
|
|
<el-table :data="pointList" v-loading="loading" border>
|
<el-table-column type="index" label="序号" width="60" align="center" />
|
<el-table-column prop="pointCode" label="点位编码" width="120" align="center" />
|
<el-table-column prop="pointName" label="点位名称" min-width="150" />
|
<el-table-column prop="deviceType" label="设备类型" width="120" align="center" />
|
<el-table-column prop="alarmLevel" label="报警级别" width="100" align="center">
|
<template #default="{ row }">
|
<el-tag :type="row.alarmLevel === '紧急' ? 'danger' : row.alarmLevel === '重要' ? 'warning' : 'info'" size="small">
|
{{ row.alarmLevel }}
|
</el-tag>
|
</template>
|
</el-table-column>
|
<el-table-column prop="thresholdValue" label="报警阈值" width="120" align="center" />
|
<el-table-column prop="area" label="所属区域" width="120" align="center" />
|
<el-table-column prop="status" label="状态" width="80" align="center">
|
<template #default="{ row }">
|
<el-tag :type="row.status === 1 ? 'success' : 'info'" size="small">
|
{{ row.status === 1 ? '启用' : '停用' }}
|
</el-tag>
|
</template>
|
</el-table-column>
|
<el-table-column label="操作" width="150" align="center" fixed="right">
|
<template #default="{ row }">
|
<el-button type="primary" link size="small" @click="handleEdit(row)">编辑</el-button>
|
<el-button type="danger" link size="small" @click="handleDelete(row)">删除</el-button>
|
</template>
|
</el-table-column>
|
</el-table>
|
|
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
|
</el-card>
|
|
<!-- 数据采集配置 -->
|
<el-card class="box-card" style="margin-top: 20px;">
|
<template #header>
|
<div class="card-header">
|
<span>数据采集配置</span>
|
<el-button type="primary" size="small" icon="Plus" @click="handleAddInterface">新增接口</el-button>
|
</div>
|
</template>
|
|
<el-table :data="interfaceList" border>
|
<el-table-column type="index" label="序号" width="60" align="center" />
|
<el-table-column prop="interfaceName" label="接口名称" min-width="150" />
|
<el-table-column prop="interfaceType" label="接口类型" width="120" align="center">
|
<template #default="{ row }">
|
<el-tag size="small">{{ row.interfaceType }}</el-tag>
|
</template>
|
</el-table-column>
|
<el-table-column prop="serverAddress" label="服务器地址" width="180" />
|
<el-table-column prop="collectFreq" label="采集频率" width="100" align="center" />
|
<el-table-column label="操作" width="250" align="center">
|
<template #default="{ row }">
|
<el-button type="primary" link size="small" @click="handleEditInterface(row)">编辑</el-button>
|
<el-button type="danger" link size="small" @click="handleDeleteInterface(row)">删除</el-button>
|
</template>
|
</el-table-column>
|
</el-table>
|
</el-card>
|
|
<!-- 新增/编辑点位弹窗 -->
|
<el-dialog :title="dialogTitle" v-model="dialogVisible" width="500px" append-to-body>
|
<el-form ref="formRef" :model="form" :rules="rules" label-width="100px">
|
<el-form-item label="点位编码" prop="pointCode">
|
<el-input v-model="form.pointCode" placeholder="请输入点位编码" />
|
</el-form-item>
|
<el-form-item label="点位名称" prop="pointName">
|
<el-input v-model="form.pointName" placeholder="请输入点位名称" />
|
</el-form-item>
|
<el-form-item label="设备类型" prop="deviceType">
|
<el-select v-model="form.deviceType" placeholder="请选择" style="width: 100%">
|
<el-option label="温度传感器" value="温度传感器" />
|
<el-option label="压力传感器" value="压力传感器" />
|
<el-option label="流量传感器" value="流量传感器" />
|
<el-option label="液位传感器" value="液位传感器" />
|
</el-select>
|
</el-form-item>
|
<el-form-item label="报警级别" prop="alarmLevel">
|
<el-select v-model="form.alarmLevel" placeholder="请选择" style="width: 100%">
|
<el-option label="紧急" value="紧急" />
|
<el-option label="重要" value="重要" />
|
<el-option label="一般" value="一般" />
|
</el-select>
|
</el-form-item>
|
<el-form-item label="报警阈值" prop="thresholdValue">
|
<el-input v-model="form.thresholdValue" placeholder="请输入报警阈值" />
|
</el-form-item>
|
<el-form-item label="所属区域" prop="area">
|
<el-input v-model="form.area" placeholder="请输入所属区域" />
|
</el-form-item>
|
<el-form-item label="状态" prop="status">
|
<el-radio-group v-model="form.status">
|
<el-radio :label="1">启用</el-radio>
|
<el-radio :label="0">停用</el-radio>
|
</el-radio-group>
|
</el-form-item>
|
</el-form>
|
<template #footer>
|
<el-button @click="dialogVisible = false">取消</el-button>
|
<el-button type="primary" @click="submitForm">确定</el-button>
|
</template>
|
</el-dialog>
|
|
<!-- 新增/编辑接口弹窗 -->
|
<el-dialog :title="interfaceDialogTitle" v-model="interfaceDialogVisible" width="550px" append-to-body>
|
<el-form ref="interfaceFormRef" :model="interfaceForm" :rules="interfaceRules" label-width="100px">
|
<el-form-item label="接口名称" prop="interfaceName">
|
<el-input v-model="interfaceForm.interfaceName" placeholder="请输入接口名称" />
|
</el-form-item>
|
<el-form-item label="接口类型" prop="interfaceType">
|
<el-select v-model="interfaceForm.interfaceType" placeholder="请选择" style="width: 100%">
|
<el-option label="OPC UA" value="OPC UA" />
|
<el-option label="Modbus TCP" value="Modbus TCP" />
|
<el-option label="Modbus RTU" value="Modbus RTU" />
|
<el-option label="MQTT" value="MQTT" />
|
<el-option label="HTTP API" value="HTTP API" />
|
</el-select>
|
</el-form-item>
|
<el-form-item label="服务器地址" prop="serverAddress">
|
<el-input v-model="interfaceForm.serverAddress" placeholder="如: opc.tcp://192.168.1.100:4840" />
|
</el-form-item>
|
<el-form-item label="采集频率" prop="collectFreq">
|
<el-input v-model="interfaceForm.collectFreq" placeholder="如: 1秒、5秒、1分钟" />
|
</el-form-item>
|
<el-form-item label="用户名">
|
<el-input v-model="interfaceForm.username" placeholder="请输入用户名(可选)" />
|
</el-form-item>
|
<el-form-item label="密码">
|
<el-input v-model="interfaceForm.password" type="password" placeholder="请输入密码(可选)" show-password />
|
</el-form-item>
|
<el-form-item label="描述">
|
<el-input v-model="interfaceForm.description" type="textarea" :rows="3" placeholder="请输入接口描述(可选)" />
|
</el-form-item>
|
</el-form>
|
<template #footer>
|
<el-button @click="interfaceDialogVisible = false">取消</el-button>
|
<el-button type="primary" @click="submitInterfaceForm">确定</el-button>
|
</template>
|
</el-dialog>
|
</div>
|
</template>
|
|
<script setup>
|
import { ref, reactive, onMounted } from 'vue';
|
import { ElMessage, ElMessageBox } from 'element-plus';
|
import Pagination from '@/components/Pagination/index.vue';
|
import {
|
listAlarmPointLedger,
|
addAlarmPoint,
|
updateAlarmPoint,
|
delAlarmPoint,
|
changePointStatus,
|
exportPointLedger,
|
listDataInterface,
|
startDataCollection,
|
stopDataCollection,
|
getCollectionStatistics,
|
addDataInterface,
|
delDataInterface,
|
changeInterfaceStatus
|
} from '@/api/alarmManagement/dataCollection';
|
|
// 统计数据
|
const statistics = reactive({
|
totalPoints: 0,
|
connectedDevices: 0,
|
todayDataVolume: 0,
|
parseSuccessRate: 0
|
});
|
|
// 查询参数
|
const queryParams = reactive({
|
pageNum: 1,
|
pageSize: 10,
|
pointName: '',
|
deviceType: ''
|
});
|
|
const loading = ref(false);
|
const pointList = ref([]);
|
const total = ref(0);
|
|
// 接口列表
|
const interfaceList = ref([]);
|
|
// 点位弹窗相关
|
const dialogVisible = ref(false);
|
const dialogTitle = ref('');
|
const formRef = ref(null);
|
const form = reactive({
|
pointId: undefined,
|
pointCode: '',
|
pointName: '',
|
deviceType: '',
|
alarmLevel: '',
|
thresholdValue: '',
|
area: '',
|
status: 1
|
});
|
|
const rules = {
|
pointCode: [{ required: true, message: '点位编码不能为空', trigger: 'blur' }],
|
pointName: [{ required: true, message: '点位名称不能为空', trigger: 'blur' }],
|
deviceType: [{ required: true, message: '设备类型不能为空', trigger: 'change' }],
|
alarmLevel: [{ required: true, message: '报警级别不能为空', trigger: 'change' }],
|
thresholdValue: [{ required: true, message: '报警阈值不能为空', trigger: 'blur' }],
|
area: [{ required: true, message: '所属区域不能为空', trigger: 'blur' }],
|
status: [{ required: true, message: '状态不能为空', trigger: 'change' }]
|
};
|
|
// 接口弹窗相关
|
const interfaceDialogVisible = ref(false);
|
const interfaceDialogTitle = ref('');
|
const interfaceFormRef = ref(null);
|
const interfaceForm = reactive({
|
interfaceId: undefined,
|
interfaceName: '',
|
interfaceType: '',
|
serverAddress: '',
|
collectFreq: '',
|
username: '',
|
password: '',
|
description: ''
|
});
|
|
const interfaceRules = {
|
interfaceName: [{ required: true, message: '接口名称不能为空', trigger: 'blur' }],
|
interfaceType: [{ required: true, message: '接口类型不能为空', trigger: 'change' }],
|
serverAddress: [{ required: true, message: '服务器地址不能为空', trigger: 'blur' }],
|
collectFreq: [{ required: true, message: '采集频率不能为空', trigger: 'blur' }]
|
};
|
|
// 获取统计数据
|
async function getStatistics() {
|
const res = await getCollectionStatistics();
|
if (res.code === 200) {
|
Object.assign(statistics, res.data);
|
}
|
}
|
|
// 获取点位列表
|
async function getList() {
|
loading.value = true;
|
const res = await listAlarmPointLedger(queryParams);
|
if (res.code === 200) {
|
pointList.value = res.data.records || res.rows;
|
total.value = res.data.total || res.total;
|
}
|
loading.value = false;
|
}
|
|
// 获取接口列表
|
async function getInterfaceList() {
|
const res = await listDataInterface();
|
if (res.code === 200) {
|
interfaceList.value = res.data;
|
}
|
}
|
|
function resetQuery() {
|
queryParams.pointName = '';
|
queryParams.deviceType = '';
|
getList();
|
}
|
|
function handleAdd() {
|
dialogTitle.value = '新增点位';
|
Object.keys(form).forEach(key => form[key] = key === 'pointId' ? undefined : '');
|
dialogVisible.value = true;
|
}
|
|
function handleEdit(row) {
|
dialogTitle.value = '编辑点位';
|
Object.assign(form, row);
|
dialogVisible.value = true;
|
}
|
|
async function submitForm() {
|
formRef.value.validate(async (valid) => {
|
if (valid) {
|
const api = form.pointId ? updateAlarmPoint : addAlarmPoint;
|
const res = await api(form);
|
if (res.code === 200) {
|
ElMessage.success(form.pointId ? '修改成功' : '新增成功');
|
dialogVisible.value = false;
|
getList();
|
}
|
}
|
});
|
}
|
|
async function handleDelete(row) {
|
await ElMessageBox.confirm('确认删除该点位吗?', '提示', { type: 'warning' });
|
const res = await delAlarmPoint(row.pointId);
|
if (res.code === 200) {
|
ElMessage.success('删除成功');
|
getList();
|
}
|
}
|
|
async function handleBeforeStatusChange(row) {
|
// 返回 true 允许切换,false 阻止切换
|
try {
|
const newStatus = row.status === 1 ? 0 : 1;
|
const res = await changePointStatus(row.pointId, newStatus);
|
if (res.code === 200) {
|
ElMessage.success('状态修改成功');
|
return true;
|
}
|
return false;
|
} catch (error) {
|
return false;
|
}
|
}
|
|
async function handleStatusChange(row, val) {
|
// 状态已经通过 before-change 更新,这里只做刷新
|
getList();
|
}
|
|
async function handleExport() {
|
await exportPointLedger(queryParams);
|
ElMessage.success('导出成功');
|
}
|
|
function handleAddInterface() {
|
interfaceDialogTitle.value = '新增接口';
|
Object.keys(interfaceForm).forEach(key => interfaceForm[key] = key === 'interfaceId' ? undefined : '');
|
interfaceDialogVisible.value = true;
|
}
|
|
function handleEditInterface(row) {
|
interfaceDialogTitle.value = '编辑接口';
|
Object.assign(interfaceForm, row);
|
interfaceDialogVisible.value = true;
|
}
|
|
async function submitInterfaceForm() {
|
interfaceFormRef.value.validate(async (valid) => {
|
if (valid) {
|
const res = await addDataInterface(interfaceForm);
|
if (res.code === 200) {
|
ElMessage.success(interfaceForm.interfaceId ? '修改成功' : '新增成功');
|
interfaceDialogVisible.value = false;
|
getInterfaceList();
|
}
|
}
|
});
|
}
|
|
async function handleDeleteInterface(row) {
|
await ElMessageBox.confirm('确认删除该接口吗?', '提示', { type: 'warning' });
|
const res = await delDataInterface(row.interfaceId);
|
if (res.code === 200) {
|
ElMessage.success('删除成功');
|
getInterfaceList();
|
}
|
}
|
|
async function handleInterfaceStatusChange(row) {
|
const res = await changeInterfaceStatus(row.interfaceId, row.status);
|
if (res.code === 200) {
|
ElMessage.success('状态修改成功');
|
}
|
}
|
|
async function handleStart(row) {
|
const res = await startDataCollection(row.interfaceId);
|
if (res.code === 200) {
|
ElMessage.success('启动成功');
|
getInterfaceList();
|
}
|
}
|
|
async function handleStop(row) {
|
const res = await stopDataCollection(row.interfaceId);
|
if (res.code === 200) {
|
ElMessage.success('停止成功');
|
getInterfaceList();
|
}
|
}
|
|
onMounted(() => {
|
getStatistics();
|
getList();
|
getInterfaceList();
|
});
|
</script>
|
|
<style scoped>
|
.stat-row {
|
margin-bottom: 20px;
|
}
|
.stat-row :deep(.el-statistic__content) {
|
font-size: 28px;
|
font-weight: 600;
|
color: #303133;
|
}
|
.stat-row :deep(.el-statistic__title) {
|
font-size: 14px;
|
color: #606266;
|
margin-bottom: 8px;
|
}
|
.card-header {
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
}
|
</style>
|