<template>
|
<div class="app-container">
|
<!-- 页面标题 -->
|
<div class="page-header">
|
<h2>用气管理系统</h2>
|
<div class="header-info">
|
<span class="update-time">最后更新:{{ lastUpdateTime }}</span>
|
<el-button type="primary" size="small" @click="refreshData">
|
<el-icon><Refresh /></el-icon>
|
刷新数据
|
</el-button>
|
</div>
|
</div>
|
|
<!-- 统计卡片区域 -->
|
<div class="stats-cards">
|
<el-row :gutter="20">
|
<el-col :span="6">
|
<el-card class="stat-card">
|
<div class="stat-content">
|
<div class="stat-icon gas-device">
|
<el-icon size="32"><Box /></el-icon>
|
</div>
|
<div class="stat-info">
|
<div class="stat-value">{{ totalDevices }}</div>
|
<div class="stat-label">在用设备</div>
|
</div>
|
</div>
|
</el-card>
|
</el-col>
|
<el-col :span="6">
|
<el-card class="stat-card">
|
<div class="stat-content">
|
<div class="stat-icon daily-consumption">
|
<el-icon size="32"><TrendCharts /></el-icon>
|
</div>
|
<div class="stat-info">
|
<div class="stat-value">{{ dailyConsumption }} m³</div>
|
<div class="stat-label">日耗量</div>
|
</div>
|
</div>
|
</el-card>
|
</el-col>
|
<el-col :span="6">
|
<el-card class="stat-card">
|
<div class="stat-content">
|
<div class="stat-icon monthly-consumption">
|
<el-icon size="32"><DataLine /></el-icon>
|
</div>
|
<div class="stat-info">
|
<div class="stat-value">{{ monthlyConsumption }} m³</div>
|
<div class="stat-label">月耗量</div>
|
</div>
|
</div>
|
</el-card>
|
</el-col>
|
<el-col :span="6">
|
<el-card class="stat-card">
|
<div class="stat-content">
|
<div class="stat-icon gas-price">
|
<el-icon size="32"><Money /></el-icon>
|
</div>
|
<div class="stat-info">
|
<div class="stat-value">¥{{ gasUnitPrice }}</div>
|
<div class="stat-label">气体单价</div>
|
</div>
|
</div>
|
</el-card>
|
</el-col>
|
</el-row>
|
</div>
|
|
<!-- 费用统计区域 -->
|
<div class="cost-stats">
|
<el-row :gutter="20">
|
<el-col :span="12">
|
<el-card class="cost-card">
|
<template #header>
|
<div class="card-header">
|
<span>日费用统计</span>
|
<el-tag type="success" size="small">今日</el-tag>
|
</div>
|
</template>
|
<div class="cost-content">
|
<div class="cost-main">
|
<span class="cost-amount">¥{{ dailyTotalCost.toFixed(2) }}</span>
|
<span class="cost-unit">元</span>
|
</div>
|
<div class="cost-details">
|
<div class="cost-item">
|
<span>消耗量:</span>
|
<span>{{ dailyConsumption }} m³</span>
|
</div>
|
<div class="cost-item">
|
<span>单价:</span>
|
<span>¥{{ gasUnitPrice }}/m³</span>
|
</div>
|
</div>
|
</div>
|
</el-card>
|
</el-col>
|
<el-col :span="12">
|
<el-card class="cost-card">
|
<template #header>
|
<div class="card-header">
|
<span>月费用统计</span>
|
<el-tag type="primary" size="small">本月</el-tag>
|
</div>
|
</template>
|
<div class="cost-content">
|
<div class="cost-main">
|
<span class="cost-amount">¥{{ monthlyTotalCost.toFixed(2) }}</span>
|
<span class="cost-unit">元</span>
|
</div>
|
<div class="cost-details">
|
<div class="cost-item">
|
<span>消耗量:</span>
|
<span>{{ monthlyConsumption }} m³</span>
|
</div>
|
<div class="cost-item">
|
<span>平均单价:</span>
|
<span>¥{{ gasUnitPrice }}/m³</span>
|
</div>
|
</div>
|
</div>
|
</el-card>
|
</el-col>
|
</el-row>
|
</div>
|
|
<!-- 设备列表区域 -->
|
<div class="device-section">
|
<el-card>
|
<template #header>
|
<div class="card-header">
|
<span>设备监控</span>
|
<div class="header-actions">
|
<el-button type="primary" size="small" @click="addDevice">
|
<el-icon><Plus /></el-icon>
|
添加设备
|
</el-button>
|
</div>
|
</div>
|
</template>
|
|
<el-table :data="deviceList" border style="width: 100%" v-loading="tableLoading">
|
<el-table-column align="center" label="序号" type="index" width="60" />
|
<el-table-column label="设备编号" prop="deviceCode" width="120" show-overflow-tooltip />
|
<el-table-column label="设备名称" prop="deviceName" width="150" show-overflow-tooltip />
|
<el-table-column label="设备类型" prop="deviceType" width="120" show-overflow-tooltip />
|
<el-table-column label="规格型号" prop="specification" width="150" show-overflow-tooltip />
|
<el-table-column label="当前压力(MPa)" prop="currentPressure" width="130" show-overflow-tooltip>
|
<template #default="scope">
|
<span :class="getPressureClass(scope.row.currentPressure)">
|
{{ scope.row.currentPressure }}
|
</span>
|
</template>
|
</el-table-column>
|
<el-table-column label="当前温度(℃)" prop="currentTemperature" width="130" show-overflow-tooltip>
|
<template #default="scope">
|
<span :class="getTemperatureClass(scope.row.currentTemperature)">
|
{{ scope.row.currentTemperature }}
|
</span>
|
</template>
|
</el-table-column>
|
<el-table-column label="气体浓度(ppm)" prop="gasConcentration" width="140" show-overflow-tooltip>
|
<template #default="scope">
|
<span :class="getConcentrationClass(scope.row.gasConcentration)">
|
{{ scope.row.gasConcentration }}
|
</span>
|
</template>
|
</el-table-column>
|
<el-table-column label="运行状态" prop="status" width="100" show-overflow-tooltip>
|
<template #default="scope">
|
<el-tag :type="getStatusType(scope.row.status)" size="small">
|
{{ getStatusText(scope.row.status) }}
|
</el-tag>
|
</template>
|
</el-table-column>
|
<el-table-column label="最后更新" prop="lastUpdate" width="160" show-overflow-tooltip />
|
<el-table-column label="操作" align="center" width="100" fixed="right">
|
<template #default="scope">
|
<el-button link size="small" @click="editDevice(scope.row)">
|
编辑
|
</el-button>
|
</template>
|
</el-table-column>
|
</el-table>
|
</el-card>
|
</div>
|
|
<!-- 添加/编辑设备弹窗 -->
|
<el-dialog v-model="deviceDialogVisible" :title="dialogTitle" width="600px">
|
<el-form :model="deviceForm" :rules="deviceRules" ref="deviceFormRef" label-width="120px">
|
<el-row :gutter="20">
|
<el-col :span="12">
|
<el-form-item label="设备编号" prop="deviceCode">
|
<el-input v-model="deviceForm.deviceCode" placeholder="请输入设备编号" />
|
</el-form-item>
|
</el-col>
|
<el-col :span="12">
|
<el-form-item label="设备名称" prop="deviceName">
|
<el-input v-model="deviceForm.deviceName" placeholder="请输入设备名称" />
|
</el-form-item>
|
</el-col>
|
</el-row>
|
<el-row :gutter="20">
|
<el-col :span="12">
|
<el-form-item label="设备类型" prop="deviceType">
|
<el-select v-model="deviceForm.deviceType" placeholder="请选择设备类型" style="width: 100%">
|
<el-option label="液化气储罐" value="液化气储罐" />
|
<el-option label="压缩气储罐" value="压缩气储罐" />
|
<el-option label="天然气储罐" value="天然气储罐" />
|
<el-option label="氧气储罐" value="氧气储罐" />
|
<el-option label="其他" value="其他" />
|
</el-select>
|
</el-form-item>
|
</el-col>
|
<el-col :span="12">
|
<el-form-item label="规格型号" prop="specification">
|
<el-input v-model="deviceForm.specification" placeholder="请输入规格型号" />
|
</el-form-item>
|
</el-col>
|
</el-row>
|
<el-row :gutter="20">
|
<el-col :span="12">
|
<el-form-item label="设计压力(MPa)" prop="designPressure">
|
<el-input-number v-model="deviceForm.designPressure" :min="0" :precision="2" style="width: 100%" />
|
</el-form-item>
|
</el-col>
|
<el-col :span="12">
|
<el-form-item label="容积(m³)" prop="volume">
|
<el-input-number v-model="deviceForm.volume" :min="0" :precision="2" style="width: 100%" />
|
</el-form-item>
|
</el-col>
|
</el-row>
|
</el-form>
|
<template #footer>
|
<el-button @click="deviceDialogVisible = false">取消</el-button>
|
<el-button type="primary" @click="saveDevice">保存</el-button>
|
</template>
|
</el-dialog>
|
</div>
|
</template>
|
|
<script setup>
|
import { ref, reactive, onMounted, onUnmounted } from 'vue'
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
import {
|
Refresh,
|
Box,
|
TrendCharts,
|
DataLine,
|
Money,
|
Plus
|
} from '@element-plus/icons-vue'
|
|
// 响应式数据
|
const lastUpdateTime = ref('')
|
const totalDevices = ref(0)
|
const dailyConsumption = ref(0)
|
const monthlyConsumption = ref(0)
|
const gasUnitPrice = ref(0)
|
const dailyTotalCost = ref(0)
|
const monthlyTotalCost = ref(0)
|
const deviceList = ref([])
|
const tableLoading = ref(false)
|
const deviceDialogVisible = ref(false)
|
const dialogTitle = ref('')
|
const deviceFormRef = ref()
|
|
// 设备表单数据
|
const deviceForm = reactive({
|
deviceCode: '',
|
deviceName: '',
|
deviceType: '',
|
specification: '',
|
designPressure: 0,
|
volume: 0
|
})
|
|
// 表单验证规则
|
const deviceRules = {
|
deviceCode: [{ required: true, message: '请输入设备编号', trigger: 'blur' }],
|
deviceName: [{ required: true, message: '请输入设备名称', trigger: 'blur' }],
|
deviceType: [{ required: true, message: '请选择设备类型', trigger: 'change' }],
|
specification: [{ required: true, message: '请输入规格型号', trigger: 'blur' }],
|
designPressure: [{ required: true, message: '请输入设计压力', trigger: 'blur' }],
|
volume: [{ required: true, message: '请输入容积', trigger: 'blur' }]
|
}
|
|
// 定时器
|
let updateTimer = null
|
|
// 模拟数据生成
|
const generateMockData = () => {
|
// 更新统计数据
|
totalDevices.value = Math.floor(Math.random() * 10) + 15 // 15-25台设备
|
dailyConsumption.value = Math.floor(Math.random() * 100) + 200 // 200-300 m³
|
monthlyConsumption.value = Math.floor(Math.random() * 2000) + 5000 // 5000-7000 m³
|
gasUnitPrice.value = (Math.random() * 2 + 3).toFixed(2) // 3-5元/m³
|
|
// 计算费用
|
dailyTotalCost.value = dailyConsumption.value * gasUnitPrice.value
|
monthlyTotalCost.value = monthlyConsumption.value * gasUnitPrice.value
|
|
// 更新设备列表数据
|
deviceList.value = Array.from({ length: totalDevices.value }, (_, index) => ({
|
id: index + 1,
|
deviceCode: `GT${String(index + 1).padStart(3, '0')}`,
|
deviceName: `储气罐${index + 1}`,
|
deviceType: ['液化气储罐', '压缩气储罐', '天然气储罐', '氧气储罐'][Math.floor(Math.random() * 4)],
|
specification: `${Math.floor(Math.random() * 50) + 50}m³`,
|
currentPressure: (Math.random() * 2 + 0.5).toFixed(2),
|
currentTemperature: (Math.random() * 20 + 15).toFixed(1),
|
gasConcentration: (Math.random() * 10).toFixed(2),
|
status: ['running', 'stopped', 'warning', 'error'][Math.floor(Math.random() * 4)],
|
lastUpdate: new Date().toLocaleString()
|
}))
|
|
// 更新最后更新时间
|
lastUpdateTime.value = new Date().toLocaleString()
|
}
|
|
// 获取压力状态样式
|
const getPressureClass = (pressure) => {
|
const p = parseFloat(pressure)
|
if (p < 0.8) return 'pressure-low'
|
if (p > 1.5) return 'pressure-high'
|
return 'pressure-normal'
|
}
|
|
// 获取温度状态样式
|
const getTemperatureClass = (temperature) => {
|
const t = parseFloat(temperature)
|
if (t < 10 || t > 35) return 'temperature-warning'
|
return 'temperature-normal'
|
}
|
|
// 获取浓度状态样式
|
const getConcentrationClass = (concentration) => {
|
const c = parseFloat(concentration)
|
if (c > 5) return 'concentration-warning'
|
return 'concentration-normal'
|
}
|
|
// 获取状态类型
|
const getStatusType = (status) => {
|
const statusMap = {
|
running: 'success',
|
stopped: 'info',
|
warning: 'warning',
|
error: 'danger'
|
}
|
return statusMap[status] || 'info'
|
}
|
|
// 获取状态文本
|
const getStatusText = (status) => {
|
const statusMap = {
|
running: '运行中',
|
stopped: '已停止',
|
warning: '警告',
|
error: '故障'
|
}
|
return statusMap[status] || '未知'
|
}
|
|
// 刷新数据
|
const refreshData = () => {
|
generateMockData()
|
ElMessage.success('数据已刷新')
|
}
|
|
// 添加设备
|
const addDevice = () => {
|
dialogTitle.value = '添加设备'
|
Object.keys(deviceForm).forEach(key => {
|
deviceForm[key] = key === 'designPressure' || key === 'volume' ? 0 : ''
|
})
|
deviceDialogVisible.value = true
|
}
|
|
// 编辑设备
|
const editDevice = (row) => {
|
dialogTitle.value = '编辑设备'
|
Object.keys(deviceForm).forEach(key => {
|
if (row[key] !== undefined) {
|
deviceForm[key] = row[key]
|
}
|
})
|
deviceDialogVisible.value = true
|
}
|
|
|
|
// 保存设备
|
const saveDevice = () => {
|
deviceFormRef.value.validate((valid) => {
|
if (valid) {
|
ElMessage.success('保存成功')
|
deviceDialogVisible.value = false
|
refreshData()
|
}
|
})
|
}
|
|
|
|
// 启动定时更新
|
const startAutoUpdate = () => {
|
updateTimer = setInterval(() => {
|
generateMockData()
|
}, 60000) // 每分钟更新一次
|
}
|
|
// 停止定时更新
|
const stopAutoUpdate = () => {
|
if (updateTimer) {
|
clearInterval(updateTimer)
|
updateTimer = null
|
}
|
}
|
|
// 组件挂载
|
onMounted(() => {
|
generateMockData()
|
startAutoUpdate()
|
})
|
|
// 组件卸载
|
onUnmounted(() => {
|
stopAutoUpdate()
|
})
|
</script>
|
|
<style lang="scss" scoped>
|
.app-container {
|
padding: 20px;
|
background: #f5f5f5;
|
min-height: 100vh;
|
}
|
|
.page-header {
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
margin-bottom: 20px;
|
padding: 20px;
|
background: white;
|
border-radius: 8px;
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
h2 {
|
margin: 0;
|
color: #303133;
|
font-size: 24px;
|
}
|
|
.header-info {
|
display: flex;
|
align-items: center;
|
gap: 15px;
|
|
.update-time {
|
color: #909399;
|
font-size: 14px;
|
}
|
}
|
}
|
|
.stats-cards {
|
margin-bottom: 20px;
|
|
.stat-card {
|
.stat-content {
|
display: flex;
|
align-items: center;
|
padding: 10px;
|
|
.stat-icon {
|
width: 60px;
|
height: 60px;
|
border-radius: 50%;
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
margin-right: 15px;
|
|
&.gas-device {
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
color: white;
|
}
|
|
&.daily-consumption {
|
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
|
color: white;
|
}
|
|
&.monthly-consumption {
|
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
|
color: white;
|
}
|
|
&.gas-price {
|
background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%);
|
color: white;
|
}
|
}
|
|
.stat-info {
|
.stat-value {
|
font-size: 24px;
|
font-weight: bold;
|
color: #303133;
|
line-height: 1;
|
}
|
|
.stat-label {
|
font-size: 14px;
|
color: #909399;
|
margin-top: 5px;
|
}
|
}
|
}
|
}
|
}
|
|
.cost-stats {
|
margin-bottom: 20px;
|
|
.cost-card {
|
.card-header {
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
}
|
|
.cost-content {
|
text-align: center;
|
padding: 20px 0;
|
|
.cost-main {
|
margin-bottom: 15px;
|
|
.cost-amount {
|
font-size: 36px;
|
font-weight: bold;
|
color: #409eff;
|
}
|
|
.cost-unit {
|
font-size: 16px;
|
color: #909399;
|
margin-left: 5px;
|
}
|
}
|
|
.cost-details {
|
.cost-item {
|
display: flex;
|
justify-content: space-between;
|
margin-bottom: 8px;
|
font-size: 14px;
|
color: #606266;
|
|
&:last-child {
|
margin-bottom: 0;
|
}
|
}
|
}
|
}
|
}
|
}
|
|
.device-section {
|
.card-header {
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
|
.header-actions {
|
display: flex;
|
gap: 10px;
|
}
|
}
|
}
|
|
// 状态样式
|
.pressure-low {
|
color: #e6a23c;
|
font-weight: bold;
|
}
|
|
.pressure-normal {
|
color: #67c23a;
|
font-weight: bold;
|
}
|
|
.pressure-high {
|
color: #f56c6c;
|
font-weight: bold;
|
}
|
|
.temperature-normal {
|
color: #67c23a;
|
font-weight: bold;
|
}
|
|
.temperature-warning {
|
color: #e6a23c;
|
font-weight: bold;
|
}
|
|
.concentration-normal {
|
color: #67c23a;
|
font-weight: bold;
|
}
|
|
.concentration-warning {
|
color: #f56c6c;
|
font-weight: bold;
|
}
|
</style>
|