From e986cee1c804ecdf6d03c080ce9a8bb187f724a4 Mon Sep 17 00:00:00 2001 From: zhang_12370 <z2864490065@outlook.com> Date: 星期一, 14 七月 2025 18:02:50 +0800 Subject: [PATCH] 1、煤质字段添加校验 使其唯一 2、优化首页 数据刷新 3、开发配煤计算器 --- src/views/basicInformation/index.vue | 10 src/views/index.vue | 3 src/views/basicInformation/mould/coalMeiZhiZiDuanWeiHu.vue | 62 ++ src/api/calculator/index.js | 11 src/api/publicApi/index.js | 10 src/views/calculator/index copy.vue | 1380 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 1,469 insertions(+), 7 deletions(-) diff --git a/src/api/calculator/index.js b/src/api/calculator/index.js new file mode 100644 index 0000000..f20ee1b --- /dev/null +++ b/src/api/calculator/index.js @@ -0,0 +1,11 @@ +import request from '@/utils/request' + +// /officialInventory/coalBlendingList +// 鑾峰彇鐓ょ偔閰嶆瘮鍒楄〃 +export function getCoalBlendingList(query) { + return request({ + url: '/officialInventory/coalBlendingList', + method: 'get', + params: query + }) +} \ No newline at end of file diff --git a/src/api/publicApi/index.js b/src/api/publicApi/index.js index a226d22..8022592 100644 --- a/src/api/publicApi/index.js +++ b/src/api/publicApi/index.js @@ -29,4 +29,14 @@ method: 'get', params: query }) +} + +// /coalField/coalFieldList +// 鏌ヨ鐓よ川瀛楁鍒楄〃 +export function getCoalFieldList(query) { + return request({ + url: '/coalField/coalFieldList', + method: 'get', + params: query + }) } \ No newline at end of file diff --git a/src/views/basicInformation/index.vue b/src/views/basicInformation/index.vue index 7d25b6a..77333ef 100644 --- a/src/views/basicInformation/index.vue +++ b/src/views/basicInformation/index.vue @@ -491,10 +491,10 @@ * 鐓よ川鏂规琛ㄦ牸鍒楅厤缃� */ const coalQualityMaintenanceColumns = ref([ - { prop: "plan", label: "鏂规鍚嶇О", minWidth: 100 }, + { prop: "plan", label: "鐓よ川鏂规", minWidth: 100 }, { prop: "fieldIds", - label: "瀛楁鍚嶇О", + label: "鐓よ川瀛楁", minWidth: 200, showOverflowTooltip: true, slot: true, @@ -505,15 +505,15 @@ return cellValue || "--"; }, }, - { prop: "schemeDesc", label: "瀛楁鎻忚堪", minWidth: 100 }, + { prop: "schemeDesc", label: "鐓よ川鎻忚堪", minWidth: 100 }, ]); /** * 鐓よ川瀛楁琛ㄦ牸鍒楅厤缃� */ const coalMeiZhiZiDuanWeiHuColumns = ref([ - { prop: "fieldName", label: "瀛楁鍚嶇О", minWidth: 200 }, - { prop: "fieldDescription", label: "瀛楁鎻忚堪", minWidth: 200 }, + { prop: "fieldName", label: "鐓よ川瀛楁", minWidth: 200 }, + { prop: "fieldDescription", label: "鐓よ川鎻忚堪", minWidth: 200 }, ]); // ===== 浜嬩欢澶勭悊鍑芥暟 ===== diff --git a/src/views/basicInformation/mould/coalMeiZhiZiDuanWeiHu.vue b/src/views/basicInformation/mould/coalMeiZhiZiDuanWeiHu.vue index 45cb626..f044fbe 100644 --- a/src/views/basicInformation/mould/coalMeiZhiZiDuanWeiHu.vue +++ b/src/views/basicInformation/mould/coalMeiZhiZiDuanWeiHu.vue @@ -19,6 +19,7 @@ v-model="formData.fieldName" placeholder="璇疯緭鍏ュ瓧娈靛悕绉�" :disabled="isViewMode" + @blur="checkFieldNameExists" /> </el-form-item> <el-form-item label="瀛楁鎻忚堪" prop="fieldDescription"> @@ -41,8 +42,10 @@ </template> <script setup> -import {ref, reactive, watch, defineProps} from "vue"; +import {ref, reactive, watch, defineProps, computed, onMounted} from "vue"; +import {ElMessage} from "element-plus"; import {addOrEditCoalField} from "@/api/basicInformation/coalFieldMaintenance.js"; +import {getCoalFieldList} from "@/api/publicApi/index.js"; const props = defineProps({ form: { @@ -64,6 +67,21 @@ const copyForm = defineModel("copyForm", { required: true, type: Object, +}); + +// 瀛樺偍宸叉湁鐨勫瓧娈靛垪琛� +const existingFields = ref([]); + +// 缁勪欢鎸傝浇鏃惰幏鍙栧凡鏈夊瓧娈靛垪琛� +onMounted(async () => { + try { + const {data, code} = await getCoalFieldList(); + if (code === 200) { + existingFields.value = data || []; + } + } catch (error) { + console.error("鑾峰彇瀛楁鍒楄〃澶辫触", error); + } }); // 琛ㄥ崟寮曠敤 const formRef = ref(); @@ -128,9 +146,49 @@ emit("handleBeforeClose"); emit("update:coalMaintenanceFieldDialogVisible", false); }; + +// 妫�鏌ュ瓧娈靛悕绉版槸鍚﹀凡瀛樺湪 +const checkFieldNameExists = () => { + if (!formData.value.fieldName) return; + + const isNameExists = existingFields.value.some(field => + field.fieldName === formData.value.fieldName + ); + + // 缂栬緫妯″紡涓嬶紝濡傛灉鏄綋鍓嶅瓧娈电殑鍘熷悕绉板垯涓嶆彁绀� + if (isNameExists && !(props.addOrEdit === 'edit' && props.form.fieldName === formData.value.fieldName)) { + ElMessage.warning("璇ュ瓧娈靛悕绉板凡瀛樺湪锛岃鎹㈠叾浠栧悕瀛�"); + // 鍙�夛細鑷姩娓呯┖杈撳叆妗� + // formData.value.fieldName = ''; + } +}; const rules = reactive({ fieldName: [ - {required: true, message: "璇疯緭鍏ョ叅绉嶅悕绉�", trigger: "blur"}, + {required: true, message: "璇疯緭鍏ュ瓧娈靛悕绉�", trigger: "blur"}, + { + validator: (rule, value, callback) => { + if (!value) { + callback(); + return; + } + // 妫�鏌ュ瓧娈靛悕绉版槸鍚﹀凡瀛樺湪 + const isNameExists = existingFields.value.some(field => + field.fieldName === value + ); + + // 缂栬緫妯″紡涓嬶紝濡傛灉鏄綋鍓嶅瓧娈电殑鍘熷悕绉板垯鍏佽 + if (isNameExists) { + if (props.addOrEdit === 'edit' && props.form.fieldName === value) { + callback(); + } else { + callback(new Error("璇ュ瓧娈靛悕绉板凡瀛樺湪锛岃鎹㈠叾浠栧悕瀛�")); + } + } else { + callback(); + } + }, + trigger: "blur" + } ], }); </script> diff --git a/src/views/calculator/index copy.vue b/src/views/calculator/index copy.vue new file mode 100644 index 0000000..4223b80 --- /dev/null +++ b/src/views/calculator/index copy.vue @@ -0,0 +1,1380 @@ +<template> + <div class="app-container"> + <div class="view"> + <div class="left-card"> + <div class="count-region">鏁板�艰緭鍏ュ尯</div> + <div class="scroll"> + <div> + <div class="title">閫氱敤璁剧疆</div> + <el-form + :inline="true" + :model="formInline" + class="demo-form-inline" + label-width="110" + label-position="top" + > + <el-row :gutter="16"> + <el-col :span="8"> + <el-form-item label="寰呴厤鐓ょ鏁伴噺"> + <el-input + v-model="formInline.num" + type="number" + style="width: 100%" + @change="updateCoalFields" + /> + </el-form-item> + </el-col> + <el-col :span="8"> + <el-form-item label="鍙備笌閰嶇叅鎬诲惃鏁�"> + <el-input + v-model="formInline.totalTonnage" + type="number" + style="width: 100%" + > + <template v-slot:suffix> + <i style="font-style: normal">鍚�</i> + </template> + </el-input> + </el-form-item> + </el-col> + <el-col :span="8"> + <el-form-item label="姣忛摬閲嶉噺"> + <el-input + v-model="formInline.scoopWeight" + type="number" + style="width: 100%" + > + <template v-slot:suffix> + <i style="font-style: normal">鍚�</i> + </template> + </el-input> + </el-form-item> + </el-col> + </el-row> + </el-form> + </div> + <div> + <div class="title">鐓ょ灞炴��</div> + <div class="coal-forms-container"> + <el-form + :model="coalForms" + :inline="true" + label-width="110" + label-position="top" + > + <div + v-for="(item, index) in coalForms" + :key="index" + style="margin-bottom: 15px" + > + <el-row :gutter="16"> + <el-col :span="6"> + <el-form-item label="鐓ょ绫诲瀷"> + <el-select + v-model="item.type" + placeholder="璇烽�夋嫨" + style="width: 100%" + @change="handleCoalTypeChange(index)" + > + <el-option label="宸叉湁鐓�" value="宸叉湁鐓�" /> + <el-option label="鏈煡鐓�" value="鏈煡鐓�" /> + </el-select> + </el-form-item> + </el-col> + <el-col :span="6"> + <el-form-item :label="'鐓ょ' + (index + 1)"> + <div class="input-wrapper"> + <el-input + v-model="item.coalId" + v-show="item.type !== '宸叉湁鐓�'" + placeholder="璇疯緭鍏�" + style="width: 100%" + /> + + <el-select + v-model="item.coalId" + :value=" + infoCoals.find((coal) => coal.key === item.coalId) + ?.value || '' + " + v-show="item.type === '宸叉湁鐓�'" + placeholder="璇烽�夋嫨" + style="width: 100%" + > + <el-option + v-for="ele in infoCoals" + :key="ele.key" + :label="ele.value" + :value="ele.key" + >{{ ele.value }} + </el-option> + </el-select> + </div> + </el-form-item> + </el-col> + <el-col :span="6"> + <el-form-item label="鍙戠儹閲�"> + <el-input + v-model="item.cv" + type="number" + style="width: 100%" + > + <template v-slot:suffix> + <i style="font-style: normal">kcal/kg</i> + </template> + </el-input> + </el-form-item> + </el-col> + <el-col :span="6"> + <el-form-item label="浠锋牸"> + <el-input + v-if="item.type !== '鏈煡鐓�'" + :value=" + infoCoals.find((coal) => coal.key === item.coalId) + ?.item.priceExcludingTax || '' + " + type="number" + style="width: 100%" + :disabled="item.type === '宸叉湁鐓�'" + > + <template v-slot:suffix> + <i style="font-style: normal">鍏�/鍚�</i> + </template> + </el-input> + <el-input + v-else + v-model="item.price" + type="number" + style="width: 100%" + > + <template v-slot:suffix> + <i style="font-style: normal">鍏�/鍚�</i> + </template> + </el-input> + </el-form-item> + </el-col> + </el-row> + <el-row :gutter="16"> + <el-col :span="6"> + <el-form-item label="纭垎"> + <el-input + v-if="item.type !== '鏈煡鐓�'" + :disabled="item.type === '宸叉湁鐓�'" + :value=" + infoCoals.find((coal) => coal.key === item.coalId) + ?.item.coalValues.find((value) => value.fieldName === '纭垎')?.coalValue || '0' + " + type="number" + placeholder="鍙��" + style="width: 100%" + > + <template v-slot:suffix> + <i style="font-style: normal">%</i> + </template> + </el-input> + <el-input + v-else + v-model="item.sulfur" + type="number" + placeholder="鍙��" + style="width: 100%" + > + <template v-slot:suffix> + <i style="font-style: normal">%</i> + </template> + </el-input> + </el-form-item> + </el-col> + <el-col :span="6"> + <el-form-item label="鐏板垎"> + <el-input + v-if="item.type !== '鏈煡鐓�'" + :disabled="item.type === '宸叉湁鐓�'" + :value=" + infoCoals.find((coal) => coal.key === item.coalId) + ?.item.coalValues.find((value) => value.fieldName === '鐏板垎')?.coalValue || '0' + " + type="number" + placeholder="鍙��" + style="width: 100%" + > + <template v-slot:suffix> + <i style="font-style: normal">%</i> + </template> + </el-input> + <el-input + v-else + v-model="item.ash" + type="number" + placeholder="鍙��" + style="width: 100%" + > + <template v-slot:suffix> + <i style="font-style: normal">%</i> + </template> + </el-input> + </el-form-item> + </el-col> + <el-col :span="6"> + <el-form-item label="姘村垎"> + <el-input + v-if="item.type !== '鏈煡鐓�'" + :disabled="item.type === '宸叉湁鐓�'" + :value=" + infoCoals.find((coal) => coal.key === item.coalId) + ?.item.coalValues.find((value) => value.fieldName === '姘村垎')?.coalValue || '0' + " + type="number" + placeholder="鍙��" + style="width: 100%" + > + <template v-slot:suffix> + <i style="font-style: normal">%</i> + </template> + </el-input> + <el-input + v-else + v-model="item.moisture" + type="number" + placeholder="鍙��" + style="width: 100%" + > + <template v-slot:suffix> + <i style="font-style: normal">%</i> + </template> + </el-input> + </el-form-item> + </el-col> + </el-row> + <el-divider /> + </div> + </el-form> + </div> + </div> + <div> + <div class="title">閰嶇叅绾︽潫鏉′欢</div> + <el-form + :inline="true" + :model="constraints" + class="demo-form-inline" + label-width="110" + label-position="top" + > + <el-row :gutter="16"> + <el-col :span="6"> + <el-form-item label="娣峰悎鐓ゆ渶浣庡彂鐑噺(CV)"> + <el-input + v-model="constraints.minCalorific" + type="number" + style="width: 100%" + > + <template v-slot:suffix> + <i style="font-style: normal">kcal/kg</i> + </template> + </el-input> + </el-form-item> + </el-col> + <el-col :span="6"> + <el-form-item label="娣峰悎鐓ゆ渶楂樼~鍒�"> + <el-input + v-model="constraints.maxSulfur" + type="number" + placeholder="鍙��" + style="width: 100%" + > + <template v-slot:suffix> + <i style="font-style: normal">%</i> + </template> + </el-input> + </el-form-item> + </el-col> + <el-col :span="6"> + <el-form-item label="娣峰悎鐓ゆ渶楂樼伆鍒�"> + <el-input + v-model="constraints.maxAsh" + type="number" + placeholder="鍙��" + style="width: 100%" + > + <template v-slot:suffix> + <i style="font-style: normal">%</i> + </template> + </el-input> + </el-form-item> + </el-col> + <el-col :span="6"> + <el-form-item label="娣峰悎鐓ゆ渶楂樻按鍒�"> + <el-input + v-model="constraints.maxMoisture" + type="number" + placeholder="鍙��" + style="width: 100%" + > + <template v-slot:suffix> + <i style="font-style: normal">%</i> + </template> + </el-input> + </el-form-item> + </el-col> + </el-row> + </el-form> + </div> + </div> + <div class="footer"> + <el-button @click="cancel">閲嶇疆</el-button> + <el-button type="primary" @click="addWarehoused" plain> + 娣诲姞鑷冲緟鍏ュ簱 + </el-button> + <el-button type="primary" @click="submitForm">璁$畻鏈�浼橀厤姣�</el-button> + </div> + </div> + <div class="right-card"> + <div class="count-region">閰嶇叅浼樺寲缁撴灉</div> + <div class="result-scroll"> + <!-- 閿欒淇℃伅 --> + <div v-if="result.show && result.error" class="error-box"> + <el-alert + :title="result.error" + type="error" + :closable="false" + show-icon + /> + </div> + + <!-- 鏈�浼橀厤姣旂粨鏋� --> + <div + v-if="result.show && result.optimal && !result.error" + class="result-section" + > + <div class="result-title">馃幆 鏈�浼橀厤姣旂粨鏋�</div> + <!-- 閰嶆瘮琛� --> + <div class="table-container"> + <el-table + :data="result.optimal.instructions" + border + size="small" + class="result-table" + style="width: 100%" + > + <el-table-column prop="coalId" label="鐓ょ" min-width="80"> + <template #default="scope"> + {{ + infoCoals.find((coal) => coal.key === scope.row.coalId) + ?.value || + "" || + matchCoalName(scope.row.coalId) + }} + </template> + </el-table-column> + <el-table-column prop="ratio" label="閰嶆瘮" min-width="80"> + <template #default="scope"> {{ scope.row.ratio }}% </template> + </el-table-column> + <el-table-column prop="quantity" label="鍚ㄦ暟" min-width="80"> + <template #default="scope"> + {{ scope.row.quantity }}鍚� + </template> + </el-table-column> + <el-table-column prop="scoops" label="閾叉暟" min-width="80"> + <template #default="scope"> + {{ scope.row.scoops }}閾� + </template> + </el-table-column> + </el-table> + </div> + + <!-- 娣峰悎鐓ゅ睘鎬� --> + <div class="props-section"> + <div class="props-title">馃搳 娣峰悎鐓ゅ睘鎬�</div> + <div class="props-grid"> + <div class="prop-item"> + <span class="prop-label">鍙戠儹閲�:</span> + <span class="prop-value" + >{{ result.optimal.props.cv.toFixed(2) }} kcal/kg</span + > + </div> + <div class="prop-item"> + <span class="prop-label">纭垎:</span> + <span class="prop-value" + >{{ result.optimal.props.sulfur.toFixed(2) }}%</span + > + </div> + <div class="prop-item"> + <span class="prop-label">鐏板垎:</span> + <span class="prop-value" + >{{ result.optimal.props.ash.toFixed(2) }}%</span + > + </div> + <div class="prop-item"> + <span class="prop-label">姘村垎:</span> + <span class="prop-value" + >{{ result.optimal.props.moisture.toFixed(2) }}%</span + > + </div> + <div class="prop-item"> + <span class="prop-label">鎴愭湰:</span> + <span class="prop-value cost" + >{{ result.optimal.props.cost.toFixed(2) }} 鍏�/鍚�</span + > + </div> + <div class="prop-item"> + <span class="prop-label">鐢熸垚:</span> + <el-autocomplete + v-model="result.optimal.props.createCoal" + :fetch-suggestions="querySearch" + clearable + size="small" + class="inline-input red-border" + style="width: 180px; min-height: 24px !important" + placeholder="璇疯緭鍏ョ敓鎴愮叅绉�" + @blur="handleSelect($event)" + @select="handleSelect($event)" + /> + </div> + </div> + </div> + </div> + + <!-- 澶囬�夋柟妗� --> + <div + v-if="result.show && result.alternatives.length > 0" + class="alternatives-section" + > + <div class="result-title">馃攧 澶囬�夋柟妗�</div> + <div + v-for="(alt, index) in result.alternatives" + :key="index" + class="alt-item" + > + <div class="alt-title">{{ alt.desc }}</div> + <div class="table-container"> + <el-table + :data="alt.instructions" + border + size="small" + class="alt-table" + style="width: 100%" + > + <el-table-column prop="coalId" label="鐓ょ" min-width="80"> + <template #default="scope"> + {{ + infoCoals.find((coal) => coal.key === scope.row.coalId) + ?.value || + "" || + matchCoalName(scope.row.coalId) + }} + </template> + </el-table-column> + <el-table-column prop="ratio" label="閰嶆瘮" min-width="80"> + <template #default="scope"> + {{ scope.row.ratio }}% + </template> + </el-table-column> + <el-table-column prop="quantity" label="鍚ㄦ暟" min-width="80"> + <template #default="scope"> + {{ scope.row.quantity }}鍚� + </template> + </el-table-column> + <el-table-column prop="scoops" label="閾叉暟" min-width="80"> + <template #default="scope"> + {{ scope.row.scoops }}閾� + </template> + </el-table-column> + </el-table> + </div> + + <div class="alt-props"> + <span>鍙戠儹閲�: {{ alt.props.cv.toFixed(2) }} kcal/kg锛�</span> + <span>纭垎: {{ alt.props.sulfur.toFixed(2) }}%锛�</span> + <span>鐏板垎: {{ alt.props.ash.toFixed(2) }}%锛�</span> + <span>姘村垎: {{ alt.props.moisture.toFixed(2) }}%锛�</span> + <span class="cost" + >鎴愭湰: {{ alt.props.cost.toFixed(2) }} 鍏�/鍚�</span + > + </div> + </div> + </div> + <!-- 绌虹姸鎬� --> + <div v-if="!result.show" class="empty-state"> + <el-empty description="鐐瑰嚮宸︿晶璁$畻鏈�浼橀厤姣旀寜閽煡鐪嬬粨鏋�" /> + </div> + </div> + </div> + </div> + </div> +</template> + +<script setup> +import { reactive, toRefs, nextTick, onMounted } from "vue"; +import { ElMessage, ElMessageBox } from "element-plus"; +import { getCoalInfoList } from "@/api/procureMent"; // 鍋囪鏈変竴涓狝PI鑾峰彇鐓ょ淇℃伅 +import { getCoalBlendingList } from "@/api/calculator/index.js"; + +const data = reactive({ + formInline: { + num: 3, // 榛樿3涓叅绉� + totalTonnage: 1000, // 鍙備笌閰嶇叅鎬诲惃鏁� + scoopWeight: 50, // 姣忛摬閲嶉噺 + }, + // 绾︽潫鏉′欢 + constraints: { + minCalorific: 5600, // 娣峰悎鐓ゆ渶浣庡彂鐑噺 + maxSulfur: 1.2, // 娣峰悎鐓ゆ渶楂樼~鍒� + maxAsh: 15.0, // 娣峰悎鐓ゆ渶楂樼伆鍒� + maxMoisture: "", // 娣峰悎鐓ゆ渶楂樻按鍒� + }, + coalForms: [ + { + type: "鏈煡鐓�", + coalId: "鐓", + cv: 6200, // 鍙戠儹閲� + price: 450, // 浠锋牸 + sulfur: 0.6, // 纭垎 + ash: 12.0, // 鐏板垎 + moisture: 8.0, // 姘村垎 + }, + { + type: "鏈煡鐓�", + coalId: "鐓", + cv: 5800, + price: 380, + sulfur: 1.0, + ash: 14.0, + moisture: 10.0, + }, + { + type: "鏈煡鐓�", + coalId: "鐓", + cv: 5400, + price: 320, + sulfur: 1.4, + ash: 16.0, + moisture: 12.0, + }, + ], + // 璁$畻缁撴灉 + result: { + show: false, + optimal: null, + alternatives: [], + error: null, + createCoal: null, + }, +}); +const coalInfoList = ref([]); +// onMounted +const getCoalInfo = async () => { + let result = await getCoalInfoList(); + if (result.code === 200) { + result.data.forEach((item) => { + let obj = { + value: item.coal, + key: item.id, + }; + coalInfoList.value.push(obj); + }); + } else { + ElMessage.error("鑾峰彇鐓ょ淇℃伅澶辫触锛岃绋嶅悗閲嶈瘯"); + } +}; +// 琛ㄦ牸灞曠ず鐢細浼樺厛鐢� key 鍖归厤涓枃鍚嶏紝鍐嶇敤 value 鍖归厤锛屾渶鍚庡師鏍疯繑鍥� +const matchCoalName = (name) => { + if ( + !name || + !Array.isArray(coalInfoList.value) || + coalInfoList.value.length === 0 + ) + return name; + // key 鍖归厤 + const byKey = coalInfoList.value.find( + (item) => String(item.key) === String(name) + ); + if (byKey) return byKey.value; + // value 鍖归厤 + const byValue = coalInfoList.value.find((item) => item.value === name); + if (byValue) return byValue.value; + // 鍘熸牱杩斿洖 + return name; +}; +// 鑷姩琛ュ叏鎼滅储 +const querySearch = (q, cb) => { + const res = q + ? coalInfoList.value.filter((c) => c.value.includes(q)) + : coalInfoList.value; + cb(res); +}; +// 閫夋嫨/澶辩劍鏃讹紝浼樺厛瀛� key锛屾壘涓嶅埌鍒欏瓨鍘熷�� +const handleSelect = (item) => { + const val = item.value || (item.target && item.target.value) || ""; + const found = coalInfoList.value.find( + (c) => c.value === val || c.key === val + ); + result.value.optimal.props.createCoal = found ? found.key : val; + // 鏂板锛氬鏋滃尮閰嶆垚鍔燂紝鐣欎竴涓猧d瀛楁 + if (found) { + result.value.optimal.props.coalId = found.key; + } else { + result.value.optimal.props.coalId = null; + } + let match = matchCoalName(result.value.optimal.props.createCoal); + if (match && match !== result.value.optimal.props.createCoal) { + result.value.optimal.props.createCoal = match; + } +}; +onMounted(async () => { + getCoalInfo(); + geInfoCoals(); +}); +const infoCoals = ref([]); +// 鍒濆鍖栫叅绉嶅瓧娈� +const geInfoCoals = async () => { + let res = await getCoalBlendingList(); + if (res.code === 200) { + infoCoals.value = res.data.map((item) => ({ + value: item.supplierCoal, + key: item.coalId, + item, + })); + console.log(infoCoals.value); + } else { + ElMessage.error("鑾峰彇鐓ょ淇℃伅澶辫触锛岃绋嶅悗閲嶈瘯"); + } +}; +// 绾挎�ц鍒掓眰瑙e嚱鏁� +const solveBlend = (coals, constraints) => { + // 鏁版嵁楠岃瘉 + if (constraints.maxSulfur) { + let missingSulfur = coals.some((coal) => !coal.sulfur && coal.sulfur !== 0); + if (missingSulfur) { + throw new Error( + "濡傛灉璁剧疆浜嗘渶澶х~鍒嗙害鏉燂紝鍒欐墍鏈夊弬涓庨厤姣旂殑鐓ょ閮藉繀椤绘彁渚涚~鍒嗘暟鎹��" + ); + } + } + if (constraints.maxAsh) { + let missingAsh = coals.some((coal) => !coal.ash && coal.ash !== 0); + if (missingAsh) { + throw new Error("濡傛灉璁剧疆浜嗘渶澶х伆鍒嗙害鏉燂紝鍒欐墍鏈夌叅绉嶉兘蹇呴』鎻愪緵鐏板垎鏁版嵁銆�"); + } + } + if (constraints.maxMoisture) { + let missingMoisture = coals.some( + (coal) => !coal.moisture && coal.moisture !== 0 + ); + if (missingMoisture) { + throw new Error("濡傛灉璁剧疆浜嗘渶澶ф按鍒嗙害鏉燂紝鍒欐墍鏈夌叅绉嶉兘蹇呴』鎻愪緵姘村垎鏁版嵁銆�"); + } + } + + // 绠�鍗曠殑绾挎�ц鍒掓眰瑙o紙鏈�灏忓寲鎴愭湰锛� + // 杩欓噷浣跨敤绠�鍖栫殑绠楁硶锛屽疄闄呴」鐩腑鍙互闆嗘垚鏇翠笓涓氱殑姹傝В鍣� + try { + // 妯℃嫙姹傝В杩囩▼ + let totalCoals = coals.length; + let ratios = new Array(totalCoals).fill(0); + + // 绠�鍗曠殑绛夋潈閲嶅垎閰嶄綔涓哄垵濮嬭В + let avgRatio = 1 / totalCoals; + ratios = ratios.map(() => avgRatio); + + // 楠岃瘉绾︽潫鏉′欢 + let blendProps = calcBlendProps(coals, ratios); + + if (constraints.minCalorific && blendProps.cv < constraints.minCalorific) { + // 璋冩暣閰嶆瘮浠ユ弧瓒虫渶浣庡彂鐑噺 + let highCvCoals = coals + .map((coal, i) => ({ index: i, cv: coal.cv })) + .sort((a, b) => b.cv - a.cv); + + ratios = new Array(totalCoals).fill(0); + ratios[highCvCoals[0].index] = 0.6; + ratios[highCvCoals[1] ? highCvCoals[1].index : 0] = 0.4; + } + + return ratios; + } catch (error) { + throw error; + } +}; + +// 璁$畻娣峰悎灞炴�� +const calcBlendProps = (coals, ratios) => { + let cv = 0, + sulfur = 0, + ash = 0, + moisture = 0, + cost = 0; + for (let i = 0; i < coals.length; i++) { + cv += ratios[i] * Number(coals[i].cv || 0); + sulfur += ratios[i] * Number(coals[i].sulfur || 0); + ash += ratios[i] * Number(coals[i].ash || 0); + moisture += ratios[i] * Number(coals[i].moisture || 0); + cost += ratios[i] * Number(coals[i].price || 0); + } + return { cv, sulfur, ash, moisture, cost }; +}; + +// 鐢熸垚鎿嶄綔鎸囦护 +const genInstructions = (coals, ratios, total, scoop) => { + return coals + .map((coal, i) => { + if (ratios[i] < 1e-6) return null; + let quantity = ratios[i] * total; + let scoops = quantity / scoop; + return { + coalId: coal.coalId, + ratio: (ratios[i] * 100).toFixed(2), + quantity: quantity.toFixed(1), + scoops: scoops.toFixed(1), + }; + }) + .filter(Boolean); +}; + +const cancel = () => { + // 閲嶇疆琛ㄥ崟閫昏緫 + data.formInline = { + num: 3, + totalTonnage: 1000, + scoopWeight: 50, + }; + data.constraints = { + minCalorific: 5600, + maxSulfur: 1.2, + maxAsh: 15.0, + maxMoisture: "", + }; + data.coalForms = [ + { + type: "鏈煡鐓�", + coalId: "鐓", + cv: 6200, + price: 450, + sulfur: 0.6, + ash: 12.0, + moisture: 8.0, + }, + { + type: "鏈煡鐓�", + coalId: "鐓", + cv: 5800, + price: 380, + sulfur: 1.0, + ash: 14.0, + moisture: 10.0, + }, + { + type: "鏈煡鐓�", + coalId: "鐓", + cv: 5400, + price: 320, + sulfur: 1.4, + ash: 16.0, + moisture: 12.0, + }, + ]; + data.result = { + show: false, + optimal: null, + alternatives: [], + error: null, + }; + ElMessage.success("琛ㄥ崟宸查噸缃�"); +}; +const addWarehoused = () => { + console.log("娣诲姞鑷冲緟鍏ュ簱鏁版嵁锛�", result.value.optimal); + + if (!result.value) { + ElMessage.error("璇峰厛璁$畻鏈�浼橀厤姣斿悗鍐嶆坊鍔犺嚦寰呭叆搴�"); + return; + } + if (result.value.optimal === null) { + ElMessage.error("璇峰厛璁$畻鏈�浼橀厤姣�"); + return; + } + if (!result.value.optimal.props.createCoal) { + ElMessage.error("璇峰厛閫夋嫨鐢熸垚鐓ょ"); + return; + } + const coals = result.value.optimal.instructions.map((item) => item.coalId); + let allFound = true; + for (const element of coals) { + let found = false; + for (const item of coalInfoList.value) { + if (item.key === element) { + found = true; + break; + } + } + if (!found) { + allFound = false; + break; + } + } + if (!allFound) { + ElMessage.error("閰嶆瘮涓寘鍚湭鐭ョ叅绉嶏紝璇峰厛娣诲姞鑷崇叅绉嶅垪琛�"); + return; + } + let createCoalFound = false; + for (const item of coalInfoList.value) { + if (item.key === result.value.optimal.props.coalId) { + createCoalFound = true; + break; + } + } + if (!createCoalFound) { + ElMessage.warning("鐢熸垚鐓ょ鏄湭鐭ョ叅绉嶏紝鏃犳硶娣诲姞鑷冲緟鍏ュ簱"); + return; + } + // cost淇濈暀涓や綅灏忔暟 + result.value.optimal.props.cost = parseFloat( + result.value.optimal.props.cost.toFixed(2) + ); + result.value.optimal.props.totalTonnage = formInline.value.totalTonnage; + const optimalArray = Object.entries(result.value.optimal.props).map( + ([key, value]) => ({ + [key]: value, + }) + ); + let arr = [[...optimalArray], [...result.value.optimal.instructions]]; + console.log("娣诲姞鑷冲緟鍏ュ簱鏁版嵁锛�", arr); +}; +const submitForm = () => { + // 鏁版嵁楠岃瘉 + let validCoals = coalForms.value.filter( + (coal) => coal.coalId && coal.cv && coal.price + ); + + if (validCoals.length < 2) { + ElMessage.error("鑷冲皯闇�瑕�2涓湁鏁堢殑鐓ょ鏁版嵁锛堝悕绉般�佸彂鐑噺銆佷环鏍间负蹇呭~锛�"); + return; + } + + try { + // 姹傝В鏈�浼橀厤姣� + let ratios = solveBlend(validCoals, constraints.value); + if (!ratios) { + data.result.error = "鏃犲彲琛岃В锛岃妫�鏌ョ害鏉熸潯浠舵垨鐓ょ鏁版嵁"; + data.result.show = true; + return; + } + + // 璁$畻缁撴灉 + let props = calcBlendProps(validCoals, ratios); + let instructions = genInstructions( + validCoals, + ratios, + formInline.value.totalTonnage, + formInline.value.scoopWeight + ); + + data.result = { + show: true, + optimal: { + ratios, + props, + instructions, + }, + alternatives: [], + error: null, + }; + + // 鐢熸垚澶囬�夋柟妗� + generateAlternatives(validCoals); + + ElMessage.success("閰嶇叅浼樺寲璁$畻瀹屾垚"); + } catch (error) { + data.result.error = error.message || "璁$畻杩囩▼涓彂鐢熼敊璇�"; + data.result.show = true; + ElMessage.error(data.result.error); + } +}; + +const generateAlternatives = (coals) => { + const altList = [ + { + desc: "鍙戠儹閲忛檷1%", + mod: { minCalorific: constraints.value.minCalorific * 0.99 }, + }, + { + desc: "鍙戠儹閲忛檷2%", + mod: { minCalorific: constraints.value.minCalorific * 0.98 }, + }, + { + desc: "纭垎鍗�1%", + mod: { maxSulfur: constraints.value.maxSulfur * 1.01 }, + }, + { + desc: "纭垎鍗�2%", + mod: { maxSulfur: constraints.value.maxSulfur * 1.02 }, + }, + { + desc: "鍙戠儹閲忛檷0.5%涓旂~鍒嗗崌0.5%", + mod: { + minCalorific: constraints.value.minCalorific * 0.995, + maxSulfur: constraints.value.maxSulfur * 1.005, + }, + }, + ]; + + data.result.alternatives = []; + + for (let alt of altList) { + try { + let altConstraints = Object.assign({}, constraints.value, alt.mod); + let altRatios = solveBlend(coals, altConstraints); + if (!altRatios) continue; + + let altProps = calcBlendProps(coals, altRatios); + let altInstructions = genInstructions( + coals, + altRatios, + formInline.value.totalTonnage, + formInline.value.scoopWeight + ); + + data.result.alternatives.push({ + desc: alt.desc, + ratios: altRatios, + props: altProps, + instructions: altInstructions, + }); + } catch (error) { + console.warn(`澶囬�夋柟妗� ${alt.desc} 璁$畻澶辫触:`, error); + } + } +}; + +const { formInline, constraints, coalForms, result } = toRefs(data); + +const updateCoalFields = () => { + const num = parseInt(formInline.value.num); + if (isNaN(num) || num <= 0) { + coalForms.value = []; + return; + } + + // 濡傛灉褰撳墠鏁扮粍闀垮害澶т簬鎵�闇�鏁伴噺锛屾埅鏂� + if (coalForms.value.length > num) { + coalForms.value = coalForms.value.slice(0, num); + return; + } + + // 鍚﹀垯锛屽~鍏呮柊鐨勭┖瀵硅薄 + while (coalForms.value.length < num) { + coalForms.value.push({ + type: "鏈煡鐓�", + coalId: `鐓�${String.fromCharCode(65 + coalForms.value.length)}`, + cv: 0, + price: 0, + sulfur: "", + ash: "", + moisture: "", + }); + } +}; + +// 澶勭悊鐓ょ绫诲瀷鍙樺寲 +const handleCoalTypeChange = (index) => { + // 褰撶叅绉嶇被鍨嬫敼鍙樻椂锛屾竻绌虹叅绉嶅悕绉帮紝閬垮厤鏁版嵁娣蜂贡 + coalForms.value[index].coalId = ""; +}; +</script> + +<style scoped lang="scss"> +.view { + display: flex; + gap: 10px; +} +.left-card { + background: #fff; + flex: 1; + min-width: 0; + padding: 16px; + border-radius: 6px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); +} +.coal-forms-container { + overflow-x: auto; + padding-bottom: 8px; +} + +.coal-forms-container::-webkit-scrollbar { + height: 6px; +} + +.coal-forms-container::-webkit-scrollbar-track { + background: #f1f1f1; + border-radius: 3px; +} + +.coal-forms-container::-webkit-scrollbar-thumb { + background: #c1c1c1; + border-radius: 3px; +} + +.coal-forms-container::-webkit-scrollbar-thumb:hover { + background: #a8a8a8; +} + +.count-region { + font-size: 18px; + color: #000000; + line-height: 25px; +} +.scroll { + height: calc(100vh - 14em); + overflow-y: auto; + overflow-x: hidden; + padding-right: 8px; +} + +.scroll::-webkit-scrollbar { + width: 6px; +} + +.scroll::-webkit-scrollbar-track { + background: #f1f1f1; + border-radius: 3px; +} + +.scroll::-webkit-scrollbar-thumb { + background: #c1c1c1; + border-radius: 3px; +} + +.scroll::-webkit-scrollbar-thumb:hover { + background: #a8a8a8; +} +.title { + font-size: 14px; + color: #165dff; + line-height: 20px; + font-weight: 600; + padding-left: 10px; + position: relative; + margin: 6px 0; +} +.title::before { + content: ""; + position: absolute; + left: 0; + top: 3px; /* 璋冩暣鍨傜洿浣嶇疆 */ + width: 4px; /* 灏忔暟鏉″搴� */ + height: 14px; /* 灏忔暟鏉¢珮搴� */ + background-color: #165dff; /* 钃濊壊 */ +} +.el-divider--horizontal { + margin: 12px 0; +} +.footer { + text-align: right; +} + +.right-card { + background: #fff; + width: 600px; + min-width: 400px; + height: auto; + padding: 16px; + border-radius: 6px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); +} + +.result-scroll { + height: calc(100vh - 14em); + overflow-y: auto; + overflow-x: hidden; + padding-right: 8px; +} + +.result-scroll::-webkit-scrollbar { + width: 6px; +} + +.result-scroll::-webkit-scrollbar-track { + background: #f1f1f1; + border-radius: 3px; +} + +.result-scroll::-webkit-scrollbar-thumb { + background: #c1c1c1; + border-radius: 3px; +} + +.result-scroll::-webkit-scrollbar-thumb:hover { + background: #a8a8a8; +} + +.error-box { + margin-bottom: 20px; +} + +.result-section, +.alternatives-section { + margin-bottom: 20px; +} + +.result-title { + font-size: 16px; + color: #165dff; + font-weight: 600; + margin-bottom: 15px; + padding-left: 10px; + position: relative; +} + +.result-title::before { + content: ""; + position: absolute; + left: 0; + top: 3px; + width: 4px; + height: 16px; + background-color: #165dff; +} + +.result-table, +.alt-table { + margin-bottom: 15px; +} + +.table-container { + overflow-x: auto; + margin-bottom: 15px; + border-radius: 4px; +} + +.table-container::-webkit-scrollbar { + height: 6px; +} + +.table-container::-webkit-scrollbar-track { + background: #f1f1f1; + border-radius: 3px; +} + +.table-container::-webkit-scrollbar-thumb { + background: #c1c1c1; + border-radius: 3px; +} + +.table-container::-webkit-scrollbar-thumb:hover { + background: #a8a8a8; +} + +.input-wrapper { + position: relative; + min-height: 32px; + width: 100%; +} + +.input-wrapper .el-input, +.input-wrapper .el-select { + position: absolute; + top: 0; + left: 0; + width: 100% !important; + transition: opacity 0.2s ease-in-out; +} + +/* 纭繚input-wrapper鍐呯殑缁勪欢瀹藉害 */ +.input-wrapper :deep(.el-input), +.input-wrapper :deep(.el-select) { + width: 100% !important; +} + +.input-wrapper :deep(.el-input__wrapper), +.input-wrapper :deep(.el-select .el-input__wrapper) { + width: 100% !important; +} + +.props-section { + margin-top: 15px; +} + +.props-title { + font-size: 14px; + color: #333; + font-weight: 600; + margin-bottom: 10px; +} + +.props-grid { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 8px; +} + +.prop-item { + display: flex; + justify-content: space-between; + padding: 8px 12px; + background: #f5f7fa; + border-radius: 4px; + font-size: 13px; + align-items: center; +} + +.prop-label { + color: #606266; +} + +.prop-value { + font-weight: 600; + color: #303133; +} + +.prop-value.cost { + color: #e6a23c; + font-weight: bold; +} + +.alt-item { + margin-bottom: 20px; + padding: 15px; + border: 1px solid #ebeef5; + border-radius: 6px; + background: #fafafa; +} + +.alt-title { + font-size: 14px; + color: #409eff; + font-weight: 600; + margin-bottom: 10px; +} + +.alt-props { + font-size: 12px; + color: #606266; + margin-top: 10px; + line-height: 1.6; +} + +.alt-props .cost { + color: #e6a23c; + font-weight: 600; +} + +.empty-state { + display: flex; + justify-content: center; + align-items: center; + height: 300px; +} + +/* 闃叉椤甸潰鎶栧姩鐨勬牱寮� */ +:deep(.el-input) { + width: 100% !important; + min-width: 100% !important; +} + +:deep(.el-select) { + width: 100% !important; + min-width: 100% !important; +} + +:deep(.el-form-item) { + margin-bottom: 18px; + width: 100%; +} + +:deep(.el-form-item__label) { + padding-bottom: 6px; + font-size: 14px; + line-height: 1.5; + height: auto; +} + +:deep(.el-form-item__content) { + min-height: 32px; + line-height: 32px; + width: 100%; +} + +:deep(.el-col) { + padding-right: 8px; + box-sizing: border-box; +} + +:deep(.el-col:last-child) { + padding-right: 0; +} + +/* 纭繚杈撳叆妗嗗鍣ㄦ湁鍥哄畾楂樺害鍜屽搴� */ +:deep(.el-input__wrapper) { + min-height: 32px; + box-sizing: border-box; + width: 100% !important; + min-width: 100% !important; +} + +:deep(.el-select .el-input__wrapper) { + min-height: 32px; + box-sizing: border-box; + width: 100% !important; + min-width: 100% !important; +} + +/* 闃叉tooltip寮曡捣鐨勬姈鍔� */ +:deep(.el-tooltip) { + display: block; + width: 100%; +} + +/* 缁熶竴琛岄珮鍜岄棿璺� */ +:deep(.el-row) { + margin-bottom: 0; +} + +/* 闃叉鍐呭鍙樺寲寮曡捣鐨勫竷灞�璺冲姩 */ +:deep(.el-input__inner), +:deep(.el-select__input) { + min-height: 30px; + line-height: 30px; +} + +/* 鍝嶅簲寮忚璁� */ +@media (max-width: 1200px) { + .view { + flex-direction: column; + } + + .left-card { + width: 100%; + } + + .right-card { + width: 100%; + min-width: auto; + } + + .scroll { + height: calc(100vh - 20em); + } + + .result-scroll { + height: calc(100vh - 20em); + } +} + +@media (max-width: 768px) { + .props-grid { + grid-template-columns: 1fr; + } + + .table-container { + font-size: 12px; + } + + :deep(.el-table .cell) { + padding: 4px 8px; + } +} +:deep(.el-input__wrapper) { + min-height: 24px !important; +} +:deep(.el-input__inner) { + min-height: 24px !important; +} +</style> diff --git a/src/views/index.vue b/src/views/index.vue index 37989a6..941b982 100644 --- a/src/views/index.vue +++ b/src/views/index.vue @@ -495,6 +495,9 @@ Xkeys, Yvalues, }; + nextTick(() => { + initAreaChart(); + }); }; onMounted(() => { getList(); -- Gitblit v1.9.3