From f7165c61dd8a808132c09096f5a367565387f3e4 Mon Sep 17 00:00:00 2001
From: spring <2396852758@qq.com>
Date: 星期五, 29 八月 2025 11:59:08 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/dev_JLMY' into dev_JLMY
---
src/views/warehouseManagement/index.vue | 13
src/views/warehouseManagement/KeyCoalLocks/index.vue | 1138 +++++++++++++++++++++++++++++
src/views/warehouseManagement/stockWarning/index.vue | 1138 +++++++++++++++++++++++++++++
3 files changed, 2,289 insertions(+), 0 deletions(-)
diff --git a/src/views/warehouseManagement/KeyCoalLocks/index.vue b/src/views/warehouseManagement/KeyCoalLocks/index.vue
new file mode 100644
index 0000000..45f77b4
--- /dev/null
+++ b/src/views/warehouseManagement/KeyCoalLocks/index.vue
@@ -0,0 +1,1138 @@
+<template>
+ <div class="app-container">
+ <!-- 鎼滅储琛ㄥ崟 -->
+ <div class="search_form">
+ <el-form :model="searchForm" :inline="true">
+ <el-form-item label="鍌ㄦ皵缃愬悕绉帮細">
+ <el-input v-model="searchForm.tankName" placeholder="璇疯緭鍏ュ偍姘旂綈鍚嶇О" clearable style="width: 200px" />
+ </el-form-item>
+ <el-form-item label="鍌ㄦ皵缃愮被鍨嬶細">
+ <el-select v-model="searchForm.tankType" placeholder="璇烽�夋嫨鍌ㄦ皵缃愮被鍨�" clearable style="width: 200px">
+ <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="棰勮绫诲瀷锛�">
+ <el-select v-model="searchForm.warningType" placeholder="璇烽�夋嫨棰勮绫诲瀷" clearable style="width: 200px">
+ <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="棰勮绾у埆锛�">
+ <el-select v-model="searchForm.warningLevel" placeholder="璇烽�夋嫨棰勮绾у埆" clearable style="width: 200px">
+ <el-option label="绱ф��" value="绱ф��" />
+ <el-option label="閲嶈" value="閲嶈" />
+ <el-option label="涓�鑸�" value="涓�鑸�" />
+ </el-select>
+ </el-form-item>
+ <el-form-item>
+ <el-button type="primary" @click="handleQuery">鎼滅储</el-button>
+ <el-button @click="resetQuery">閲嶇疆</el-button>
+ </el-form-item>
+ </el-form>
+ </div>
+
+ <!-- 鏁版嵁琛ㄦ牸 -->
+ <div class="table_list">
+ <!-- 鎿嶄綔鎸夐挳 -->
+ <div class="table-operations">
+ <el-button type="primary" @click="handleAdd">鏂板棰勮瑙勫垯</el-button>
+ <el-button type="success" @click="handleBatchProcess">鎵归噺澶勭悊</el-button>
+ <el-button @click="handleExport">瀵煎嚭</el-button>
+ </div>
+ <el-table
+ :data="tableData"
+ border
+ v-loading="tableLoading"
+ @selection-change="handleSelectionChange"
+ style="width: 100%"
+ height="calc(100vh - 280px)"
+ >
+ <el-table-column align="center" type="selection" width="55" />
+ <el-table-column align="center" label="搴忓彿" type="index" width="60" />
+
+ <!-- 鍩虹淇℃伅瀛楁 -->
+ <el-table-column label="鍌ㄦ皵缃愮紪鐮�" prop="tankCode" width="120" show-overflow-tooltip />
+ <el-table-column label="鍌ㄦ皵缃愬悕绉�" prop="tankName" width="200" show-overflow-tooltip />
+ <el-table-column label="鍌ㄦ皵缃愮被鍨�" prop="tankType" width="120" show-overflow-tooltip />
+ <el-table-column label="瑙勬牸鍨嬪彿" prop="specificationModel" width="150" show-overflow-tooltip />
+ <el-table-column label="瀹圭Н(m鲁)" prop="volume" width="100" show-overflow-tooltip />
+
+ <!-- 搴撳瓨鐩稿叧瀛楁 -->
+ <el-table-column label="褰撳墠姘斾綋閲�" prop="currentGasLevel" width="120" show-overflow-tooltip>
+ <template #default="scope">
+ <span :class="getGasLevelClass(scope.row)">{{ scope.row.currentGasLevel }}%</span>
+ </template>
+ </el-table-column>
+ <el-table-column label="瀹夊叏姘斾綋閲�" prop="safetyGasLevel" width="120" show-overflow-tooltip />
+ <el-table-column label="鏈�浣庢皵浣撻噺" prop="minGasLevel" width="120" show-overflow-tooltip />
+ <el-table-column label="鏈�楂樻皵浣撻噺" prop="maxGasLevel" width="120" show-overflow-tooltip />
+ <el-table-column label="褰撳墠鍘嬪姏(MPa)" prop="currentPressure" width="140" show-overflow-tooltip />
+
+ <!-- 棰勮瑙勫垯瀛楁 -->
+ <el-table-column label="棰勮绫诲瀷" prop="warningType" width="100" show-overflow-tooltip>
+ <template #default="scope">
+ <el-tag :type="getWarningTypeTag(scope.row.warningType)">
+ {{ scope.row.warningType }}
+ </el-tag>
+ </template>
+ </el-table-column>
+ <el-table-column label="棰勮绾у埆" prop="warningLevel" width="100" show-overflow-tooltip>
+ <template #default="scope">
+ <el-tag :type="getWarningLevelTag(scope.row.warningLevel)">
+ {{ scope.row.warningLevel }}
+ </el-tag>
+ </template>
+ </el-table-column>
+ <el-table-column label="棰勮闃堝��" prop="warningThreshold" width="100" show-overflow-tooltip />
+ <el-table-column label="鏄惁鍚敤" prop="isEnabled" width="100" show-overflow-tooltip>
+ <template #default="scope">
+ <el-switch v-model="scope.row.isEnabled" @change="handleEnableChange(scope.row)" />
+ </template>
+ </el-table-column>
+
+ <!-- 鏃堕棿鐩稿叧瀛楁 -->
+ <el-table-column label="棰勮鏃堕棿" prop="warningTime" width="150" show-overflow-tooltip />
+ <el-table-column label="棰勮鎸佺画澶╂暟" prop="warningDuration" width="120" show-overflow-tooltip />
+ <el-table-column label="鏈�鍚庢洿鏂版椂闂�" prop="lastUpdateTime" width="150" show-overflow-tooltip />
+ <el-table-column label="棰勮鍏呰鏃堕棿" prop="expectedRefillTime" width="150" show-overflow-tooltip />
+ <el-table-column label="棰勮缂烘皵鏃堕棿" prop="expectedShortageTime" width="150" show-overflow-tooltip>
+ <template #default="scope">
+ <div v-if="scope.row.expectedShortageTime">
+ <div v-if="getCountdown(scope.row.expectedShortageTime).isExpired" class="countdown-expired">
+ <el-tag type="danger">宸茬己姘�</el-tag>
+ </div>
+ <div v-else class="countdown-timer">
+ <span :class="getCountdownClass(scope.row.expectedShortageTime)">
+ {{ getCountdown(scope.row.expectedShortageTime).text }}
+ </span>
+ </div>
+ </div>
+ <span v-else>-</span>
+ </template>
+ </el-table-column>
+
+ <!-- 鎿嶄綔鍒� -->
+ <el-table-column fixed="right" label="鎿嶄綔" width="200" align="center">
+ <template #default="scope">
+ <el-button link type="primary" size="small" @click="handleEdit(scope.row)">缂栬緫</el-button>
+ <el-button link type="success" size="small" @click="handleProcess(scope.row)">澶勭悊</el-button>
+ <el-button link type="danger" size="small" @click="handleDelete(scope.row)">鍒犻櫎</el-button>
+ </template>
+ </el-table-column>
+ </el-table>
+
+ <!-- 鍒嗛〉 -->
+ <pagination
+ v-show="total > 0"
+ :total="total"
+ layout="total, sizes, prev, pager, next, jumper"
+ :page="page.current"
+ :limit="page.size"
+ @pagination="paginationChange"
+ />
+ </div>
+
+ <!-- 鏂板/缂栬緫棰勮瑙勫垯寮圭獥 -->
+ <el-dialog
+ v-model="dialogFormVisible"
+ :title="operationType === 'add' ? '鏂板棰勮瑙勫垯' : '缂栬緫棰勮瑙勫垯'"
+ width="50%"
+ @close="closeDialog"
+ >
+ <el-form :model="form" :rules="rules" ref="formRef" label-width="140px">
+ <el-row :gutter="20">
+ <!-- 鍩虹淇℃伅 -->
+ <el-col :span="12">
+ <el-form-item label="鍌ㄦ皵缃愮紪鐮侊細" prop="tankCode">
+ <el-input v-model="form.tankCode" placeholder="璇疯緭鍏ュ偍姘旂綈缂栫爜" />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="鍌ㄦ皵缃愬悕绉帮細" prop="tankName">
+ <el-input v-model="form.tankName" placeholder="璇疯緭鍏ュ偍姘旂綈鍚嶇О" />
+ </el-form-item>
+ </el-col>
+ </el-row>
+
+ <el-row :gutter="20">
+ <el-col :span="12">
+ <el-form-item label="鍌ㄦ皵缃愮被鍨嬶細" prop="tankType">
+ <el-select v-model="form.tankType" 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-col>
+ <el-col :span="12">
+ <el-form-item label="瑙勬牸鍨嬪彿锛�" prop="specificationModel">
+ <el-input v-model="form.specificationModel" placeholder="璇疯緭鍏ヨ鏍煎瀷鍙�" />
+ </el-form-item>
+ </el-col>
+ </el-row>
+
+ <el-row :gutter="20">
+ <el-col :span="12">
+ <el-form-item label="瀹圭Н(m鲁)锛�" prop="volume">
+ <el-input-number v-model="form.volume" :min="0" :precision="2" style="width: 100%" />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="褰撳墠姘斾綋閲�(%)锛�" prop="currentGasLevel">
+ <el-input-number v-model="form.currentGasLevel" :min="0" :max="100" :precision="1" style="width: 100%" />
+ </el-form-item>
+ </el-col>
+ </el-row>
+
+ <!-- 搴撳瓨鐩稿叧 -->
+ <el-row :gutter="20">
+ <el-col :span="12">
+ <el-form-item label="瀹夊叏姘斾綋閲�(%)锛�" prop="safetyGasLevel">
+ <el-input-number v-model="form.safetyGasLevel" :min="0" :max="100" :precision="1" style="width: 100%" />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="鏈�浣庢皵浣撻噺(%)锛�" prop="minGasLevel">
+ <el-input-number v-model="form.minGasLevel" :min="0" :max="100" :precision="1" style="width: 100%" />
+ </el-form-item>
+ </el-col>
+ </el-row>
+
+ <el-row :gutter="20">
+ <el-col :span="12">
+ <el-form-item label="鏈�楂樻皵浣撻噺(%)锛�" prop="maxGasLevel">
+ <el-input-number v-model="form.maxGasLevel" :min="0" :max="100" :precision="1" style="width: 100%" />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="褰撳墠鍘嬪姏(MPa)锛�" prop="currentPressure">
+ <el-input-number v-model="form.currentPressure" :min="0" :precision="2" style="width: 100%" />
+ </el-form-item>
+ </el-col>
+ </el-row>
+
+ <!-- 棰勮瑙勫垯 -->
+ <el-row :gutter="20">
+ <el-col :span="12">
+ <el-form-item label="棰勮绫诲瀷锛�" prop="warningType">
+ <el-select v-model="form.warningType" 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-col>
+ <el-col :span="12">
+ <el-form-item label="棰勮绾у埆锛�" prop="warningLevel">
+ <el-select v-model="form.warningLevel" placeholder="璇烽�夋嫨棰勮绾у埆" style="width: 100%">
+ <el-option label="绱ф��" value="绱ф��" />
+ <el-option label="閲嶈" value="閲嶈" />
+ <el-option label="涓�鑸�" value="涓�鑸�" />
+ </el-select>
+ </el-form-item>
+ </el-col>
+ </el-row>
+
+ <el-row :gutter="20">
+ <el-col :span="12">
+ <el-form-item label="棰勮闃堝�硷細" prop="warningThreshold">
+ <el-input-number v-model="form.warningThreshold" :min="0" :precision="2" style="width: 100%" />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="鏄惁鍚敤锛�" prop="isEnabled">
+ <el-switch v-model="form.isEnabled" />
+ </el-form-item>
+ </el-col>
+ </el-row>
+
+ <!-- 鏃堕棿鐩稿叧 -->
+ <el-row :gutter="20">
+ <el-col :span="12">
+ <el-form-item label="棰勮鏃堕棿锛�" prop="warningTime">
+ <el-date-picker
+ v-model="form.warningTime"
+ type="datetime"
+ placeholder="璇烽�夋嫨棰勮鏃堕棿"
+ style="width: 100%"
+ value-format="YYYY-MM-DD HH:mm:ss"
+ />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="棰勮鍏呰鏃堕棿锛�" prop="expectedRefillTime">
+ <el-date-picker
+ v-model="form.expectedRefillTime"
+ type="datetime"
+ placeholder="璇烽�夋嫨棰勮鍏呰鏃堕棿"
+ style="width: 100%"
+ value-format="YYYY-MM-DD HH:mm:ss"
+ />
+ </el-form-item>
+ </el-col>
+ </el-row>
+
+ <el-row :gutter="20">
+ <el-col :span="12">
+ <el-form-item label="棰勮缂烘皵鏃堕棿锛�" prop="expectedShortageTime">
+ <el-date-picker
+ v-model="form.expectedShortageTime"
+ type="datetime"
+ placeholder="璇烽�夋嫨棰勮缂烘皵鏃堕棿"
+ style="width: 100%"
+ value-format="YYYY-MM-DD HH:mm:ss"
+ />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="棰勮瑙勫垯鎻忚堪锛�" prop="warningRule">
+ <el-input
+ v-model="form.warningRule"
+ type="textarea"
+ :rows="3"
+ placeholder="璇疯緭鍏ラ璀﹁鍒欐弿杩�"
+ />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ </el-form>
+
+ <template #footer>
+ <div class="dialog-footer">
+ <el-button @click="closeDialog">鍙栨秷</el-button>
+ <el-button type="primary" @click="submitForm">纭</el-button>
+ </div>
+ </template>
+ </el-dialog>
+
+ <!-- 缂烘皵棰勮寮规 -->
+ <el-dialog
+ v-model="shortageWarningVisible"
+ title="鈿狅笍 缂烘皵棰勮"
+ width="400px"
+ :close-on-click-modal="false"
+ :close-on-press-escape="false"
+ :show-close="false"
+ >
+ <div class="shortage-warning-content">
+ <div class="warning-icon">
+ <el-icon size="48" color="#f56c6c"><WarningFilled /></el-icon>
+ </div>
+ <div class="warning-message">
+ <h3>{{ currentWarningTank.tankName }}</h3>
+ <p>鍌ㄦ皵缃愬凡缂烘皵锛岃鍙婃椂澶勭悊锛�</p>
+ <p class="warning-details">
+ 鍌ㄦ皵缃愮紪鐮侊細{{ currentWarningTank.tankCode }}<br>
+ 鍌ㄦ皵缃愮被鍨嬶細{{ currentWarningTank.tankType }}<br>
+ 褰撳墠姘斾綋閲忥細{{ currentWarningTank.currentGasLevel }}%
+ </p>
+ </div>
+ </div>
+ <template #footer>
+ <div class="dialog-footer">
+ <el-button type="primary" @click="handleShortageWarning">绔嬪嵆澶勭悊</el-button>
+ <el-button @click="closeShortageWarning">绋嶅悗澶勭悊</el-button>
+ </div>
+ </template>
+ </el-dialog>
+
+ <!-- 缂烘皵棰勮寮规 -->
+ <el-dialog
+ v-model="shortageWarningVisible"
+ title="鈿狅笍 缂烘皵棰勮"
+ width="400px"
+ :close-on-click-modal="false"
+ :close-on-press-escape="false"
+ :show-close="false"
+ >
+ <div class="shortage-warning-content">
+ <div class="warning-icon">
+ <el-icon size="48" color="#f56c6c"><WarningFilled /></el-icon>
+ </div>
+ <div class="warning-message">
+ <h3>{{ currentWarningTank.tankName }}</h3>
+ <p>鍌ㄦ皵缃愬凡缂烘皵锛岃鍙婃椂澶勭悊锛�</p>
+ <p class="warning-details">
+ 鍌ㄦ皵缃愮紪鐮侊細{{ currentWarningTank.tankCode }}<br>
+ 鍌ㄦ皵缃愮被鍨嬶細{{ currentWarningTank.tankType }}<br>
+ 褰撳墠姘斾綋閲忥細{{ currentWarningTank.currentGasLevel }}%
+ </p>
+ </div>
+ </div>
+ <template #footer>
+ <div class="dialog-footer">
+ <el-button type="primary" @click="handleShortageWarning">绔嬪嵆澶勭悊</el-button>
+ <el-button @click="closeShortageWarning">绋嶅悗澶勭悊</el-button>
+ </div>
+ </template>
+ </el-dialog>
+ </div>
+</template>
+
+<script setup>
+import { ref, reactive, onMounted, onUnmounted } from 'vue'
+import { ElMessage, ElMessageBox } from 'element-plus'
+import { WarningFilled } from '@element-plus/icons-vue'
+import pagination from '@/components/PIMTable/Pagination.vue'
+// 娉ㄩ噴鎺堿PI瀵煎叆锛屼娇鐢ㄥ亣鏁版嵁
+// import {
+// getStockWarningPage,
+// addStockWarning,
+// updateStockWarning,
+// deleteStockWarning,
+// batchProcessStockWarning,
+// exportStockWarning,
+// toggleStockWarningStatus
+// } from '@/api/inventoryManagement/stockWarning.js'
+
+const { proxy } = getCurrentInstance()
+
+// 鍝嶅簲寮忔暟鎹�
+const tableData = ref([])
+const tableLoading = ref(false)
+const selectedRows = ref([])
+const dialogFormVisible = ref(false)
+const operationType = ref('add')
+const total = ref(0)
+
+// 缂烘皵棰勮鐩稿叧
+const shortageWarningVisible = ref(false)
+const currentWarningTank = ref({})
+const countdownTimer = ref(null)
+
+// 鍒嗛〉鍙傛暟
+const page = reactive({
+ current: 1,
+ size: 10
+})
+
+// 鎼滅储琛ㄥ崟
+const searchForm = reactive({
+ tankName: '',
+ tankType: '',
+ warningType: '',
+ warningLevel: ''
+})
+
+// 琛ㄥ崟鏁版嵁
+const form = reactive({
+ id: null,
+ tankCode: '',
+ tankName: '',
+ tankType: '',
+ specificationModel: '',
+ volume: 0,
+ currentGasLevel: 0,
+ safetyGasLevel: 0,
+ minGasLevel: 0,
+ maxGasLevel: 0,
+ currentPressure: 0,
+ warningType: '',
+ warningLevel: '',
+ warningThreshold: 0,
+ isEnabled: true,
+ warningTime: '',
+ warningDuration: 0,
+ lastUpdateTime: '',
+ expectedRefillTime: '',
+ expectedShortageTime: '',
+ warningRule: ''
+})
+
+// 琛ㄥ崟楠岃瘉瑙勫垯
+const rules = {
+ tankCode: [{ required: true, message: '璇疯緭鍏ュ偍姘旂綈缂栫爜', trigger: 'blur' }],
+ tankName: [{ required: true, message: '璇疯緭鍏ュ偍姘旂綈鍚嶇О', trigger: 'blur' }],
+ tankType: [{ required: true, message: '璇烽�夋嫨鍌ㄦ皵缃愮被鍨�', trigger: 'change' }],
+ warningType: [{ required: true, message: '璇烽�夋嫨棰勮绫诲瀷', trigger: 'change' }],
+ warningLevel: [{ required: true, message: '璇烽�夋嫨棰勮绾у埆', trigger: 'change' }],
+ warningThreshold: [{ required: true, message: '璇疯緭鍏ラ璀﹂槇鍊�', trigger: 'blur' }]
+}
+
+// 鑾峰彇鍊掕鏃朵俊鎭�
+const getCountdown = (expectedTime) => {
+ if (!expectedTime) return { text: '-', isExpired: false }
+
+ const now = new Date().getTime()
+ const expected = new Date(expectedTime).getTime()
+ const diff = expected - now
+
+ if (diff <= 0) {
+ return { text: '宸茬己姘�', isExpired: true }
+ }
+
+ const days = Math.floor(diff / (1000 * 60 * 60 * 24))
+ const hours = Math.floor((diff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60))
+ const minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60))
+
+ if (days > 0) {
+ return { text: `${days}澶�${hours}灏忔椂`, isExpired: false }
+ } else if (hours > 0) {
+ return { text: `${hours}灏忔椂${minutes}鍒嗛挓`, isExpired: false }
+ } else {
+ return { text: `${minutes}鍒嗛挓`, isExpired: false }
+ }
+}
+
+// 鑾峰彇鍊掕鏃舵牱寮忕被
+const getCountdownClass = (expectedTime) => {
+ if (!expectedTime) return ''
+
+ const now = new Date().getTime()
+ const expected = new Date(expectedTime).getTime()
+ const diff = expected - now
+
+ if (diff <= 0) {
+ return 'countdown-expired'
+ } else if (diff <= 24 * 60 * 60 * 1000) { // 24灏忔椂鍐�
+ return 'countdown-urgent'
+ } else if (diff <= 7 * 24 * 60 * 60 * 1000) { // 7澶╁唴
+ return 'countdown-warning'
+ } else {
+ return 'countdown-normal'
+ }
+}
+
+// 妫�鏌ョ己姘旈璀�
+const checkShortageWarnings = () => {
+ tableData.value.forEach(tank => {
+ if (tank.expectedShortageTime) {
+ const countdown = getCountdown(tank.expectedShortageTime)
+ if (countdown.isExpired && !tank.warningShown) {
+ // 鏍囪宸叉樉绀洪璀︼紝閬垮厤閲嶅寮规
+ tank.warningShown = true
+ showShortageWarning(tank)
+ }
+ }
+ })
+}
+
+// 鏄剧ず缂烘皵棰勮寮规
+const showShortageWarning = (tank) => {
+ currentWarningTank.value = tank
+ shortageWarningVisible.value = true
+
+ // 鎾斁鎻愮ず闊筹紙鍙�夛級
+ // const audio = new Audio('/path/to/warning-sound.mp3')
+ // audio.play()
+}
+
+// 澶勭悊缂烘皵棰勮
+const handleShortageWarning = () => {
+ ElMessage.success(`姝e湪澶勭悊鍌ㄦ皵缃� ${currentWarningTank.value.tankName} 鐨勭己姘旈棶棰榒)
+ shortageWarningVisible.value = false
+ // 杩欓噷鍙互璋冪敤澶勭悊API
+}
+// 澶勭悊缂烘皵棰勮
+const closeShortageWarning = () => {
+ // ElMessage.success(`姝e湪澶勭悊鍌ㄦ皵缃� ${currentWarningTank.value.tankName} 鐨勭己姘旈棶棰榒)
+ shortageWarningVisible.value = false
+ // 杩欓噷鍙互璋冪敤澶勭悊API
+}
+
+
+
+// 鐢熸垚鍋囨暟鎹�
+const generateMockData = () => {
+ const mockData = [
+ {
+ id: 1,
+ tankCode: 'TANK001',
+ tankName: '娑插寲姘斿偍缃怉',
+ tankType: '娑插寲姘斿偍缃�',
+ specificationModel: 'LPG-5000L',
+ volume: 5000,
+ currentGasLevel: 15,
+ safetyGasLevel: 30,
+ minGasLevel: 10,
+ maxGasLevel: 95,
+ currentPressure: 2.5,
+ warningType: '姘斾綋涓嶈冻',
+ warningLevel: '绱ф��',
+ warningThreshold: 20,
+ isEnabled: true,
+ warningTime: '2024-01-15 08:30:00',
+ warningDuration: 3,
+ lastUpdateTime: '2024-01-15 10:00:00',
+ expectedRefillTime: '2024-01-16 14:00:00',
+ expectedShortageTime: '2024-01-15 18:30:00', // 浠婂ぉ涓嬪崍6:30缂烘皵
+ warningRule: '褰撴皵浣撻噺浣庝簬20%鏃惰Е鍙戦璀�'
+ },
+ {
+ id: 2,
+ tankCode: 'TANK002',
+ tankName: '鍘嬬缉姘斿偍缃怋',
+ tankType: '鍘嬬缉姘斿偍缃�',
+ specificationModel: 'COMP-3000L',
+ volume: 3000,
+ currentGasLevel: 45,
+ safetyGasLevel: 25,
+ minGasLevel: 15,
+ maxGasLevel: 90,
+ currentPressure: 8.2,
+ warningType: '鍘嬪姏寮傚父',
+ warningLevel: '閲嶈',
+ warningThreshold: 10,
+ isEnabled: true,
+ warningTime: '2024-01-14 16:20:00',
+ warningDuration: 2,
+ lastUpdateTime: '2024-01-15 09:15:00',
+ expectedRefillTime: '2024-01-17 09:00:00',
+ expectedShortageTime: '2024-01-18 12:00:00', // 3澶╁悗缂烘皵
+ warningRule: '褰撳帇鍔涜秴杩�8MPa鏃惰Е鍙戦璀�'
+ },
+ {
+ id: 3,
+ tankCode: 'TANK003',
+ tankName: '澶╃劧姘斿偍缃怌',
+ tankType: '澶╃劧姘斿偍缃�',
+ specificationModel: 'NG-8000L',
+ volume: 8000,
+ currentGasLevel: 75,
+ safetyGasLevel: 20,
+ minGasLevel: 10,
+ maxGasLevel: 95,
+ currentPressure: 4.8,
+ warningType: '娓╁害寮傚父',
+ warningLevel: '涓�鑸�',
+ warningThreshold: 5,
+ isEnabled: true,
+ warningTime: '2024-01-13 11:45:00',
+ warningDuration: 1,
+ lastUpdateTime: '2024-01-15 08:45:00',
+ expectedRefillTime: '2024-01-20 10:00:00',
+ expectedShortageTime: '2024-01-22 15:30:00', // 7澶╁悗缂烘皵
+ warningRule: '褰撴俯搴﹁秴杩�60掳C鏃惰Е鍙戦璀�'
+ },
+ {
+ id: 4,
+ tankCode: 'TANK004',
+ tankName: '姘ф皵鍌ㄧ綈D',
+ tankType: '姘ф皵鍌ㄧ綈',
+ specificationModel: 'O2-2000L',
+ volume: 2000,
+ currentGasLevel: 8,
+ safetyGasLevel: 25,
+ minGasLevel: 5,
+ maxGasLevel: 90,
+ currentPressure: 6.5,
+ warningType: '娉勬紡棰勮',
+ warningLevel: '绱ф��',
+ warningThreshold: 15,
+ isEnabled: true,
+ warningTime: '2024-01-15 07:15:00',
+ warningDuration: 4,
+ lastUpdateTime: '2024-01-15 11:30:00',
+ expectedRefillTime: '2024-01-15 16:00:00',
+ expectedShortageTime: '2024-01-15 14:00:00', // 浠婂ぉ涓嬪崍2鐐圭己姘�
+ warningRule: '褰撴娴嬪埌姘斾綋娉勬紡鏃惰Е鍙戦璀�'
+ },
+ {
+ id: 5,
+ tankCode: 'TANK005',
+ tankName: '娑插寲姘斿偍缃怑',
+ tankType: '娑插寲姘斿偍缃�',
+ specificationModel: 'LPG-6000L',
+ volume: 6000,
+ currentGasLevel: 35,
+ safetyGasLevel: 30,
+ minGasLevel: 15,
+ maxGasLevel: 95,
+ currentPressure: 3.2,
+ warningType: '姘斾綋涓嶈冻',
+ warningLevel: '閲嶈',
+ warningThreshold: 20,
+ isEnabled: false,
+ warningTime: '2024-01-14 14:30:00',
+ warningDuration: 2,
+ lastUpdateTime: '2024-01-15 09:00:00',
+ expectedRefillTime: '2024-01-19 08:00:00',
+ expectedShortageTime: '2024-01-21 10:00:00', // 6澶╁悗缂烘皵
+ warningRule: '褰撴皵浣撻噺浣庝簬20%鏃惰Е鍙戦璀�'
+ },
+ {
+ id: 6,
+ tankCode: 'TANK006',
+ tankName: '鍘嬬缉姘斿偍缃怓',
+ tankType: '鍘嬬缉姘斿偍缃�',
+ specificationModel: 'COMP-4000L',
+ volume: 4000,
+ currentGasLevel: 85,
+ safetyGasLevel: 20,
+ minGasLevel: 10,
+ maxGasLevel: 90,
+ currentPressure: 7.8,
+ warningType: '鍘嬪姏寮傚父',
+ warningLevel: '涓�鑸�',
+ warningThreshold: 8,
+ isEnabled: true,
+ warningTime: '2024-01-12 09:20:00',
+ warningDuration: 1,
+ lastUpdateTime: '2024-01-15 08:30:00',
+ expectedRefillTime: '2024-01-25 14:00:00',
+ expectedShortageTime: '2024-01-28 16:00:00', // 13澶╁悗缂烘皵
+ warningRule: '褰撳帇鍔涜秴杩�8MPa鏃惰Е鍙戦璀�'
+ },
+ {
+ id: 7,
+ tankCode: 'TANK007',
+ tankName: '澶╃劧姘斿偍缃怗',
+ tankType: '澶╃劧姘斿偍缃�',
+ specificationModel: 'NG-10000L',
+ volume: 10000,
+ currentGasLevel: 92,
+ safetyGasLevel: 15,
+ minGasLevel: 8,
+ maxGasLevel: 95,
+ currentPressure: 5.2,
+ warningType: '娓╁害寮傚父',
+ warningLevel: '閲嶈',
+ warningThreshold: 6,
+ isEnabled: true,
+ warningTime: '2024-01-11 16:45:00',
+ warningDuration: 1,
+ lastUpdateTime: '2024-01-15 07:45:00',
+ expectedRefillTime: '2024-01-30 09:00:00',
+ expectedShortageTime: '2024-02-05 12:00:00', // 21澶╁悗缂烘皵
+ warningRule: '褰撴俯搴﹁秴杩�60掳C鏃惰Е鍙戦璀�'
+ },
+ {
+ id: 8,
+ tankCode: 'TANK008',
+ tankName: '姘ф皵鍌ㄧ綈H',
+ tankType: '姘ф皵鍌ㄧ綈',
+ specificationModel: 'O2-1500L',
+ volume: 1500,
+ currentGasLevel: 12,
+ safetyGasLevel: 30,
+ minGasLevel: 8,
+ maxGasLevel: 90,
+ currentPressure: 4.5,
+ warningType: '娉勬紡棰勮',
+ warningLevel: '绱ф��',
+ warningThreshold: 12,
+ isEnabled: true,
+ warningTime: '2024-01-15 06:30:00',
+ warningDuration: 5,
+ lastUpdateTime: '2024-01-15 12:15:00',
+ expectedRefillTime: '2024-01-15 20:00:00',
+ expectedShortageTime: '2024-01-15 17:30:00', // 浠婂ぉ涓嬪崍5:30缂烘皵
+ warningRule: '褰撴娴嬪埌姘斾綋娉勬紡鏃惰Е鍙戦璀�'
+ }
+ ]
+
+ // 鏍规嵁鎼滅储鏉′欢杩囨护鏁版嵁
+ let filteredData = mockData.filter(item => {
+ if (searchForm.tankName && !item.tankName.includes(searchForm.tankName)) return false
+ if (searchForm.tankType && item.tankType !== searchForm.tankType) return false
+ if (searchForm.warningType && item.warningType !== searchForm.warningType) return false
+ if (searchForm.warningLevel && item.warningLevel !== searchForm.warningLevel) return false
+ return true
+ })
+
+ // 鍒嗛〉澶勭悊
+ const start = (page.current - 1) * page.size
+ const end = start + page.size
+ const paginatedData = filteredData.slice(start, end)
+
+ return {
+ records: paginatedData,
+ total: filteredData.length
+ }
+}
+
+// 鑾峰彇鍒楄〃鏁版嵁
+const getList = async () => {
+ tableLoading.value = true
+ try {
+ // 妯℃嫙缃戠粶寤惰繜
+ await new Promise(resolve => setTimeout(resolve, 500))
+
+ const result = generateMockData()
+ tableData.value = result.records
+ total.value = result.total
+
+ // 妫�鏌ョ己姘旈璀�
+ checkShortageWarnings()
+ } catch (error) {
+ console.error('鑾峰彇鍒楄〃澶辫触:', error)
+ ElMessage.error('鑾峰彇鍒楄〃澶辫触')
+ } finally {
+ tableLoading.value = false
+ }
+}
+
+// 鎼滅储
+const handleQuery = () => {
+ page.current = 1
+ getList()
+}
+
+// 閲嶇疆鎼滅储
+const resetQuery = () => {
+ Object.keys(searchForm).forEach(key => {
+ searchForm[key] = ''
+ })
+ handleQuery()
+}
+
+// 鍒嗛〉鍙樺寲
+const paginationChange = (obj) => {
+ page.current = obj.page
+ page.size = obj.limit
+ getList()
+}
+
+// 琛ㄦ牸閫夋嫨鍙樺寲
+const handleSelectionChange = (selection) => {
+ selectedRows.value = selection
+}
+
+// 鏂板
+const handleAdd = () => {
+ operationType.value = 'add'
+ resetForm()
+ dialogFormVisible.value = true
+}
+
+// 缂栬緫
+const handleEdit = (row) => {
+ operationType.value = 'edit'
+ Object.assign(form, row)
+ dialogFormVisible.value = true
+}
+
+// 澶勭悊棰勮
+const handleProcess = async (row) => {
+ try {
+ // 妯℃嫙API璋冪敤寤惰繜
+ await new Promise(resolve => setTimeout(resolve, 300))
+ ElMessage.success(`姝e湪澶勭悊棰勮锛�${row.tankName}`)
+ getList()
+ } catch (error) {
+ ElMessage.error('澶勭悊棰勮澶辫触')
+ }
+}
+
+// 鍒犻櫎
+const handleDelete = async (row) => {
+ try {
+ await ElMessageBox.confirm(`纭畾瑕佸垹闄ら璀﹁鍒欙細${row.tankName}鍚楋紵`, '鎻愮ず', {
+ confirmButtonText: '纭畾',
+ cancelButtonText: '鍙栨秷',
+ type: 'warning'
+ })
+
+ // 妯℃嫙API璋冪敤寤惰繜
+ await new Promise(resolve => setTimeout(resolve, 300))
+ ElMessage.success('鍒犻櫎鎴愬姛')
+ getList()
+ } catch (error) {
+ if (error !== 'cancel') {
+ ElMessage.error('鍒犻櫎澶辫触')
+ }
+ }
+}
+
+// 鎵归噺澶勭悊
+const handleBatchProcess = async () => {
+ if (selectedRows.value.length === 0) {
+ ElMessage.warning('璇烽�夋嫨瑕佸鐞嗙殑棰勮')
+ return
+ }
+
+ try {
+ // 妯℃嫙API璋冪敤寤惰繜
+ await new Promise(resolve => setTimeout(resolve, 500))
+ ElMessage.success(`鎵归噺澶勭悊浜� ${selectedRows.value.length} 鏉¢璀)
+ getList()
+ } catch (error) {
+ ElMessage.error('鎵归噺澶勭悊澶辫触')
+ }
+}
+
+// 瀵煎嚭
+const handleExport = async () => {
+ try {
+ // 妯℃嫙API璋冪敤寤惰繜
+ await new Promise(resolve => setTimeout(resolve, 800))
+
+ // 鐢熸垚瀵煎嚭鏁版嵁
+ const exportData = generateMockData().records
+ const csvContent = generateCSV(exportData)
+
+ // 鍒涘缓涓嬭浇閾炬帴
+ const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' })
+ const url = window.URL.createObjectURL(blob)
+ const link = document.createElement('a')
+ link.href = url
+ link.download = `鍌ㄦ皵缃愰璀︽暟鎹甠${new Date().getTime()}.csv`
+ link.click()
+ window.URL.revokeObjectURL(url)
+
+ ElMessage.success('瀵煎嚭鎴愬姛')
+ } catch (error) {
+ ElMessage.error('瀵煎嚭澶辫触')
+ }
+}
+
+// 鐢熸垚CSV鍐呭
+const generateCSV = (data) => {
+ const headers = [
+ '鍌ㄦ皵缃愮紪鐮�', '鍌ㄦ皵缃愬悕绉�', '鍌ㄦ皵缃愮被鍨�', '瑙勬牸鍨嬪彿', '瀹圭Н(m鲁)',
+ '褰撳墠姘斾綋閲�(%)', '瀹夊叏姘斾綋閲�(%)', '鏈�浣庢皵浣撻噺(%)', '鏈�楂樻皵浣撻噺(%)',
+ '褰撳墠鍘嬪姏(MPa)', '棰勮绫诲瀷', '棰勮绾у埆', '棰勮闃堝��', '鏄惁鍚敤',
+ '棰勮鏃堕棿', '棰勮鎸佺画澶╂暟', '鏈�鍚庢洿鏂版椂闂�', '棰勮鍏呰鏃堕棿', '棰勮缂烘皵鏃堕棿', '棰勮瑙勫垯鎻忚堪'
+ ]
+
+ const csvRows = [headers.join(',')]
+
+ data.forEach(item => {
+ const row = [
+ item.tankCode,
+ item.tankName,
+ item.tankType,
+ item.specificationModel,
+ item.volume,
+ item.currentGasLevel,
+ item.safetyGasLevel,
+ item.minGasLevel,
+ item.maxGasLevel,
+ item.currentPressure,
+ item.warningType,
+ item.warningLevel,
+ item.warningThreshold,
+ item.isEnabled ? '鏄�' : '鍚�',
+ item.warningTime,
+ item.warningDuration,
+ item.lastUpdateTime,
+ item.expectedRefillTime,
+ item.expectedShortageTime,
+ item.warningRule
+ ]
+ csvRows.push(row.join(','))
+ })
+
+ return csvRows.join('\n')
+}
+
+// 鍚敤鐘舵�佸彉鍖�
+const handleEnableChange = async (row) => {
+ try {
+ // 妯℃嫙API璋冪敤寤惰繜
+ await new Promise(resolve => setTimeout(resolve, 200))
+ ElMessage.success(`${row.tankName} 鐨勫惎鐢ㄧ姸鎬佸凡鏇存柊`)
+ } catch (error) {
+ ElMessage.error('鐘舵�佹洿鏂板け璐�')
+ // 鎭㈠鍘熺姸鎬�
+ row.isEnabled = !row.isEnabled
+ }
+}
+
+// 鎻愪氦琛ㄥ崟
+const submitForm = async () => {
+ try {
+ await proxy.$refs.formRef.validate()
+
+ // 妯℃嫙API璋冪敤寤惰繜
+ await new Promise(resolve => setTimeout(resolve, 500))
+
+ if (operationType.value === 'add') {
+ ElMessage.success('鏂板鎴愬姛')
+ } else {
+ ElMessage.success('缂栬緫鎴愬姛')
+ }
+
+ closeDialog()
+ getList()
+ } catch (error) {
+ if (!error.errors) {
+ ElMessage.error(operationType.value === 'add' ? '鏂板澶辫触' : '缂栬緫澶辫触')
+ }
+ }
+}
+
+// 鍏抽棴寮圭獥
+const closeDialog = () => {
+ dialogFormVisible.value = false
+ resetForm()
+}
+
+// 閲嶇疆琛ㄥ崟
+const resetForm = () => {
+ Object.keys(form).forEach(key => {
+ if (key === 'isEnabled') {
+ form[key] = true
+ } else if (typeof form[key] === 'number') {
+ form[key] = 0
+ } else {
+ form[key] = ''
+ }
+ })
+ proxy.$refs.formRef?.resetFields()
+}
+
+// 鑾峰彇姘斾綋閲忔牱寮忕被
+const getGasLevelClass = (row) => {
+ if (row.currentGasLevel < row.minGasLevel) {
+ return 'text-danger'
+ } else if (row.currentGasLevel > row.maxGasLevel) {
+ return 'text-warning'
+ }
+ return 'text-success'
+}
+
+// 鑾峰彇棰勮绫诲瀷鏍囩鏍峰紡
+const getWarningTypeTag = (type) => {
+ const typeMap = {
+ '姘斾綋涓嶈冻': 'danger',
+ '鍘嬪姏寮傚父': 'warning',
+ '娓╁害寮傚父': 'info',
+ '娉勬紡棰勮': 'danger'
+ }
+ return typeMap[type] || 'info'
+}
+
+// 鑾峰彇棰勮绾у埆鏍囩鏍峰紡
+const getWarningLevelTag = (level) => {
+ const levelMap = {
+ '绱ф��': 'danger',
+ '閲嶈': 'warning',
+ '涓�鑸�': 'info'
+ }
+ return levelMap[level] || 'info'
+}
+
+// 鍚姩鍊掕鏃跺畾鏃跺櫒
+const startCountdownTimer = () => {
+ countdownTimer.value = setInterval(() => {
+ checkShortageWarnings()
+ }, 60000) // 姣忓垎閽熸鏌ヤ竴娆�
+}
+
+// 鍋滄鍊掕鏃跺畾鏃跺櫒
+const stopCountdownTimer = () => {
+ if (countdownTimer.value) {
+ clearInterval(countdownTimer.value)
+ countdownTimer.value = null
+ }
+}
+
+// 椤甸潰鍔犺浇
+onMounted(() => {
+ getList()
+ startCountdownTimer()
+})
+
+// 椤甸潰鍗歌浇
+onUnmounted(() => {
+ stopCountdownTimer()
+})
+</script>
+
+<style scoped lang="scss">
+.app-container {
+ padding: 20px;
+
+ .table-operations {
+ text-align: right;
+ margin-bottom: 20px;
+
+ .el-button {
+ margin-top: 20px;
+ margin-right: 10px;
+ }
+ }
+
+ .table_list {
+ background: #fff;
+ border-radius: 4px;
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+ }
+
+ .text-danger {
+ color: #f56c6c;
+ font-weight: bold;
+ }
+
+ .text-warning {
+ color: #e6a23c;
+ font-weight: bold;
+ }
+
+ .text-success {
+ color: #67c23a;
+ font-weight: bold;
+ }
+
+ .dialog-footer {
+ text-align: right;
+ }
+
+ // 鍊掕鏃舵牱寮�
+ .countdown-timer {
+ font-weight: bold;
+ }
+
+ .countdown-normal {
+ color: #67c23a;
+ }
+
+ .countdown-warning {
+ color: #e6a23c;
+ }
+
+ .countdown-urgent {
+ color: #f56c6c;
+ animation: blink 1s infinite;
+ }
+
+ .countdown-expired {
+ color: #f56c6c;
+ font-weight: bold;
+ }
+
+ @keyframes blink {
+ 0%, 50% { opacity: 1; }
+ 51%, 100% { opacity: 0.5; }
+ }
+
+ // 缂烘皵棰勮寮规鏍峰紡
+ .shortage-warning-content {
+ text-align: center;
+ padding: 20px 0;
+
+ .warning-icon {
+ margin-bottom: 20px;
+ }
+
+ .warning-message {
+ h3 {
+ color: #f56c6c;
+ margin-bottom: 10px;
+ }
+
+ p {
+ margin-bottom: 10px;
+ color: #606266;
+ }
+
+ .warning-details {
+ background: #f5f7fa;
+ padding: 15px;
+ border-radius: 4px;
+ text-align: left;
+ font-size: 14px;
+ line-height: 1.6;
+ }
+ }
+ }
+}
+</style>
diff --git a/src/views/warehouseManagement/index.vue b/src/views/warehouseManagement/index.vue
index 6183677..1f4bcba 100644
--- a/src/views/warehouseManagement/index.vue
+++ b/src/views/warehouseManagement/index.vue
@@ -68,6 +68,7 @@
style="width: 100%; height: calc(100vh - 30em)"
show-summary
:summary-method="summarizeChildrenTable"
+ :row-class-name="tableRowClassName"
>
<el-table-column type="selection" width="55" align="center" />
<el-table-column
@@ -897,6 +898,14 @@
}
});
};
+
+// 娣诲姞鍒ゆ柇琛岀被鍚嶇殑鍑芥暟
+const tableRowClassName = (row) => {
+ if (activeTab.value === 'officialInventory' && row.row.inventoryQuantity < 3) {
+ return 'low-inventory-row';
+ }
+ return '';
+};
// 鍏抽棴瀹℃牳寮规
const cancelReview = () => {
proxy.$refs.formRef.resetFields();
@@ -908,4 +917,8 @@
:deep(.el-table) {
margin: 20px 0;
}
+
+:deep(.low-inventory-row) {
+ background-color: #ffcccc;
+}
</style>
diff --git a/src/views/warehouseManagement/stockWarning/index.vue b/src/views/warehouseManagement/stockWarning/index.vue
new file mode 100644
index 0000000..8a23ec7
--- /dev/null
+++ b/src/views/warehouseManagement/stockWarning/index.vue
@@ -0,0 +1,1138 @@
+<template>
+ <div class="app-container">
+ <!-- 鎼滅储琛ㄥ崟 -->
+ <div class="search_form">
+ <el-form :model="searchForm" :inline="true">
+ <el-form-item label="鍌ㄦ皵缃愬悕绉帮細">
+ <el-input v-model="searchForm.tankName" placeholder="璇疯緭鍏ュ偍姘旂綈鍚嶇О" clearable style="width: 200px" />
+ </el-form-item>
+ <el-form-item label="鍌ㄦ皵缃愮被鍨嬶細">
+ <el-select v-model="searchForm.tankType" placeholder="璇烽�夋嫨鍌ㄦ皵缃愮被鍨�" clearable style="width: 200px">
+ <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="棰勮绫诲瀷锛�">
+ <el-select v-model="searchForm.warningType" placeholder="璇烽�夋嫨棰勮绫诲瀷" clearable style="width: 200px">
+ <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="棰勮绾у埆锛�">
+ <el-select v-model="searchForm.warningLevel" placeholder="璇烽�夋嫨棰勮绾у埆" clearable style="width: 200px">
+ <el-option label="绱ф��" value="绱ф��" />
+ <el-option label="閲嶈" value="閲嶈" />
+ <el-option label="涓�鑸�" value="涓�鑸�" />
+ </el-select>
+ </el-form-item>
+ <el-form-item>
+ <el-button type="primary" @click="handleQuery">鎼滅储</el-button>
+ <el-button @click="resetQuery">閲嶇疆</el-button>
+ </el-form-item>
+ </el-form>
+ </div>
+
+ <!-- 鏁版嵁琛ㄦ牸 -->
+ <div class="table_list">
+ <!-- 鎿嶄綔鎸夐挳 -->
+ <div class="table-operations">
+ <el-button type="primary" @click="handleAdd">鏂板棰勮瑙勫垯</el-button>
+ <el-button type="success" @click="handleBatchProcess">鎵归噺澶勭悊</el-button>
+ <el-button @click="handleExport">瀵煎嚭</el-button>
+ </div>
+ <el-table
+ :data="tableData"
+ border
+ v-loading="tableLoading"
+ @selection-change="handleSelectionChange"
+ style="width: 100%"
+ height="calc(100vh - 280px)"
+ >
+ <el-table-column align="center" type="selection" width="55" />
+ <el-table-column align="center" label="搴忓彿" type="index" width="60" />
+
+ <!-- 鍩虹淇℃伅瀛楁 -->
+ <el-table-column label="鍌ㄦ皵缃愮紪鐮�" prop="tankCode" width="120" show-overflow-tooltip />
+ <el-table-column label="鍌ㄦ皵缃愬悕绉�" prop="tankName" width="200" show-overflow-tooltip />
+ <el-table-column label="鍌ㄦ皵缃愮被鍨�" prop="tankType" width="120" show-overflow-tooltip />
+ <el-table-column label="瑙勬牸鍨嬪彿" prop="specificationModel" width="150" show-overflow-tooltip />
+ <el-table-column label="瀹圭Н(m鲁)" prop="volume" width="100" show-overflow-tooltip />
+
+ <!-- 搴撳瓨鐩稿叧瀛楁 -->
+ <el-table-column label="褰撳墠姘斾綋閲�" prop="currentGasLevel" width="120" show-overflow-tooltip>
+ <template #default="scope">
+ <span :class="getGasLevelClass(scope.row)">{{ scope.row.currentGasLevel }}%</span>
+ </template>
+ </el-table-column>
+ <el-table-column label="瀹夊叏姘斾綋閲�" prop="safetyGasLevel" width="120" show-overflow-tooltip />
+ <el-table-column label="鏈�浣庢皵浣撻噺" prop="minGasLevel" width="120" show-overflow-tooltip />
+ <el-table-column label="鏈�楂樻皵浣撻噺" prop="maxGasLevel" width="120" show-overflow-tooltip />
+ <el-table-column label="褰撳墠鍘嬪姏(MPa)" prop="currentPressure" width="140" show-overflow-tooltip />
+
+ <!-- 棰勮瑙勫垯瀛楁 -->
+ <el-table-column label="棰勮绫诲瀷" prop="warningType" width="100" show-overflow-tooltip>
+ <template #default="scope">
+ <el-tag :type="getWarningTypeTag(scope.row.warningType)">
+ {{ scope.row.warningType }}
+ </el-tag>
+ </template>
+ </el-table-column>
+ <el-table-column label="棰勮绾у埆" prop="warningLevel" width="100" show-overflow-tooltip>
+ <template #default="scope">
+ <el-tag :type="getWarningLevelTag(scope.row.warningLevel)">
+ {{ scope.row.warningLevel }}
+ </el-tag>
+ </template>
+ </el-table-column>
+ <el-table-column label="棰勮闃堝��" prop="warningThreshold" width="100" show-overflow-tooltip />
+ <el-table-column label="鏄惁鍚敤" prop="isEnabled" width="100" show-overflow-tooltip>
+ <template #default="scope">
+ <el-switch v-model="scope.row.isEnabled" @change="handleEnableChange(scope.row)" />
+ </template>
+ </el-table-column>
+
+ <!-- 鏃堕棿鐩稿叧瀛楁 -->
+ <el-table-column label="棰勮鏃堕棿" prop="warningTime" width="150" show-overflow-tooltip />
+ <el-table-column label="棰勮鎸佺画澶╂暟" prop="warningDuration" width="120" show-overflow-tooltip />
+ <el-table-column label="鏈�鍚庢洿鏂版椂闂�" prop="lastUpdateTime" width="150" show-overflow-tooltip />
+ <el-table-column label="棰勮鍏呰鏃堕棿" prop="expectedRefillTime" width="150" show-overflow-tooltip />
+ <el-table-column label="棰勮缂烘皵鏃堕棿" prop="expectedShortageTime" width="150" show-overflow-tooltip>
+ <template #default="scope">
+ <div v-if="scope.row.expectedShortageTime">
+ <div v-if="getCountdown(scope.row.expectedShortageTime).isExpired" class="countdown-expired">
+ <el-tag type="danger">宸茬己姘�</el-tag>
+ </div>
+ <div v-else class="countdown-timer">
+ <span :class="getCountdownClass(scope.row.expectedShortageTime)">
+ {{ getCountdown(scope.row.expectedShortageTime).text }}
+ </span>
+ </div>
+ </div>
+ <span v-else>-</span>
+ </template>
+ </el-table-column>
+
+ <!-- 鎿嶄綔鍒� -->
+ <el-table-column fixed="right" label="鎿嶄綔" width="200" align="center">
+ <template #default="scope">
+ <el-button link type="primary" size="small" @click="handleEdit(scope.row)">缂栬緫</el-button>
+ <el-button link type="success" size="small" @click="handleProcess(scope.row)">澶勭悊</el-button>
+ <el-button link type="danger" size="small" @click="handleDelete(scope.row)">鍒犻櫎</el-button>
+ </template>
+ </el-table-column>
+ </el-table>
+
+ <!-- 鍒嗛〉 -->
+ <pagination
+ v-show="total > 0"
+ :total="total"
+ layout="total, sizes, prev, pager, next, jumper"
+ :page="page.current"
+ :limit="page.size"
+ @pagination="paginationChange"
+ />
+ </div>
+
+ <!-- 鏂板/缂栬緫棰勮瑙勫垯寮圭獥 -->
+ <el-dialog
+ v-model="dialogFormVisible"
+ :title="operationType === 'add' ? '鏂板棰勮瑙勫垯' : '缂栬緫棰勮瑙勫垯'"
+ width="50%"
+ @close="closeDialog"
+ >
+ <el-form :model="form" :rules="rules" ref="formRef" label-width="140px">
+ <el-row :gutter="20">
+ <!-- 鍩虹淇℃伅 -->
+ <el-col :span="12">
+ <el-form-item label="鍌ㄦ皵缃愮紪鐮侊細" prop="tankCode">
+ <el-input v-model="form.tankCode" placeholder="璇疯緭鍏ュ偍姘旂綈缂栫爜" />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="鍌ㄦ皵缃愬悕绉帮細" prop="tankName">
+ <el-input v-model="form.tankName" placeholder="璇疯緭鍏ュ偍姘旂綈鍚嶇О" />
+ </el-form-item>
+ </el-col>
+ </el-row>
+
+ <el-row :gutter="20">
+ <el-col :span="12">
+ <el-form-item label="鍌ㄦ皵缃愮被鍨嬶細" prop="tankType">
+ <el-select v-model="form.tankType" 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-col>
+ <el-col :span="12">
+ <el-form-item label="瑙勬牸鍨嬪彿锛�" prop="specificationModel">
+ <el-input v-model="form.specificationModel" placeholder="璇疯緭鍏ヨ鏍煎瀷鍙�" />
+ </el-form-item>
+ </el-col>
+ </el-row>
+
+ <el-row :gutter="20">
+ <el-col :span="12">
+ <el-form-item label="瀹圭Н(m鲁)锛�" prop="volume">
+ <el-input-number v-model="form.volume" :min="0" :precision="2" style="width: 100%" />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="褰撳墠姘斾綋閲�(%)锛�" prop="currentGasLevel">
+ <el-input-number v-model="form.currentGasLevel" :min="0" :max="100" :precision="1" style="width: 100%" />
+ </el-form-item>
+ </el-col>
+ </el-row>
+
+ <!-- 搴撳瓨鐩稿叧 -->
+ <el-row :gutter="20">
+ <el-col :span="12">
+ <el-form-item label="瀹夊叏姘斾綋閲�(%)锛�" prop="safetyGasLevel">
+ <el-input-number v-model="form.safetyGasLevel" :min="0" :max="100" :precision="1" style="width: 100%" />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="鏈�浣庢皵浣撻噺(%)锛�" prop="minGasLevel">
+ <el-input-number v-model="form.minGasLevel" :min="0" :max="100" :precision="1" style="width: 100%" />
+ </el-form-item>
+ </el-col>
+ </el-row>
+
+ <el-row :gutter="20">
+ <el-col :span="12">
+ <el-form-item label="鏈�楂樻皵浣撻噺(%)锛�" prop="maxGasLevel">
+ <el-input-number v-model="form.maxGasLevel" :min="0" :max="100" :precision="1" style="width: 100%" />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="褰撳墠鍘嬪姏(MPa)锛�" prop="currentPressure">
+ <el-input-number v-model="form.currentPressure" :min="0" :precision="2" style="width: 100%" />
+ </el-form-item>
+ </el-col>
+ </el-row>
+
+ <!-- 棰勮瑙勫垯 -->
+ <el-row :gutter="20">
+ <el-col :span="12">
+ <el-form-item label="棰勮绫诲瀷锛�" prop="warningType">
+ <el-select v-model="form.warningType" 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-col>
+ <el-col :span="12">
+ <el-form-item label="棰勮绾у埆锛�" prop="warningLevel">
+ <el-select v-model="form.warningLevel" placeholder="璇烽�夋嫨棰勮绾у埆" style="width: 100%">
+ <el-option label="绱ф��" value="绱ф��" />
+ <el-option label="閲嶈" value="閲嶈" />
+ <el-option label="涓�鑸�" value="涓�鑸�" />
+ </el-select>
+ </el-form-item>
+ </el-col>
+ </el-row>
+
+ <el-row :gutter="20">
+ <el-col :span="12">
+ <el-form-item label="棰勮闃堝�硷細" prop="warningThreshold">
+ <el-input-number v-model="form.warningThreshold" :min="0" :precision="2" style="width: 100%" />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="鏄惁鍚敤锛�" prop="isEnabled">
+ <el-switch v-model="form.isEnabled" />
+ </el-form-item>
+ </el-col>
+ </el-row>
+
+ <!-- 鏃堕棿鐩稿叧 -->
+ <el-row :gutter="20">
+ <el-col :span="12">
+ <el-form-item label="棰勮鏃堕棿锛�" prop="warningTime">
+ <el-date-picker
+ v-model="form.warningTime"
+ type="datetime"
+ placeholder="璇烽�夋嫨棰勮鏃堕棿"
+ style="width: 100%"
+ value-format="YYYY-MM-DD HH:mm:ss"
+ />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="棰勮鍏呰鏃堕棿锛�" prop="expectedRefillTime">
+ <el-date-picker
+ v-model="form.expectedRefillTime"
+ type="datetime"
+ placeholder="璇烽�夋嫨棰勮鍏呰鏃堕棿"
+ style="width: 100%"
+ value-format="YYYY-MM-DD HH:mm:ss"
+ />
+ </el-form-item>
+ </el-col>
+ </el-row>
+
+ <el-row :gutter="20">
+ <el-col :span="12">
+ <el-form-item label="棰勮缂烘皵鏃堕棿锛�" prop="expectedShortageTime">
+ <el-date-picker
+ v-model="form.expectedShortageTime"
+ type="datetime"
+ placeholder="璇烽�夋嫨棰勮缂烘皵鏃堕棿"
+ style="width: 100%"
+ value-format="YYYY-MM-DD HH:mm:ss"
+ />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="棰勮瑙勫垯鎻忚堪锛�" prop="warningRule">
+ <el-input
+ v-model="form.warningRule"
+ type="textarea"
+ :rows="3"
+ placeholder="璇疯緭鍏ラ璀﹁鍒欐弿杩�"
+ />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ </el-form>
+
+ <template #footer>
+ <div class="dialog-footer">
+ <el-button @click="closeDialog">鍙栨秷</el-button>
+ <el-button type="primary" @click="submitForm">纭</el-button>
+ </div>
+ </template>
+ </el-dialog>
+
+ <!-- 缂烘皵棰勮寮规 -->
+ <el-dialog
+ v-model="shortageWarningVisible"
+ title="鈿狅笍 缂烘皵棰勮"
+ width="400px"
+ :close-on-click-modal="false"
+ :close-on-press-escape="false"
+ :show-close="false"
+ >
+ <div class="shortage-warning-content">
+ <div class="warning-icon">
+ <el-icon size="48" color="#f56c6c"><WarningFilled /></el-icon>
+ </div>
+ <div class="warning-message">
+ <h3>{{ currentWarningTank.tankName }}</h3>
+ <p>鍌ㄦ皵缃愬凡缂烘皵锛岃鍙婃椂澶勭悊锛�</p>
+ <p class="warning-details">
+ 鍌ㄦ皵缃愮紪鐮侊細{{ currentWarningTank.tankCode }}<br>
+ 鍌ㄦ皵缃愮被鍨嬶細{{ currentWarningTank.tankType }}<br>
+ 褰撳墠姘斾綋閲忥細{{ currentWarningTank.currentGasLevel }}%
+ </p>
+ </div>
+ </div>
+ <template #footer>
+ <div class="dialog-footer">
+ <el-button type="primary" @click="handleShortageWarning">绔嬪嵆澶勭悊</el-button>
+ <el-button @click="closeShortageWarning">绋嶅悗澶勭悊</el-button>
+ </div>
+ </template>
+ </el-dialog>
+
+ <!-- 缂烘皵棰勮寮规 -->
+ <el-dialog
+ v-model="shortageWarningVisible"
+ title="鈿狅笍 缂烘皵棰勮"
+ width="400px"
+ :close-on-click-modal="false"
+ :close-on-press-escape="false"
+ :show-close="false"
+ >
+ <div class="shortage-warning-content">
+ <div class="warning-icon">
+ <el-icon size="48" color="#f56c6c"><WarningFilled /></el-icon>
+ </div>
+ <div class="warning-message">
+ <h3>{{ currentWarningTank.tankName }}</h3>
+ <p>鍌ㄦ皵缃愬凡缂烘皵锛岃鍙婃椂澶勭悊锛�</p>
+ <p class="warning-details">
+ 鍌ㄦ皵缃愮紪鐮侊細{{ currentWarningTank.tankCode }}<br>
+ 鍌ㄦ皵缃愮被鍨嬶細{{ currentWarningTank.tankType }}<br>
+ 褰撳墠姘斾綋閲忥細{{ currentWarningTank.currentGasLevel }}%
+ </p>
+ </div>
+ </div>
+ <template #footer>
+ <div class="dialog-footer">
+ <el-button type="primary" @click="handleShortageWarning">绔嬪嵆澶勭悊</el-button>
+ <el-button @click="closeShortageWarning">绋嶅悗澶勭悊</el-button>
+ </div>
+ </template>
+ </el-dialog>
+ </div>
+</template>
+
+<script setup>
+import { ref, reactive, onMounted, onUnmounted } from 'vue'
+import { ElMessage, ElMessageBox } from 'element-plus'
+import { WarningFilled } from '@element-plus/icons-vue'
+import pagination from '@/components/PIMTable/Pagination.vue'
+// 娉ㄩ噴鎺堿PI瀵煎叆锛屼娇鐢ㄥ亣鏁版嵁
+// import {
+// getStockWarningPage,
+// addStockWarning,
+// updateStockWarning,
+// deleteStockWarning,
+// batchProcessStockWarning,
+// exportStockWarning,
+// toggleStockWarningStatus
+// } from '@/api/inventoryManagement/stockWarning.js'
+
+const { proxy } = getCurrentInstance()
+
+// 鍝嶅簲寮忔暟鎹�
+const tableData = ref([])
+const tableLoading = ref(false)
+const selectedRows = ref([])
+const dialogFormVisible = ref(false)
+const operationType = ref('add')
+const total = ref(0)
+
+// 缂烘皵棰勮鐩稿叧
+const shortageWarningVisible = ref(false)
+const currentWarningTank = ref({})
+const countdownTimer = ref(null)
+
+// 鍒嗛〉鍙傛暟
+const page = reactive({
+ current: 1,
+ size: 10
+})
+
+// 鎼滅储琛ㄥ崟
+const searchForm = reactive({
+ tankName: '',
+ tankType: '',
+ warningType: '',
+ warningLevel: ''
+})
+
+// 琛ㄥ崟鏁版嵁
+const form = reactive({
+ id: null,
+ tankCode: '',
+ tankName: '',
+ tankType: '',
+ specificationModel: '',
+ volume: 0,
+ currentGasLevel: 0,
+ safetyGasLevel: 0,
+ minGasLevel: 0,
+ maxGasLevel: 0,
+ currentPressure: 0,
+ warningType: '',
+ warningLevel: '',
+ warningThreshold: 0,
+ isEnabled: true,
+ warningTime: '',
+ warningDuration: 0,
+ lastUpdateTime: '',
+ expectedRefillTime: '',
+ expectedShortageTime: '',
+ warningRule: ''
+})
+
+// 琛ㄥ崟楠岃瘉瑙勫垯
+const rules = {
+ tankCode: [{ required: true, message: '璇疯緭鍏ュ偍姘旂綈缂栫爜', trigger: 'blur' }],
+ tankName: [{ required: true, message: '璇疯緭鍏ュ偍姘旂綈鍚嶇О', trigger: 'blur' }],
+ tankType: [{ required: true, message: '璇烽�夋嫨鍌ㄦ皵缃愮被鍨�', trigger: 'change' }],
+ warningType: [{ required: true, message: '璇烽�夋嫨棰勮绫诲瀷', trigger: 'change' }],
+ warningLevel: [{ required: true, message: '璇烽�夋嫨棰勮绾у埆', trigger: 'change' }],
+ warningThreshold: [{ required: true, message: '璇疯緭鍏ラ璀﹂槇鍊�', trigger: 'blur' }]
+}
+
+// 鑾峰彇鍊掕鏃朵俊鎭�
+const getCountdown = (expectedTime) => {
+ if (!expectedTime) return { text: '-', isExpired: false }
+
+ const now = new Date().getTime()
+ const expected = new Date(expectedTime).getTime()
+ const diff = expected - now
+
+ if (diff <= 0) {
+ return { text: '宸茬己姘�', isExpired: true }
+ }
+
+ const days = Math.floor(diff / (1000 * 60 * 60 * 24))
+ const hours = Math.floor((diff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60))
+ const minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60))
+
+ if (days > 0) {
+ return { text: `${days}澶�${hours}灏忔椂`, isExpired: false }
+ } else if (hours > 0) {
+ return { text: `${hours}灏忔椂${minutes}鍒嗛挓`, isExpired: false }
+ } else {
+ return { text: `${minutes}鍒嗛挓`, isExpired: false }
+ }
+}
+
+// 鑾峰彇鍊掕鏃舵牱寮忕被
+const getCountdownClass = (expectedTime) => {
+ if (!expectedTime) return ''
+
+ const now = new Date().getTime()
+ const expected = new Date(expectedTime).getTime()
+ const diff = expected - now
+
+ if (diff <= 0) {
+ return 'countdown-expired'
+ } else if (diff <= 24 * 60 * 60 * 1000) { // 24灏忔椂鍐�
+ return 'countdown-urgent'
+ } else if (diff <= 7 * 24 * 60 * 60 * 1000) { // 7澶╁唴
+ return 'countdown-warning'
+ } else {
+ return 'countdown-normal'
+ }
+}
+
+// 妫�鏌ョ己姘旈璀�
+const checkShortageWarnings = () => {
+ tableData.value.forEach(tank => {
+ if (tank.expectedShortageTime) {
+ const countdown = getCountdown(tank.expectedShortageTime)
+ if (countdown.isExpired && !tank.warningShown) {
+ // 鏍囪宸叉樉绀洪璀︼紝閬垮厤閲嶅寮规
+ tank.warningShown = true
+ showShortageWarning(tank)
+ }
+ }
+ })
+}
+
+// 鏄剧ず缂烘皵棰勮寮规
+const showShortageWarning = (tank) => {
+ currentWarningTank.value = tank
+ shortageWarningVisible.value = true
+
+ // 鎾斁鎻愮ず闊筹紙鍙�夛級
+ // const audio = new Audio('/path/to/warning-sound.mp3')
+ // audio.play()
+}
+
+// 澶勭悊缂烘皵棰勮
+const handleShortageWarning = () => {
+ ElMessage.success(`姝e湪澶勭悊鍌ㄦ皵缃� ${currentWarningTank.value.tankName} 鐨勭己姘旈棶棰榒)
+ shortageWarningVisible.value = false
+ // 杩欓噷鍙互璋冪敤澶勭悊API
+}
+// 澶勭悊缂烘皵棰勮
+const closeShortageWarning = () => {
+ // ElMessage.success(`姝e湪澶勭悊鍌ㄦ皵缃� ${currentWarningTank.value.tankName} 鐨勭己姘旈棶棰榒)
+ shortageWarningVisible.value = false
+ // 杩欓噷鍙互璋冪敤澶勭悊API
+}
+
+
+
+// 鐢熸垚鍋囨暟鎹�
+const generateMockData = () => {
+ const mockData = [
+ {
+ id: 1,
+ tankCode: 'TANK001',
+ tankName: '娑插寲姘斿偍缃怉',
+ tankType: '娑插寲姘斿偍缃�',
+ specificationModel: 'LPG-5000L',
+ volume: 5000,
+ currentGasLevel: 15,
+ safetyGasLevel: 30,
+ minGasLevel: 10,
+ maxGasLevel: 95,
+ currentPressure: 2.5,
+ warningType: '姘斾綋涓嶈冻',
+ warningLevel: '绱ф��',
+ warningThreshold: 20,
+ isEnabled: true,
+ warningTime: '2024-01-15 08:30:00',
+ warningDuration: 3,
+ lastUpdateTime: '2024-01-15 10:00:00',
+ expectedRefillTime: '2024-01-16 14:00:00',
+ expectedShortageTime: '2024-01-15 18:30:00', // 浠婂ぉ涓嬪崍6:30缂烘皵
+ warningRule: '褰撴皵浣撻噺浣庝簬20%鏃惰Е鍙戦璀�'
+ },
+ {
+ id: 2,
+ tankCode: 'TANK002',
+ tankName: '鍘嬬缉姘斿偍缃怋',
+ tankType: '鍘嬬缉姘斿偍缃�',
+ specificationModel: 'COMP-3000L',
+ volume: 3000,
+ currentGasLevel: 45,
+ safetyGasLevel: 25,
+ minGasLevel: 15,
+ maxGasLevel: 90,
+ currentPressure: 8.2,
+ warningType: '鍘嬪姏寮傚父',
+ warningLevel: '閲嶈',
+ warningThreshold: 10,
+ isEnabled: true,
+ warningTime: '2024-01-14 16:20:00',
+ warningDuration: 2,
+ lastUpdateTime: '2024-01-15 09:15:00',
+ expectedRefillTime: '2024-01-17 09:00:00',
+ expectedShortageTime: '2024-01-18 12:00:00', // 3澶╁悗缂烘皵
+ warningRule: '褰撳帇鍔涜秴杩�8MPa鏃惰Е鍙戦璀�'
+ },
+ {
+ id: 3,
+ tankCode: 'TANK003',
+ tankName: '澶╃劧姘斿偍缃怌',
+ tankType: '澶╃劧姘斿偍缃�',
+ specificationModel: 'NG-8000L',
+ volume: 8000,
+ currentGasLevel: 75,
+ safetyGasLevel: 20,
+ minGasLevel: 10,
+ maxGasLevel: 95,
+ currentPressure: 4.8,
+ warningType: '娓╁害寮傚父',
+ warningLevel: '涓�鑸�',
+ warningThreshold: 5,
+ isEnabled: true,
+ warningTime: '2024-01-13 11:45:00',
+ warningDuration: 1,
+ lastUpdateTime: '2024-01-15 08:45:00',
+ expectedRefillTime: '2024-01-20 10:00:00',
+ expectedShortageTime: '2024-01-22 15:30:00', // 7澶╁悗缂烘皵
+ warningRule: '褰撴俯搴﹁秴杩�60掳C鏃惰Е鍙戦璀�'
+ },
+ {
+ id: 4,
+ tankCode: 'TANK004',
+ tankName: '姘ф皵鍌ㄧ綈D',
+ tankType: '姘ф皵鍌ㄧ綈',
+ specificationModel: 'O2-2000L',
+ volume: 2000,
+ currentGasLevel: 8,
+ safetyGasLevel: 25,
+ minGasLevel: 5,
+ maxGasLevel: 90,
+ currentPressure: 6.5,
+ warningType: '娉勬紡棰勮',
+ warningLevel: '绱ф��',
+ warningThreshold: 15,
+ isEnabled: true,
+ warningTime: '2024-01-15 07:15:00',
+ warningDuration: 4,
+ lastUpdateTime: '2024-01-15 11:30:00',
+ expectedRefillTime: '2024-01-15 16:00:00',
+ expectedShortageTime: '2024-01-15 14:00:00', // 浠婂ぉ涓嬪崍2鐐圭己姘�
+ warningRule: '褰撴娴嬪埌姘斾綋娉勬紡鏃惰Е鍙戦璀�'
+ },
+ {
+ id: 5,
+ tankCode: 'TANK005',
+ tankName: '娑插寲姘斿偍缃怑',
+ tankType: '娑插寲姘斿偍缃�',
+ specificationModel: 'LPG-6000L',
+ volume: 6000,
+ currentGasLevel: 35,
+ safetyGasLevel: 30,
+ minGasLevel: 15,
+ maxGasLevel: 95,
+ currentPressure: 3.2,
+ warningType: '姘斾綋涓嶈冻',
+ warningLevel: '閲嶈',
+ warningThreshold: 20,
+ isEnabled: false,
+ warningTime: '2024-01-14 14:30:00',
+ warningDuration: 2,
+ lastUpdateTime: '2024-01-15 09:00:00',
+ expectedRefillTime: '2024-01-19 08:00:00',
+ expectedShortageTime: '2024-01-21 10:00:00', // 6澶╁悗缂烘皵
+ warningRule: '褰撴皵浣撻噺浣庝簬20%鏃惰Е鍙戦璀�'
+ },
+ {
+ id: 6,
+ tankCode: 'TANK006',
+ tankName: '鍘嬬缉姘斿偍缃怓',
+ tankType: '鍘嬬缉姘斿偍缃�',
+ specificationModel: 'COMP-4000L',
+ volume: 4000,
+ currentGasLevel: 85,
+ safetyGasLevel: 20,
+ minGasLevel: 10,
+ maxGasLevel: 90,
+ currentPressure: 7.8,
+ warningType: '鍘嬪姏寮傚父',
+ warningLevel: '涓�鑸�',
+ warningThreshold: 8,
+ isEnabled: true,
+ warningTime: '2024-01-12 09:20:00',
+ warningDuration: 1,
+ lastUpdateTime: '2024-01-15 08:30:00',
+ expectedRefillTime: '2024-01-25 14:00:00',
+ expectedShortageTime: '2024-01-28 16:00:00', // 13澶╁悗缂烘皵
+ warningRule: '褰撳帇鍔涜秴杩�8MPa鏃惰Е鍙戦璀�'
+ },
+ {
+ id: 7,
+ tankCode: 'TANK007',
+ tankName: '澶╃劧姘斿偍缃怗',
+ tankType: '澶╃劧姘斿偍缃�',
+ specificationModel: 'NG-10000L',
+ volume: 10000,
+ currentGasLevel: 92,
+ safetyGasLevel: 15,
+ minGasLevel: 8,
+ maxGasLevel: 95,
+ currentPressure: 5.2,
+ warningType: '娓╁害寮傚父',
+ warningLevel: '閲嶈',
+ warningThreshold: 6,
+ isEnabled: true,
+ warningTime: '2024-01-11 16:45:00',
+ warningDuration: 1,
+ lastUpdateTime: '2024-01-15 07:45:00',
+ expectedRefillTime: '2024-01-30 09:00:00',
+ expectedShortageTime: '2024-02-05 12:00:00', // 21澶╁悗缂烘皵
+ warningRule: '褰撴俯搴﹁秴杩�60掳C鏃惰Е鍙戦璀�'
+ },
+ {
+ id: 8,
+ tankCode: 'TANK008',
+ tankName: '姘ф皵鍌ㄧ綈H',
+ tankType: '姘ф皵鍌ㄧ綈',
+ specificationModel: 'O2-1500L',
+ volume: 1500,
+ currentGasLevel: 12,
+ safetyGasLevel: 30,
+ minGasLevel: 8,
+ maxGasLevel: 90,
+ currentPressure: 4.5,
+ warningType: '娉勬紡棰勮',
+ warningLevel: '绱ф��',
+ warningThreshold: 12,
+ isEnabled: true,
+ warningTime: '2024-01-15 06:30:00',
+ warningDuration: 5,
+ lastUpdateTime: '2024-01-15 12:15:00',
+ expectedRefillTime: '2024-01-15 20:00:00',
+ expectedShortageTime: '2024-01-15 17:30:00', // 浠婂ぉ涓嬪崍5:30缂烘皵
+ warningRule: '褰撴娴嬪埌姘斾綋娉勬紡鏃惰Е鍙戦璀�'
+ }
+ ]
+
+ // 鏍规嵁鎼滅储鏉′欢杩囨护鏁版嵁
+ let filteredData = mockData.filter(item => {
+ if (searchForm.tankName && !item.tankName.includes(searchForm.tankName)) return false
+ if (searchForm.tankType && item.tankType !== searchForm.tankType) return false
+ if (searchForm.warningType && item.warningType !== searchForm.warningType) return false
+ if (searchForm.warningLevel && item.warningLevel !== searchForm.warningLevel) return false
+ return true
+ })
+
+ // 鍒嗛〉澶勭悊
+ const start = (page.current - 1) * page.size
+ const end = start + page.size
+ const paginatedData = filteredData.slice(start, end)
+
+ return {
+ records: paginatedData,
+ total: filteredData.length
+ }
+}
+
+// 鑾峰彇鍒楄〃鏁版嵁
+const getList = async () => {
+ tableLoading.value = true
+ try {
+ // 妯℃嫙缃戠粶寤惰繜
+ await new Promise(resolve => setTimeout(resolve, 500))
+
+ const result = generateMockData()
+ tableData.value = result.records
+ total.value = result.total
+
+ // 妫�鏌ョ己姘旈璀�
+ checkShortageWarnings()
+ } catch (error) {
+ console.error('鑾峰彇鍒楄〃澶辫触:', error)
+ ElMessage.error('鑾峰彇鍒楄〃澶辫触')
+ } finally {
+ tableLoading.value = false
+ }
+}
+
+// 鎼滅储
+const handleQuery = () => {
+ page.current = 1
+ getList()
+}
+
+// 閲嶇疆鎼滅储
+const resetQuery = () => {
+ Object.keys(searchForm).forEach(key => {
+ searchForm[key] = ''
+ })
+ handleQuery()
+}
+
+// 鍒嗛〉鍙樺寲
+const paginationChange = (obj) => {
+ page.current = obj.page
+ page.size = obj.limit
+ getList()
+}
+
+// 琛ㄦ牸閫夋嫨鍙樺寲
+const handleSelectionChange = (selection) => {
+ selectedRows.value = selection
+}
+
+// 鏂板
+const handleAdd = () => {
+ operationType.value = 'add'
+ resetForm()
+ dialogFormVisible.value = true
+}
+
+// 缂栬緫
+const handleEdit = (row) => {
+ operationType.value = 'edit'
+ Object.assign(form, row)
+ dialogFormVisible.value = true
+}
+
+// 澶勭悊棰勮
+const handleProcess = async (row) => {
+ try {
+ // 妯℃嫙API璋冪敤寤惰繜
+ await new Promise(resolve => setTimeout(resolve, 300))
+ ElMessage.success(`姝e湪澶勭悊棰勮锛�${row.tankName}`)
+ getList()
+ } catch (error) {
+ ElMessage.error('澶勭悊棰勮澶辫触')
+ }
+}
+
+// 鍒犻櫎
+const handleDelete = async (row) => {
+ try {
+ await ElMessageBox.confirm(`纭畾瑕佸垹闄ら璀﹁鍒欙細${row.tankName}鍚楋紵`, '鎻愮ず', {
+ confirmButtonText: '纭畾',
+ cancelButtonText: '鍙栨秷',
+ type: 'warning'
+ })
+
+ // 妯℃嫙API璋冪敤寤惰繜
+ await new Promise(resolve => setTimeout(resolve, 300))
+ ElMessage.success('鍒犻櫎鎴愬姛')
+ getList()
+ } catch (error) {
+ if (error !== 'cancel') {
+ ElMessage.error('鍒犻櫎澶辫触')
+ }
+ }
+}
+
+// 鎵归噺澶勭悊
+const handleBatchProcess = async () => {
+ if (selectedRows.value.length === 0) {
+ ElMessage.warning('璇烽�夋嫨瑕佸鐞嗙殑棰勮')
+ return
+ }
+
+ try {
+ // 妯℃嫙API璋冪敤寤惰繜
+ await new Promise(resolve => setTimeout(resolve, 500))
+ ElMessage.success(`鎵归噺澶勭悊浜� ${selectedRows.value.length} 鏉¢璀)
+ getList()
+ } catch (error) {
+ ElMessage.error('鎵归噺澶勭悊澶辫触')
+ }
+}
+
+// 瀵煎嚭
+const handleExport = async () => {
+ try {
+ // 妯℃嫙API璋冪敤寤惰繜
+ await new Promise(resolve => setTimeout(resolve, 800))
+
+ // 鐢熸垚瀵煎嚭鏁版嵁
+ const exportData = generateMockData().records
+ const csvContent = generateCSV(exportData)
+
+ // 鍒涘缓涓嬭浇閾炬帴
+ const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' })
+ const url = window.URL.createObjectURL(blob)
+ const link = document.createElement('a')
+ link.href = url
+ link.download = `鍌ㄦ皵缃愰璀︽暟鎹甠${new Date().getTime()}.csv`
+ link.click()
+ window.URL.revokeObjectURL(url)
+
+ ElMessage.success('瀵煎嚭鎴愬姛')
+ } catch (error) {
+ ElMessage.error('瀵煎嚭澶辫触')
+ }
+}
+
+// 鐢熸垚CSV鍐呭
+const generateCSV = (data) => {
+ const headers = [
+ '鍌ㄦ皵缃愮紪鐮�', '鍌ㄦ皵缃愬悕绉�', '鍌ㄦ皵缃愮被鍨�', '瑙勬牸鍨嬪彿', '瀹圭Н(m鲁)',
+ '褰撳墠姘斾綋閲�(%)', '瀹夊叏姘斾綋閲�(%)', '鏈�浣庢皵浣撻噺(%)', '鏈�楂樻皵浣撻噺(%)',
+ '褰撳墠鍘嬪姏(MPa)', '棰勮绫诲瀷', '棰勮绾у埆', '棰勮闃堝��', '鏄惁鍚敤',
+ '棰勮鏃堕棿', '棰勮鎸佺画澶╂暟', '鏈�鍚庢洿鏂版椂闂�', '棰勮鍏呰鏃堕棿', '棰勮缂烘皵鏃堕棿', '棰勮瑙勫垯鎻忚堪'
+ ]
+
+ const csvRows = [headers.join(',')]
+
+ data.forEach(item => {
+ const row = [
+ item.tankCode,
+ item.tankName,
+ item.tankType,
+ item.specificationModel,
+ item.volume,
+ item.currentGasLevel,
+ item.safetyGasLevel,
+ item.minGasLevel,
+ item.maxGasLevel,
+ item.currentPressure,
+ item.warningType,
+ item.warningLevel,
+ item.warningThreshold,
+ item.isEnabled ? '鏄�' : '鍚�',
+ item.warningTime,
+ item.warningDuration,
+ item.lastUpdateTime,
+ item.expectedRefillTime,
+ item.expectedShortageTime,
+ item.warningRule
+ ]
+ csvRows.push(row.join(','))
+ })
+
+ return csvRows.join('\n')
+}
+
+// 鍚敤鐘舵�佸彉鍖�
+const handleEnableChange = async (row) => {
+ try {
+ // 妯℃嫙API璋冪敤寤惰繜
+ await new Promise(resolve => setTimeout(resolve, 200))
+ ElMessage.success(`${row.tankName} 鐨勫惎鐢ㄧ姸鎬佸凡鏇存柊`)
+ } catch (error) {
+ ElMessage.error('鐘舵�佹洿鏂板け璐�')
+ // 鎭㈠鍘熺姸鎬�
+ row.isEnabled = !row.isEnabled
+ }
+}
+
+// 鎻愪氦琛ㄥ崟
+const submitForm = async () => {
+ try {
+ await proxy.$refs.formRef.validate()
+
+ // 妯℃嫙API璋冪敤寤惰繜
+ await new Promise(resolve => setTimeout(resolve, 500))
+
+ if (operationType.value === 'add') {
+ ElMessage.success('鏂板鎴愬姛')
+ } else {
+ ElMessage.success('缂栬緫鎴愬姛')
+ }
+
+ closeDialog()
+ getList()
+ } catch (error) {
+ if (!error.errors) {
+ ElMessage.error(operationType.value === 'add' ? '鏂板澶辫触' : '缂栬緫澶辫触')
+ }
+ }
+}
+
+// 鍏抽棴寮圭獥
+const closeDialog = () => {
+ dialogFormVisible.value = false
+ resetForm()
+}
+
+// 閲嶇疆琛ㄥ崟
+const resetForm = () => {
+ Object.keys(form).forEach(key => {
+ if (key === 'isEnabled') {
+ form[key] = true
+ } else if (typeof form[key] === 'number') {
+ form[key] = 0
+ } else {
+ form[key] = ''
+ }
+ })
+ proxy.$refs.formRef?.resetFields()
+}
+
+// 鑾峰彇姘斾綋閲忔牱寮忕被
+const getGasLevelClass = (row) => {
+ if (row.currentGasLevel < row.minGasLevel) {
+ return 'text-danger'
+ } else if (row.currentGasLevel > row.maxGasLevel) {
+ return 'text-warning'
+ }
+ return 'text-success'
+}
+
+// 鑾峰彇棰勮绫诲瀷鏍囩鏍峰紡
+const getWarningTypeTag = (type) => {
+ const typeMap = {
+ '姘斾綋涓嶈冻': 'danger',
+ '鍘嬪姏寮傚父': 'warning',
+ '娓╁害寮傚父': 'info',
+ '娉勬紡棰勮': 'danger'
+ }
+ return typeMap[type] || 'info'
+}
+
+// 鑾峰彇棰勮绾у埆鏍囩鏍峰紡
+const getWarningLevelTag = (level) => {
+ const levelMap = {
+ '绱ф��': 'danger',
+ '閲嶈': 'warning',
+ '涓�鑸�': 'info'
+ }
+ return levelMap[level] || 'info'
+}
+
+// 鍚姩鍊掕鏃跺畾鏃跺櫒
+const startCountdownTimer = () => {
+ countdownTimer.value = setInterval(() => {
+ checkShortageWarnings()
+ }, 60000) // 姣忓垎閽熸鏌ヤ竴娆�
+}
+
+// 鍋滄鍊掕鏃跺畾鏃跺櫒
+const stopCountdownTimer = () => {
+ if (countdownTimer.value) {
+ clearInterval(countdownTimer.value)
+ countdownTimer.value = null
+ }
+}
+
+// 椤甸潰鍔犺浇
+onMounted(() => {
+ getList()
+ startCountdownTimer()
+})
+
+// 椤甸潰鍗歌浇
+onUnmounted(() => {
+ stopCountdownTimer()
+})
+</script>
+
+<style scoped lang="scss">
+.app-container {
+ padding: 20px;
+
+ .table-operations {
+ text-align: right;
+ margin-bottom: 20px;
+
+ .el-button {
+ margin-top: 20px;
+ margin-right: 10px;
+ }
+ }
+
+ .table_list {
+ background: #fff;
+ border-radius: 4px;
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+ }
+
+ .text-danger {
+ color: #f56c6c;
+ font-weight: bold;
+ }
+
+ .text-warning {
+ color: #e6a23c;
+ font-weight: bold;
+ }
+
+ .text-success {
+ color: #67c23a;
+ font-weight: bold;
+ }
+
+ .dialog-footer {
+ text-align: right;
+ }
+
+ // 鍊掕鏃舵牱寮�
+ .countdown-timer {
+ font-weight: bold;
+ }
+
+ .countdown-normal {
+ color: #67c23a;
+ }
+
+ .countdown-warning {
+ color: #e6a23c;
+ }
+
+ .countdown-urgent {
+ color: #f56c6c;
+ animation: blink 1s infinite;
+ }
+
+ .countdown-expired {
+ color: #f56c6c;
+ font-weight: bold;
+ }
+
+ @keyframes blink {
+ 0%, 50% { opacity: 1; }
+ 51%, 100% { opacity: 0.5; }
+ }
+
+ // 缂烘皵棰勮寮规鏍峰紡
+ .shortage-warning-content {
+ text-align: center;
+ padding: 20px 0;
+
+ .warning-icon {
+ margin-bottom: 20px;
+ }
+
+ .warning-message {
+ h3 {
+ color: #f56c6c;
+ margin-bottom: 10px;
+ }
+
+ p {
+ margin-bottom: 10px;
+ color: #606266;
+ }
+
+ .warning-details {
+ background: #f5f7fa;
+ padding: 15px;
+ border-radius: 4px;
+ text-align: left;
+ font-size: 14px;
+ line-height: 1.6;
+ }
+ }
+ }
+}
+</style>
--
Gitblit v1.9.3