From 386375a8e04f72a8bc17553b649a38b5535e21ba Mon Sep 17 00:00:00 2001 From: gaoluyang <2820782392@qq.com> Date: 星期一, 11 八月 2025 09:26:52 +0800 Subject: [PATCH] 1.添加用水管理页面 --- src/views/equipmentManagement/gasTank/simple.vue | 566 +++++++++++ src/api/inventoryManagement/stockWarning.js | 81 + src/views/energyManagement/waterManagement/index.vue | 312 ++++++ src/views/energyManagement/waterManagement/waterTrends.vue | 118 ++ src/views/energyManagement/waterManagement/components/formDia.vue | 221 ++++ src/views/energyManagement/waterManagement/waterBill.vue | 181 +++ src/views/energyManagement/waterManagement/components/waterBillForm.vue | 210 ++++ src/views/inventoryManagement/stockWarning/index.vue | 1137 +++++++++++++++++++++++ src/api/energyManagement/waterManagement.js | 91 + 9 files changed, 2,917 insertions(+), 0 deletions(-) diff --git a/src/api/energyManagement/waterManagement.js b/src/api/energyManagement/waterManagement.js new file mode 100644 index 0000000..aef7465 --- /dev/null +++ b/src/api/energyManagement/waterManagement.js @@ -0,0 +1,91 @@ +// 鐢ㄦ按绠$悊 +import request from "@/utils/request"; + +// 鐢ㄦ按璁惧-鍒嗛〉鏌ヨ +export function waterEquipmentListPage(query) { + return request({ + url: '/waterEquipmentConsumption/listPage', + method: 'get', + params: query, + }) +} + +// 鐢ㄦ按瓒嬪娍-鍒嗛〉鏌ヨ +export function listPageByWaterTrend(query) { + return request({ + url: '/waterEquipmentConsumption/listPageByTrend', + method: 'get', + params: query, + }) +} + +// 鐢ㄦ按璁惧-鍒犻櫎 +export function waterEquipmentDelete(query) { + return request({ + url: '/waterEquipmentConsumption/delete', + method: 'delete', + data: query, + }) +} + +// 鐢ㄦ按璁惧-鏂板 +export function waterEquipmentAdd(query) { + return request({ + url: '/waterEquipmentConsumption/add', + method: 'post', + data: query, + }) +} + +// 鐢ㄦ按璁惧-淇敼 +export function waterEquipmentUpdate(query) { + return request({ + url: '/waterEquipmentConsumption/update', + method: 'post', + data: query, + }) +} + +// 鐢ㄦ按璁惧涓嬫媺妗嗘煡璇� +export function waterDeviceList(query) { + return request({ + url: '/waterEquipmentConsumption/deviceList', + method: 'get', + }) +} + +// 姘磋垂绠$悊-鍒嗛〉鏌ヨ +export function waterBillListPage(query) { + return request({ + url: '/waterBill/listPage', + method: 'get', + params: query, + }) +} + +// 姘磋垂绠$悊-鏂板 +export function waterBillAdd(query) { + return request({ + url: '/waterBill/add', + method: 'post', + data: query, + }) +} + +// 姘磋垂绠$悊-淇敼 +export function waterBillUpdate(query) { + return request({ + url: '/waterBill/update', + method: 'post', + data: query, + }) +} + +// 姘磋垂绠$悊-鍒犻櫎 +export function waterBillDelete(query) { + return request({ + url: '/waterBill/delete', + method: 'delete', + data: query, + }) +} diff --git a/src/api/inventoryManagement/stockWarning.js b/src/api/inventoryManagement/stockWarning.js new file mode 100644 index 0000000..092fb80 --- /dev/null +++ b/src/api/inventoryManagement/stockWarning.js @@ -0,0 +1,81 @@ +import request from "@/utils/request"; + +// 鏌ヨ鍌ㄦ皵缃愰璀﹀垪琛� +export const getStockWarningPage = (params) => { + return request({ + url: "/gasTankWarning/listPage", + method: "get", + params, + }); +}; + +// 鏂板鍌ㄦ皵缃愰璀﹁鍒� +export const addStockWarning = (data) => { + return request({ + url: "/gasTankWarning/add", + method: "post", + data, + }); +}; + +// 淇敼鍌ㄦ皵缃愰璀﹁鍒� +export const updateStockWarning = (data) => { + return request({ + url: "/gasTankWarning/update", + method: "put", + data, + }); +}; + +// 鍒犻櫎鍌ㄦ皵缃愰璀﹁鍒� +export const deleteStockWarning = (ids) => { + return request({ + url: "/gasTankWarning/delete", + method: "delete", + data: { ids }, + }); +}; + +// 鎵归噺澶勭悊鍌ㄦ皵缃愰璀� +export const batchProcessStockWarning = (data) => { + return request({ + url: "/gasTankWarning/batchProcess", + method: "post", + data, + }); +}; + +// 瀵煎嚭鍌ㄦ皵缃愰璀︽暟鎹� +export const exportStockWarning = (params) => { + return request({ + url: "/gasTankWarning/export", + method: "get", + params, + responseType: "blob", + }); +}; + +// 鏍规嵁ID鑾峰彇鍌ㄦ皵缃愰璀﹁鎯� +export const getStockWarningById = (id) => { + return request({ + url: `/gasTankWarning/${id}`, + method: "get", + }); +}; + +// 鍚敤/绂佺敤棰勮瑙勫垯 +export const toggleStockWarningStatus = (data) => { + return request({ + url: "/gasTankWarning/toggleStatus", + method: "put", + data, + }); +}; + +// 鑾峰彇棰勮缁熻淇℃伅 +export const getStockWarningStatistics = () => { + return request({ + url: "/gasTankWarning/statistics", + method: "get", + }); +}; diff --git a/src/views/energyManagement/waterManagement/components/formDia.vue b/src/views/energyManagement/waterManagement/components/formDia.vue new file mode 100644 index 0000000..a692b95 --- /dev/null +++ b/src/views/energyManagement/waterManagement/components/formDia.vue @@ -0,0 +1,221 @@ +<template> + <div> + <el-dialog + v-model="dialogFormVisible" + title="鐢ㄦ按璁惧" + width="70%" + @close="closeDia" + > + <el-form + :model="form" + label-width="140px" + label-position="top" + :rules="rules" + ref="formRef" + > + <el-row :gutter="30"> + <el-col :span="12"> + <el-form-item label="璁惧锛�" prop="code"> + <el-select + v-model="form.code" + placeholder="璇烽�夋嫨" + clearable + @change="setName" + :disabled="operationType !== 'add'" + > + <el-option + v-for="item in codeList" + :key="item.deviceModel" + :label="item.deviceName" + :value="item.deviceModel" + > + {{item.deviceName + '--' + item.deviceModel}} + </el-option> + </el-select> + </el-form-item> + </el-col> + <el-col :span="12"> + <el-form-item label="姣忔棩闄愬埗姘撮噺锛�" prop="everyNum"> + <el-input + v-model="form.everyNum" + placeholder="璇疯緭鍏�" + clearable + /> + </el-form-item> + </el-col> + </el-row> + <el-row :gutter="30"> + <el-col :span="12"> + <el-form-item label="棰濆畾娴侀噺锛�" prop="flowRating"> + <el-input + v-model="form.flowRating" + placeholder="璇疯緭鍏�" + clearable + /> + </el-form-item> + </el-col> + <el-col :span="12"> + <el-form-item label="瀹為檯娴侀噺锛�" prop="flowActual"> + <el-input + v-model="form.flowActual" + placeholder="璇疯緭鍏�" + clearable + /> + </el-form-item> + </el-col> + </el-row> + <el-row :gutter="30"> + <el-col :span="12"> + <el-form-item label="杩愯鏃堕棿锛�" prop="runDate"> + <el-date-picker + style="width: 100%" + v-model="form.runDate" + value-format="YYYY-MM-DD" + format="YYYY-MM-DD" + type="date" + placeholder="璇烽�夋嫨" + clearable + /> + </el-form-item> + </el-col> + <el-col :span="12"> + <el-form-item label="褰撴棩鐢ㄦ按閲忥細" prop="dayNum"> + <el-input + v-model="form.dayNum" + placeholder="璇疯緭鍏�" + clearable + /> + </el-form-item> + </el-col> + </el-row> + <el-row :gutter="30"> + <el-col :span="12"> + <el-form-item label="姘磋垂鍗曚环锛�" prop="waterPrice"> + <el-input + v-model="form.waterPrice" + placeholder="璇疯緭鍏�" + clearable + /> + </el-form-item> + </el-col> + <el-col :span="12"> + <el-form-item label="鐢ㄦ按绫诲瀷锛�" prop="waterType"> + <el-select + v-model="form.waterType" + placeholder="璇烽�夋嫨" + clearable + > + <el-option label="宸ヤ笟鐢ㄦ按" value="industrial" /> + <el-option label="鐢熸椿鐢ㄦ按" value="domestic" /> + <el-option label="娑堥槻鐢ㄦ按" value="fire" /> + <el-option label="缁垮寲鐢ㄦ按" value="greening" /> + </el-select> + </el-form-item> + </el-col> + </el-row> + </el-form> + <template #footer> + <div class="dialog-footer"> + <el-button type="primary" @click="submitForm">纭</el-button> + <el-button @click="closeDia">鍙栨秷</el-button> + </div> + </template> + </el-dialog> + </div> +</template> + +<script setup> +import {ref, reactive, nextTick} from "vue"; +import useUserStore from "@/store/modules/user.js"; +import {waterDeviceList, waterEquipmentAdd, waterEquipmentUpdate} from "@/api/energyManagement/waterManagement.js"; +const { proxy } = getCurrentInstance() +const emit = defineEmits(['close']) +const dialogFormVisible = ref(false); +const operationType = ref('') +const userStore = useUserStore(); + +const data = reactive({ + form: { + name: "", + code: "", + everyNum: "", + flowRating: "", + flowActual: "", + runDate: "", + dayNum: "", + waterPrice: "", + waterType: "", + }, + rules: { + code: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }], + runDate: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }], + everyNum: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }], + flowRating: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }], + flowActual: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }], + dayNum: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }], + waterPrice: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }], + waterType: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }], + }, +}) +const { form, rules } = toRefs(data); +const codeList = ref([]) + +// 鎵撳紑寮规 +const openDialog = (type, row) => { + operationType.value = type; + dialogFormVisible.value = true; + form.value = {} + proxy.resetForm("formRef"); + waterDeviceList().then((res) => { + codeList.value = res.data; + }); + if (type === "edit") { + form.value = {...row} + } +} +const setName = (code) => { + const index = codeList.value.findIndex(item => item.deviceModel === code); + if (index > -1) { + console.log(codeList) + form.value.name = codeList.value[index].deviceName; + } +} +const submitForm = () => { + proxy.$refs["formRef"].validate(valid => { + if (valid) { + if (operationType.value === "add") { + waterEquipmentAdd(form.value).then(response => { + proxy.$modal.msgSuccess("鏂板鎴愬姛") + closeDia() + }) + } else { + waterEquipmentUpdate(form.value).then(response => { + proxy.$modal.msgSuccess("淇敼鎴愬姛") + closeDia() + }) + } + } + }) +} +// 鍏抽棴寮规 +const closeDia = () => { + proxy.resetForm("formRef"); + dialogFormVisible.value = false; + emit('close') +}; +// 鑾峰彇褰撳墠鏃ユ湡骞舵牸寮忓寲涓� YYYY-MM-DD +function getCurrentDate() { + const today = new Date(); + const year = today.getFullYear(); + const month = String(today.getMonth() + 1).padStart(2, "0"); // 鏈堜唤浠�0寮�濮� + const day = String(today.getDate()).padStart(2, "0"); + return `${year}-${month}-${day}`; +} +defineExpose({ + openDialog, +}); +</script> + +<style scoped> + +</style> diff --git a/src/views/energyManagement/waterManagement/components/waterBillForm.vue b/src/views/energyManagement/waterManagement/components/waterBillForm.vue new file mode 100644 index 0000000..a132041 --- /dev/null +++ b/src/views/energyManagement/waterManagement/components/waterBillForm.vue @@ -0,0 +1,210 @@ +<template> + <div> + <el-dialog + v-model="dialogFormVisible" + title="姘磋垂绠$悊" + width="70%" + @close="closeDia" + > + <el-form + :model="form" + label-width="140px" + label-position="top" + :rules="rules" + ref="formRef" + > + <el-row :gutter="30"> + <el-col :span="12"> + <el-form-item label="璁惧锛�" prop="code"> + <el-select + v-model="form.code" + placeholder="璇烽�夋嫨" + clearable + @change="setName" + :disabled="operationType !== 'add'" + > + <el-option + v-for="item in codeList" + :key="item.deviceModel" + :label="item.deviceName" + :value="item.deviceModel" + > + {{item.deviceName + '--' + item.deviceModel}} + </el-option> + </el-select> + </el-form-item> + </el-col> + <el-col :span="12"> + <el-form-item label="鐢ㄦ按閲忥細" prop="waterConsumption"> + <el-input + v-model="form.waterConsumption" + placeholder="璇疯緭鍏�" + clearable + /> + </el-form-item> + </el-col> + </el-row> + <el-row :gutter="30"> + <el-col :span="12"> + <el-form-item label="姘磋垂鍗曚环锛�" prop="waterPrice"> + <el-input + v-model="form.waterPrice" + placeholder="璇疯緭鍏�" + clearable + /> + </el-form-item> + </el-col> + <el-col :span="12"> + <el-form-item label="姘磋垂閲戦锛�" prop="waterBill"> + <el-input + v-model="form.waterBill" + placeholder="鑷姩璁$畻" + clearable + disabled + /> + </el-form-item> + </el-col> + </el-row> + <el-row :gutter="30"> + <el-col :span="12"> + <el-form-item label="璁¤垂鏃ユ湡锛�" prop="billDate"> + <el-date-picker + style="width: 100%" + v-model="form.billDate" + value-format="YYYY-MM-DD" + format="YYYY-MM-DD" + type="date" + placeholder="璇烽�夋嫨" + clearable + /> + </el-form-item> + </el-col> + <el-col :span="12"> + <el-form-item label="鐢ㄦ按绫诲瀷锛�" prop="waterType"> + <el-select + v-model="form.waterType" + placeholder="璇烽�夋嫨" + clearable + > + <el-option label="宸ヤ笟鐢ㄦ按" value="industrial" /> + <el-option label="鐢熸椿鐢ㄦ按" value="domestic" /> + <el-option label="娑堥槻鐢ㄦ按" value="fire" /> + <el-option label="缁垮寲鐢ㄦ按" value="greening" /> + </el-select> + </el-form-item> + </el-col> + </el-row> + </el-form> + <template #footer> + <div class="dialog-footer"> + <el-button type="primary" @click="submitForm">纭</el-button> + <el-button @click="closeDia">鍙栨秷</el-button> + </div> + </template> + </el-dialog> + </div> +</template> + +<script setup> +import {ref, reactive, nextTick, watch} from "vue"; +import useUserStore from "@/store/modules/user.js"; +import {waterDeviceList, waterBillAdd, waterBillUpdate} from "@/api/energyManagement/waterManagement.js"; +const { proxy } = getCurrentInstance() +const emit = defineEmits(['close']) +const dialogFormVisible = ref(false); +const operationType = ref('') +const userStore = useUserStore(); + +const data = reactive({ + form: { + name: "", + code: "", + waterConsumption: "", + waterPrice: "", + waterBill: "", + billDate: "", + waterType: "", + }, + rules: { + code: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }], + waterConsumption: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }], + waterPrice: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }], + billDate: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }], + waterType: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }], + }, +}) +const { form, rules } = toRefs(data); +const codeList = ref([]) + +// 鎵撳紑寮规 +const openDialog = (type, row) => { + operationType.value = type; + dialogFormVisible.value = true; + form.value = {} + proxy.resetForm("formRef"); + waterDeviceList().then((res) => { + codeList.value = res.data; + }); + if (type === "edit") { + form.value = {...row} + } +} +const setName = (code) => { + const index = codeList.value.findIndex(item => item.deviceModel === code); + if (index > -1) { + console.log(codeList) + form.value.name = codeList.value[index].deviceName; + } +} + +// 璁$畻姘磋垂閲戦 +const calculateWaterBill = () => { + if (form.value.waterConsumption && form.value.waterPrice) { + form.value.waterBill = (parseFloat(form.value.waterConsumption) * parseFloat(form.value.waterPrice)).toFixed(2); + } +} + +// 鐩戝惉鐢ㄦ按閲忓拰姘磋垂鍗曚环鍙樺寲 +watch([() => form.value.waterConsumption, () => form.value.waterPrice], () => { + calculateWaterBill(); +}); + +const submitForm = () => { + proxy.$refs["formRef"].validate(valid => { + if (valid) { + if (operationType.value === "add") { + waterBillAdd(form.value).then(response => { + proxy.$modal.msgSuccess("鏂板鎴愬姛") + closeDia() + }) + } else { + waterBillUpdate(form.value).then(response => { + proxy.$modal.msgSuccess("淇敼鎴愬姛") + closeDia() + }) + } + } + }) +} +// 鍏抽棴寮规 +const closeDia = () => { + proxy.resetForm("formRef"); + dialogFormVisible.value = false; + emit('close') +}; +// 鑾峰彇褰撳墠鏃ユ湡骞舵牸寮忓寲涓� YYYY-MM-DD +function getCurrentDate() { + const today = new Date(); + const year = today.getFullYear(); + const month = String(today.getMonth() + 1).padStart(2, "0"); // 鏈堜唤浠�0寮�濮� + const day = String(today.getDate()).padStart(2, "0"); + return `${year}-${month}-${day}`; +} +defineExpose({ + openDialog, +}); +</script> + +<style scoped> + +</style> diff --git a/src/views/energyManagement/waterManagement/index.vue b/src/views/energyManagement/waterManagement/index.vue new file mode 100644 index 0000000..848a945 --- /dev/null +++ b/src/views/energyManagement/waterManagement/index.vue @@ -0,0 +1,312 @@ +<template> + <div class="app-container"> + <div class="search_form"> + <div> + <span class="search_title">璁惧鍚嶇О锛�</span> + <el-input + v-model="searchForm.name" + style="width: 240px" + placeholder="璇疯緭鍏�" + @change="handleQuery" + clearable + :prefix-icon="Search" + /> + <el-button type="primary" @click="handleQuery" style="margin-left: 10px" + >鎼滅储</el-button + > + </div> + <div> + <el-button type="primary" @click="openForm('add')">鏂板</el-button> + <el-button type="info" plain icon="Upload" @click="handleImport">瀵煎叆</el-button> + <el-button type="danger" plain @click="handleDelete">鍒犻櫎</el-button> + </div> + </div> + <div class="table_list"> + <PIMTable + rowKey="id" + :column="tableColumn" + :tableData="tableData" + :page="page" + :isSelection="true" + @selection-change="handleSelectionChange" + :tableLoading="tableLoading" + @pagination="pagination" + ></PIMTable> + </div> + <form-dia ref="formDia" @close="handleQuery"></form-dia> + <el-dialog + :title="upload.title" + v-model="upload.open" + width="400px" + append-to-body + @close="handleDialogClose" + > + <el-upload + ref="uploadRef" + :limit="1" + accept=".xlsx, .xls" + :headers="upload.headers" + :action="upload.url" + :disabled="upload.isUploading" + :before-upload="upload.beforeUpload" + :on-progress="upload.onProgress" + :on-success="upload.onSuccess" + :on-error="upload.onError" + :on-change="upload.onChange" + :auto-upload="false" + drag + > + <el-icon class="el-icon--upload"><upload-filled /></el-icon> + <div class="el-upload__text">灏嗘枃浠舵嫋鍒版澶勶紝鎴�<em>鐐瑰嚮涓婁紶</em></div> + <template #tip> + <div class="el-upload__tip text-center"> + <span>浠呭厑璁稿鍏ls銆亁lsx鏍煎紡鏂囦欢銆�</span> + <el-link + type="primary" + :underline="false" + style="font-size: 12px; vertical-align: baseline" + @click="importTemplate" + >涓嬭浇妯℃澘</el-link + > + </div> + </template> + </el-upload> + <template #footer> + <div class="dialog-footer"> + <el-button type="primary" @click="submitFileForm">纭� 瀹�</el-button> + <el-button @click="upload.open = false">鍙� 娑�</el-button> + </div> + </template> + </el-dialog> + </div> +</template> + +<script setup> +import {Search} from "@element-plus/icons-vue"; +import {onMounted, ref, reactive, nextTick} from "vue"; +import FormDia from "@/views/energyManagement/waterManagement/components/formDia.vue"; +import {ElMessageBox} from "element-plus"; +import {getToken} from "@/utils/auth.js"; +import {waterEquipmentDelete, waterEquipmentListPage} from "@/api/energyManagement/waterManagement.js"; +const { proxy } = getCurrentInstance(); + +const data = reactive({ + searchForm: { + name: "", + }, +}); +const { searchForm } = toRefs(data); + +const selectedRows = ref([]); +const tableColumn = ref([ + { + label: "璁惧鍚嶇О", + prop: "name", + width: 200, + }, + { + label: "瑙勬牸鍨嬪彿", + prop: "code", + width: 200, + }, + { + label: "棰濆畾娴侀噺", + prop: "flowRating", + }, + { + label: "瀹為檯娴侀噺", + prop: "flowActual", + }, + { + label: "杩愯鏃堕棿", + prop: "runDate", + width:150 + }, + { + label: "褰撴棩鐢ㄦ按閲�", + prop: "dayNum", + width: 150, + }, + { + label: "姣忔棩闄愬埗姘撮噺", + prop: "everyNum", + width:220 + }, + { + label: "姘磋垂鍗曚环", + prop: "waterPrice", + width: 120, + }, + { + dataType: "action", + label: "鎿嶄綔", + align: "center", + fixed: 'right', + operation: [ + { + name: "缂栬緫", + type: "text", + clickFun: (row) => { + openForm("edit", row); + }, + }, + ], + }, +]); +const tableData = ref([]); +const tableLoading = ref(false); +const page = reactive({ + current: 1, + size: 100, + total: 0, +}); +// 琛ㄦ牸閫夋嫨鏁版嵁 +const handleSelectionChange = (selection) => { + selectedRows.value = selection; +}; +const formDia = ref() +const upload = reactive({ + // 鏄惁鏄剧ず寮瑰嚭灞傦紙瀹㈡埛瀵煎叆锛� + open: false, + // 寮瑰嚭灞傛爣棰橈紙瀹㈡埛瀵煎叆锛� + title: "", + // 鏄惁绂佺敤涓婁紶 + isUploading: false, + // 璁剧疆涓婁紶鐨勮姹傚ご閮� + headers: { Authorization: "Bearer " + getToken() }, + // 涓婁紶鐨勫湴鍧� + url: import.meta.env.VITE_APP_BASE_API + "/waterEquipmentConsumption/importData", + // 鏂囦欢涓婁紶鍓嶇殑鍥炶皟 + beforeUpload: (file) => { + console.log('鏂囦欢鍗冲皢涓婁紶', file); + // 鍙互鍦ㄦ澶勫仛鏂囦欢绫诲瀷鎴栧ぇ灏忔牎楠� + const isValid = file.type === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' || file.name.endsWith('.xlsx') || file.name.endsWith('.xls'); + if (!isValid) { + proxy.$modal.msgError("鍙兘涓婁紶 Excel 鏂囦欢"); + } + return isValid; + }, + // 鏂囦欢鐘舵�佹敼鍙樻椂鐨勫洖璋� + onChange: (file, fileList) => { + console.log('鏂囦欢鐘舵�佹敼鍙�', file, fileList); + }, + // 鏂囦欢涓婁紶鎴愬姛鏃剁殑鍥炶皟 + onSuccess: (response, file, fileList) => { + console.log('涓婁紶鎴愬姛', response, file, fileList); + if(response.code === 200){ + proxy.$modal.msgSuccess("鏂囦欢涓婁紶鎴愬姛"); + }else if(response.code === 500){ + proxy.$modal.msgError(response.msg); + }else{ + proxy.$modal.msgError("鏂囦欢涓婁紶澶辫触"); + } + upload.open = false; + getList(); + }, + // 鏂囦欢涓婁紶澶辫触鏃剁殑鍥炶皟 + onError: (error, file, fileList) => { + console.log('涓婁紶澶辫触', error, file, fileList); + proxy.$modal.msgError("鏂囦欢涓婁紶澶辫触"); + upload.open = false; + }, + // 鏂囦欢涓婁紶杩涘害鏀瑰彉鏃剁殑鍥炶皟 + onProgress: (event, file, fileList) => { + console.log('涓婁紶杩涘害', event, file, fileList); + upload.isUploading = true; + }, +}); + +// 鏌ヨ鍒楄〃 +/** 鎼滅储鎸夐挳鎿嶄綔 */ +const handleQuery = () => { + page.current = 1; + getList(); +}; +const pagination = (obj) => { + page.current = obj.page; + page.size = obj.limit; + getList(); +}; + +const getList = () => { + tableLoading.value = true; + waterEquipmentListPage({ ...searchForm.value, ...page }).then((res) => { + tableLoading.value = false; + tableData.value = res.data.records; + page.total = res.data.total; + }).catch(() => { + tableLoading.value = false; + }) +}; + +// 鎵撳紑寮规 +const openForm = (type, row) => { + nextTick(() => { + formDia.value?.openDialog(type, row) + }) +}; + +/** 瀵煎叆鎸夐挳鎿嶄綔 */ +function handleImport() { + upload.title = "鐢ㄦ按璁惧"; + upload.open = true; + // 娓呯┖涓婃涓婁紶鐨勬枃浠跺垪琛� + nextTick(() => { + proxy.$refs["uploadRef"]?.clearFiles(); + }); +} +function importTemplate() { + proxy.download( + "/waterEquipmentConsumption/export", + {}, + '鐢ㄦ按璁惧瀵煎叆妯$増.xlsx' + ); +} +/** 鎻愪氦涓婁紶鏂囦欢 */ +function submitFileForm() { + proxy.$refs["uploadRef"].submit(); +} + +/** 寮规鍏抽棴鏃舵竻绌烘枃浠跺垪琛� */ +function handleDialogClose() { + nextTick(() => { + proxy.$refs["uploadRef"]?.clearFiles(); + }); +} + +const handleDelete = () => { + let ids = []; + if (selectedRows.value.length > 0) { + ids = selectedRows.value.map((item) => item.id); + } else { + proxy.$modal.msgWarning("璇烽�夋嫨鏁版嵁"); + return; + } + ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚垹闄わ紝鏄惁纭鍒犻櫎锛�", "鍒犻櫎鎻愮ず", { + confirmButtonText: "纭", + cancelButtonText: "鍙栨秷", + type: "warning", + }) + .then(() => { + tableLoading.value = true; + waterEquipmentDelete(ids) + .then((res) => { + proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛"); + getList(); + }) + .finally(() => { + tableLoading.value = false; + }); + }) + .catch(() => { + proxy.$modal.msg("宸插彇娑�"); + }); +}; +onMounted(() => { + getList(); +}); +</script> + +<style scoped> + +</style> diff --git a/src/views/energyManagement/waterManagement/waterBill.vue b/src/views/energyManagement/waterManagement/waterBill.vue new file mode 100644 index 0000000..ea382f0 --- /dev/null +++ b/src/views/energyManagement/waterManagement/waterBill.vue @@ -0,0 +1,181 @@ +<template> + <div class="app-container"> + <div class="search_form"> + <div> + <span class="search_title">璁惧鍚嶇О锛�</span> + <el-input + v-model="searchForm.name" + style="width: 240px" + placeholder="璇疯緭鍏�" + @change="handleQuery" + clearable + :prefix-icon="Search" + /> + <el-button type="primary" @click="handleQuery" style="margin-left: 10px" + >鎼滅储</el-button + > + </div> + <div> + <el-button type="primary" @click="openForm('add')">鏂板</el-button> + <el-button type="danger" plain @click="handleDelete">鍒犻櫎</el-button> + </div> + </div> + <div class="table_list"> + <PIMTable + rowKey="id" + :column="tableColumn" + :tableData="tableData" + :page="page" + :isSelection="true" + @selection-change="handleSelectionChange" + :tableLoading="tableLoading" + @pagination="pagination" + ></PIMTable> + </div> + <form-dia ref="formDia" @close="handleQuery"></form-dia> + </div> +</template> + +<script setup> +import {Search} from "@element-plus/icons-vue"; +import {onMounted, ref, reactive, nextTick} from "vue"; +import FormDia from "@/views/energyManagement/waterManagement/components/waterBillForm.vue"; +import {ElMessageBox} from "element-plus"; +import {waterBillDelete, waterBillListPage} from "@/api/energyManagement/waterManagement.js"; +const { proxy } = getCurrentInstance(); + +const data = reactive({ + searchForm: { + name: "", + }, +}); +const { searchForm } = toRefs(data); + +const selectedRows = ref([]); +const tableColumn = ref([ + { + label: "璁惧鍚嶇О", + prop: "name", + width: 200, + }, + { + label: "瑙勬牸鍨嬪彿", + prop: "code", + width: 200, + }, + { + label: "鐢ㄦ按閲�", + prop: "waterConsumption", + }, + { + label: "姘磋垂鍗曚环", + prop: "waterPrice", + }, + { + label: "姘磋垂閲戦", + prop: "waterBill", + width:150 + }, + { + label: "璁¤垂鏃ユ湡", + prop: "billDate", + width: 150, + }, + { + label: "鐢ㄦ按绫诲瀷", + prop: "waterType", + width:120 + }, + { + dataType: "action", + label: "鎿嶄綔", + align: "center", + fixed: 'right', + operation: [ + { + name: "缂栬緫", + type: "text", + clickFun: (row) => { + openForm("edit", row); + }, + }, + ], + }, +]); +const tableData = ref([]); +const tableLoading = ref(false); +const page = reactive({ + current: 1, + size: 100, + total: 0, +}); +// 琛ㄦ牸閫夋嫨鏁版嵁 +const handleSelectionChange = (selection) => { + selectedRows.value = selection; +}; +const formDia = ref() + +// 鏌ヨ鍒楄〃 +/** 鎼滅储鎸夐挳鎿嶄綔 */ +const handleQuery = () => { + page.current = 1; + getList(); +}; +const pagination = (obj) => { + page.current = obj.page; + page.size = obj.limit; + getList(); +}; + +const getList = () => { + tableLoading.value = true; + waterBillListPage({ ...searchForm.value, ...page }).then((res) => { + tableLoading.value = false; + tableData.value = res.data.records; + page.total = res.data.total; + }); +}; + +// 鎵撳紑寮规 +const openForm = (type, row) => { + nextTick(() => { + formDia.value?.openDialog(type, row) + }) +}; + +const handleDelete = () => { + let ids = []; + if (selectedRows.value.length > 0) { + ids = selectedRows.value.map((item) => item.id); + } else { + proxy.$modal.msgWarning("璇烽�夋嫨鏁版嵁"); + return; + } + ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚垹闄わ紝鏄惁纭鍒犻櫎锛�", "鍒犻櫎鎻愮ず", { + confirmButtonText: "纭", + cancelButtonText: "鍙栨秷", + type: "warning", + }) + .then(() => { + tableLoading.value = true; + waterBillDelete(ids) + .then((res) => { + proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛"); + getList(); + }) + .finally(() => { + tableLoading.value = false; + }); + }) + .catch(() => { + proxy.$modal.msg("宸插彇娑�"); + }); +}; +onMounted(() => { + getList(); +}); +</script> + +<style scoped> + +</style> diff --git a/src/views/energyManagement/waterManagement/waterTrends.vue b/src/views/energyManagement/waterManagement/waterTrends.vue new file mode 100644 index 0000000..12e45fc --- /dev/null +++ b/src/views/energyManagement/waterManagement/waterTrends.vue @@ -0,0 +1,118 @@ +<template> + <div class="app-container"> + <div class="search_form"> + <div> + <span class="search_title">璁惧鍚嶇О锛�</span> + <el-input + v-model="searchForm.name" + style="width: 240px" + placeholder="璇疯緭鍏�" + @change="handleQuery" + clearable + :prefix-icon="Search" + /> + <el-button type="primary" @click="handleQuery" style="margin-left: 10px" + >鎼滅储</el-button + > + </div> + </div> + <div class="table_list"> + <PIMTable + rowKey="id" + :column="tableColumn" + :tableData="tableData" + :page="page" + :isSelection="true" + @selection-change="handleSelectionChange" + :tableLoading="tableLoading" + @pagination="pagination" + ></PIMTable> + </div> + </div> +</template> + +<script setup> +import {Search} from "@element-plus/icons-vue"; +import {onMounted, ref, reactive} from "vue"; +import {listPageByWaterTrend} from "@/api/energyManagement/waterManagement.js"; + +const data = reactive({ + searchForm: { + name: "", + }, +}); +const { searchForm } = toRefs(data); + +const selectedRows = ref([]); +const tableColumn = ref([ + { + label: "璁惧鍚嶇О", + prop: "name", + width: 220, + }, + { + label: "瑙勬牸鍨嬪彿", + prop: "code", + width: 220, + }, + { + label: "杩愯鏃堕棿", + prop: "runDate", + width: 250, + }, + { + label: "鏄ㄦ棩鐢ㄦ按閲�", + prop: "toDayNum", + }, + { + label: "鏈湀骞冲潎姘撮噺", + prop: "avgNum", + width:150 + }, + { + label: "瓒嬪娍", + prop: "trend", + width: 220, + }, +]); +const tableData = ref([]); +const tableLoading = ref(false); +const page = reactive({ + current: 1, + size: 100, + total: 0, +}); +// 琛ㄦ牸閫夋嫨鏁版嵁 +const handleSelectionChange = (selection) => { + selectedRows.value = selection; +}; + +// 鏌ヨ鍒楄〃 +/** 鎼滅储鎸夐挳鎿嶄綔 */ +const handleQuery = () => { + page.current = 1; + getList(); +}; +const pagination = (obj) => { + page.current = obj.page; + page.size = obj.limit; + getList(); +}; + +const getList = () => { + tableLoading.value = true; + listPageByWaterTrend({ ...searchForm.value, ...page }).then((res) => { + tableLoading.value = false; + tableData.value = res.data.records; + page.total = res.data.total; + }); +}; + +onMounted(() => { + getList(); +}); +</script> + +<style scoped> + +</style> diff --git a/src/views/equipmentManagement/gasTank/simple.vue b/src/views/equipmentManagement/gasTank/simple.vue new file mode 100644 index 0000000..92e88df --- /dev/null +++ b/src/views/equipmentManagement/gasTank/simple.vue @@ -0,0 +1,566 @@ +<template> + <div class="app-container"> + <!-- 椤甸潰鏍囬 --> + <div class="page-header"> + <h2>閲嶅瀷缃愬紡璐ц溅鐩戞帶</h2> + <div class="header-actions"> +<!-- <el-button type="primary" @click="addTank">鏂板鍌ㄧ綈</el-button>--> +<!-- <el-button @click="exportData">瀵煎嚭鏁版嵁</el-button>--> + </div> + </div> + + <!-- 鍥涗釜涓昏妯″潡 --> + <div class="modules-container"> + <!-- 1. 鍩烘湰淇℃伅妯″潡 --> + <el-card class="module-card"> + <template #header> + <div class="card-header"> + <span>1. 鍩烘湰淇℃伅</span> + <el-button type="text" @click="handleEditBasicInfo">缂栬緫</el-button> + </div> + </template> + <div class="info-grid"> + <div class="info-item"> + <label>鍌ㄧ綈缂栧彿锛�</label> + <span>{{ basicInfo.tankCode }}</span> + </div> + <div class="info-item"> + <label>鍌ㄧ綈鍚嶇О锛�</label> + <span>{{ basicInfo.tankName }}</span> + </div> + <div class="info-item"> + <label>鍌ㄧ綈绫诲瀷锛�</label> + <span>{{ basicInfo.tankType }}</span> + </div> + <div class="info-item"> + <label>璁捐鍘嬪姏锛�</label> + <span>{{ basicInfo.designPressure }} MPa</span> + </div> + <div class="info-item"> + <label>宸ヤ綔鍘嬪姏锛�</label> + <span>{{ basicInfo.workingPressure }} MPa</span> + </div> + <div class="info-item"> + <label>瀹圭Н锛�</label> + <span>{{ basicInfo.volume }} m鲁</span> + </div> + </div> + </el-card> + + <!-- 2. 鐩戞祴鍙傛暟妯″潡 --> + <el-card class="module-card"> + <template #header> + <div class="card-header"> + <span>2. 鐩戞祴鍙傛暟</span> + <el-button type="text" @click="refreshMonitoring">鍒锋柊</el-button> + </div> + </template> + <div class="monitoring-grid"> + <div class="monitor-item"> + <div class="monitor-label">鍘嬪姏</div> + <div class="monitor-value" :class="getStatusClass(monitoringData.pressureStatus)"> + {{ monitoringData.pressure }} MPa + </div> + <div class="monitor-status">{{ monitoringData.pressureStatus === 'normal' ? '姝e父' : '寮傚父' }}</div> + </div> + <div class="monitor-item"> + <div class="monitor-label">娓╁害</div> + <div class="monitor-value" :class="getStatusClass(monitoringData.temperatureStatus)"> + {{ monitoringData.temperature }} 鈩� + </div> + <div class="monitor-status">{{ monitoringData.temperatureStatus === 'normal' ? '姝e父' : '寮傚父' }}</div> + </div> + <div class="monitor-item"> + <div class="monitor-label">姘斾綋娴撳害</div> + <div class="monitor-value" :class="getStatusClass(monitoringData.gasStatus)"> + {{ monitoringData.gasConcentration }} ppm + </div> + <div class="monitor-status">{{ monitoringData.gasStatus === 'normal' ? '姝e父' : '寮傚父' }}</div> + </div> + <div class="monitor-item"> + <div class="monitor-label">娴侀噺</div> + <div class="monitor-value" :class="getStatusClass(monitoringData.flowStatus)"> + {{ monitoringData.flow }} m鲁/h + </div> + <div class="monitor-status">{{ monitoringData.flowStatus === 'normal' ? '姝e父' : '寮傚父' }}</div> + </div> + </div> + </el-card> + + <!-- 3. 瀹夊叏瑁呯疆妯″潡 --> + <el-card class="module-card"> + <template #header> + <div class="card-header"> + <span>3. 瀹夊叏瑁呯疆</span> + <el-button type="text" @click="checkSafetyDevices">妫�鏌�</el-button> + </div> + </template> + <div class="safety-grid"> + <div class="safety-item" v-for="device in safetyDevices" :key="device.name"> + + <div class="device-info"> + <div class="device-name">{{ device.name }}</div> + <div class="device-status" :class="device.status"> + {{ device.status === 'normal' ? '姝e父' : '寮傚父' }} + </div> + </div> + </div> + </div> + </el-card> + + <!-- 4. 缁存姢璁板綍妯″潡 --> + <el-card class="module-card"> + <template #header> + <div class="card-header"> + <span>4. 缁存姢璁板綍</span> + <el-button type="text" @click="addMaintenanceRecord">娣诲姞璁板綍</el-button> + </div> + </template> + <div class="maintenance-list"> + <div class="maintenance-item" v-for="record in maintenanceRecords" :key="record.id"> + <div class="record-header"> + <span class="record-date">{{ record.date }}</span> + <el-tag :type="record.type === 'inspection' ? 'primary' : 'success'" size="small"> + {{ record.type === 'inspection' ? '妫�楠�' : '缁存姢' }} + </el-tag> + </div> + <div class="record-content"> + <div class="record-title">{{ record.title }}</div> + <div class="record-desc">{{ record.description }}</div> + <div class="record-operator">鎿嶄綔浜猴細{{ record.operator }}</div> + </div> + </div> + </div> + </el-card> + </div> + + <!-- 缂栬緫鍩烘湰淇℃伅寮圭獥 --> + <el-dialog v-model="basicInfoDialogVisible" title="缂栬緫鍩烘湰淇℃伅" width="600px"> + <el-form :model="editBasicInfo" label-width="120px"> + <el-form-item label="鍌ㄧ綈缂栧彿"> + <el-input v-model="editBasicInfo.tankCode" /> + </el-form-item> + <el-form-item label="鍌ㄧ綈鍚嶇О"> + <el-input v-model="editBasicInfo.tankName" /> + </el-form-item> + <el-form-item label="鍌ㄧ綈绫诲瀷"> + <el-select v-model="editBasicInfo.tankType" style="width: 100%"> + <el-option label="娑插寲姘斾綋鍌ㄧ綈" value="娑插寲姘斾綋鍌ㄧ綈" /> + <el-option label="鍘嬪姏瀹瑰櫒" value="鍘嬪姏瀹瑰櫒" /> + <el-option label="甯稿帇鍌ㄧ綈" value="甯稿帇鍌ㄧ綈" /> + </el-select> + </el-form-item> + <el-form-item label="璁捐鍘嬪姏"> + <el-input-number v-model="editBasicInfo.designPressure" :precision="2" style="width: 100%" /> + </el-form-item> + <el-form-item label="宸ヤ綔鍘嬪姏"> + <el-input-number v-model="editBasicInfo.workingPressure" :precision="2" style="width: 100%" /> + </el-form-item> + <el-form-item label="瀹圭Н"> + <el-input-number v-model="editBasicInfo.volume" :precision="2" style="width: 100%" /> + </el-form-item> + </el-form> + <template #footer> + <el-button @click="basicInfoDialogVisible = false">鍙栨秷</el-button> + <el-button type="primary" @click="saveBasicInfo">淇濆瓨</el-button> + </template> + </el-dialog> + + <!-- 娣诲姞缁存姢璁板綍寮圭獥 --> + <el-dialog v-model="maintenanceDialogVisible" title="娣诲姞缁存姢璁板綍" width="600px"> + <el-form :model="newMaintenanceRecord" label-width="120px"> + <el-form-item label="璁板綍绫诲瀷"> + <el-select v-model="newMaintenanceRecord.type" style="width: 100%"> + <el-option label="妫�楠�" value="inspection" /> + <el-option label="缁存姢" value="maintenance" /> + </el-select> + </el-form-item> + <el-form-item label="鏍囬"> + <el-input v-model="newMaintenanceRecord.title" /> + </el-form-item> + <el-form-item label="鎻忚堪"> + <el-input type="textarea" v-model="newMaintenanceRecord.description" :rows="3" /> + </el-form-item> + <el-form-item label="鎿嶄綔浜�"> + <el-input v-model="newMaintenanceRecord.operator" /> + </el-form-item> + </el-form> + <template #footer> + <el-button @click="maintenanceDialogVisible = false">鍙栨秷</el-button> + <el-button type="primary" @click="saveMaintenanceRecord">淇濆瓨</el-button> + </template> + </el-dialog> + </div> +</template> + +<script setup> +import { ref, reactive, onMounted } from 'vue' +import { ElMessage } from 'element-plus' + +// 鍩烘湰淇℃伅 +const basicInfo = reactive({ + tankCode: 'GT001', + tankName: '娑插寲姘斿偍缃怉', + tankType: '娑插寲姘斾綋鍌ㄧ綈', + designPressure: 1.6, + workingPressure: 0.8, + volume: 100.5 +}) + +// 鐩戞祴鍙傛暟 +const monitoringData = reactive({ + pressure: 0.8, + pressureStatus: 'normal', + temperature: 25.5, + temperatureStatus: 'normal', + gasConcentration: 0.1, + gasStatus: 'normal', + flow: 15.2, + flowStatus: 'normal' +}) + +// 瀹夊叏瑁呯疆 +const safetyDevices = ref([ + { name: '瀹夊叏闃�', status: 'normal' }, + { name: '鍘嬪姏浼犳劅鍣�', status: 'normal' }, + { name: '娓╁害浼犳劅鍣�', status: 'normal' }, + { name: '姘斾綋妫�娴嬪櫒', status: 'normal' }, + { name: '鐖嗙牬鐗�', status: 'normal' }, + { name: '娉勫帇瑁呯疆', status: 'normal' } +]) + +// 缁存姢璁板綍 +const maintenanceRecords = ref([ + { + id: 1, + date: '2024-01-15', + type: 'inspection', + title: '骞村害妫�楠�', + description: '鎸夌収TSG 21-2016鏍囧噯杩涜骞村害妫�楠岋紝璁惧鐘舵�佽壇濂�', + operator: '寮犲伐绋嬪笀' + }, + { + id: 2, + date: '2024-02-20', + type: 'maintenance', + title: '瀹夊叏闃�缁存姢', + description: '鏇存崲瀹夊叏闃�瀵嗗皝鍦堬紝鏍″噯鍘嬪姏璁惧畾鍊�', + operator: '鏉庢妧甯�' + }, + { + id: 3, + date: '2024-03-10', + type: 'inspection', + title: '鍘嬪姏娴嬭瘯', + description: '杩涜鍘嬪姏瀹瑰櫒姘村帇璇曢獙锛岀鍚堣璁¤姹�', + operator: '鐜嬫楠屽憳' + } +]) + +// 寮圭獥鎺у埗 +const basicInfoDialogVisible = ref(false) +const maintenanceDialogVisible = ref(false) + +// 缂栬緫琛ㄥ崟鏁版嵁 +const editBasicInfo = reactive({ ...basicInfo }) +const newMaintenanceRecord = reactive({ + type: 'inspection', + title: '', + description: '', + operator: '' +}) + +// 鑾峰彇鐘舵�佹牱寮忕被 +const getStatusClass = (status) => { + return status === 'normal' ? 'status-normal' : 'status-warning' +} + +// 鏂板鍌ㄧ綈 +const addTank = () => { + ElMessage.success('鏂板鍌ㄧ綈鍔熻兘') +} + +// 瀵煎嚭鏁版嵁 +const exportData = () => { + ElMessage.success('瀵煎嚭鎴愬姛') +} + +// 缂栬緫鍩烘湰淇℃伅 +const handleEditBasicInfo = () => { + Object.assign(editBasicInfo, basicInfo) + basicInfoDialogVisible.value = true +} + +// 淇濆瓨鍩烘湰淇℃伅 +const saveBasicInfo = () => { + Object.assign(basicInfo, editBasicInfo) + basicInfoDialogVisible.value = false + ElMessage.success('淇濆瓨鎴愬姛') +} + +// 鍒锋柊鐩戞祴鏁版嵁 +const refreshMonitoring = () => { + // 妯℃嫙鏁版嵁鏇存柊 + monitoringData.pressure = (Math.random() * 0.5 + 0.6).toFixed(2) + monitoringData.temperature = (Math.random() * 10 + 20).toFixed(1) + monitoringData.gasConcentration = (Math.random() * 0.2).toFixed(2) + monitoringData.flow = (Math.random() * 10 + 10).toFixed(1) + ElMessage.success('鏁版嵁宸插埛鏂�') +} + +// 妫�鏌ュ畨鍏ㄨ缃� +const checkSafetyDevices = () => { + // 妯℃嫙妫�鏌ヨ繃绋� + safetyDevices.value.forEach(device => { + device.status = Math.random() > 0.1 ? 'normal' : 'warning' + }) + ElMessage.success('瀹夊叏瑁呯疆妫�鏌ュ畬鎴�') +} + +// 娣诲姞缁存姢璁板綍 +const addMaintenanceRecord = () => { + newMaintenanceRecord.type = 'inspection' + newMaintenanceRecord.title = '' + newMaintenanceRecord.description = '' + newMaintenanceRecord.operator = '' + maintenanceDialogVisible.value = true +} + +// 淇濆瓨缁存姢璁板綍 +const saveMaintenanceRecord = () => { + const record = { + id: Date.now(), + date: new Date().toISOString().split('T')[0], + ...newMaintenanceRecord + } + maintenanceRecords.value.unshift(record) + maintenanceDialogVisible.value = false + ElMessage.success('璁板綍娣诲姞鎴愬姛') +} + +// 妯℃嫙瀹炴椂鏁版嵁鏇存柊 +onMounted(() => { + setInterval(() => { + monitoringData.pressure = (Math.random() * 0.5 + 0.6).toFixed(2) + monitoringData.temperature = (Math.random() * 10 + 20).toFixed(1) + monitoringData.gasConcentration = (Math.random() * 0.2).toFixed(2) + monitoringData.flow = (Math.random() * 10 + 10).toFixed(1) + }, 5000) +}) +</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; + } + + .header-actions { + display: flex; + gap: 10px; + } +} + +.modules-container { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 20px; +} + +.module-card { + .card-header { + display: flex; + justify-content: space-between; + align-items: center; + font-weight: bold; + color: #303133; + } +} + +.info-grid { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 15px; + + .info-item { + display: flex; + justify-content: space-between; + padding: 10px; + background: #f8f9fa; + border-radius: 4px; + + label { + font-weight: bold; + color: #606266; + } + + span { + color: #303133; + } + } +} + +.monitoring-grid { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 15px; + + .monitor-item { + text-align: center; + padding: 15px; + background: #f8f9fa; + border-radius: 8px; + border: 2px solid transparent; + + .monitor-label { + font-size: 14px; + color: #606266; + margin-bottom: 8px; + } + + .monitor-value { + font-size: 20px; + font-weight: bold; + margin-bottom: 5px; + + &.status-normal { + color: #67c23a; + } + + &.status-warning { + color: #e6a23c; + } + } + + .monitor-status { + font-size: 12px; + color: #909399; + } + } +} + +.safety-grid { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 15px; + + .safety-item { + display: flex; + align-items: center; + padding: 15px; + background: #f8f9fa; + border-radius: 8px; + border: 2px solid transparent; + + .device-icon { + margin-right: 15px; + } + + .device-info { + flex: 1; + + .device-name { + font-weight: bold; + color: #303133; + margin-bottom: 5px; + } + + .device-status { + font-size: 12px; + padding: 2px 8px; + border-radius: 10px; + display: inline-block; + + &.normal { + background: #f0f9ff; + color: #409eff; + } + + &.warning { + background: #fef7e0; + color: #e6a23c; + } + } + } + } +} + +.maintenance-list { + max-height: 300px; + overflow-y: auto; + + .maintenance-item { + padding: 15px; + border-bottom: 1px solid #ebeef5; + margin-bottom: 10px; + + &:last-child { + border-bottom: none; + margin-bottom: 0; + } + + .record-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 8px; + + .record-date { + font-size: 14px; + color: #909399; + } + } + + .record-content { + .record-title { + font-weight: bold; + color: #303133; + margin-bottom: 5px; + } + + .record-desc { + font-size: 14px; + color: #606266; + margin-bottom: 5px; + line-height: 1.4; + } + + .record-operator { + font-size: 12px; + color: #909399; + } + } + } +} + +// 鍝嶅簲寮忚璁� +@media (max-width: 1200px) { + .modules-container { + grid-template-columns: 1fr; + } +} + +@media (max-width: 768px) { + .info-grid, + .monitoring-grid, + .safety-grid { + grid-template-columns: 1fr; + } +} +</style> 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