From c104bf2b4ecf604245b38590bf1e8119530de10b Mon Sep 17 00:00:00 2001 From: yaowanxin <3588231647@qq.com> Date: 星期二, 12 八月 2025 16:26:55 +0800 Subject: [PATCH] Merge branch 'ywx' of http://114.132.189.42:9002/r/product-inventory-management into dev_ai --- src/views/inventoryManagement/stockWarning/index.vue | 1137 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 1,137 insertions(+), 0 deletions(-) diff --git a/src/views/inventoryManagement/stockWarning/index.vue b/src/views/inventoryManagement/stockWarning/index.vue new file mode 100644 index 0000000..3694265 --- /dev/null +++ b/src/views/inventoryManagement/stockWarning/index.vue @@ -0,0 +1,1137 @@ +<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-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