<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.name"
|
v-show="item.type !== '已有煤'"
|
placeholder="请输入"
|
style="width: 100%"
|
/>
|
<el-select
|
v-model="item.name"
|
v-show="item.type === '已有煤'"
|
placeholder="请选择"
|
style="width: 100%"
|
>
|
<el-option label="已有煤" value="已有煤" />
|
<el-option label="未知煤" value="未知煤" />
|
</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-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-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-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-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="submitForm" 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="name" label="煤种" min-width="80" />
|
<el-table-column prop="ratio" label="配比" min-width="80">
|
<template #default="scope"> {{ scope.row.ratio }}% </template>
|
</el-table-column>
|
<el-table-column prop="tonnage" label="吨数" min-width="80">
|
<template #default="scope">
|
{{ scope.row.tonnage }}吨
|
</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>
|
</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="name" label="煤种" min-width="80" />
|
<el-table-column prop="ratio" label="配比" min-width="80">
|
<template #default="scope">
|
{{ scope.row.ratio }}%
|
</template>
|
</el-table-column>
|
<el-table-column prop="tonnage" label="吨数" min-width="80">
|
<template #default="scope">
|
{{ scope.row.tonnage }}吨
|
</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 } from "vue";
|
import { ElMessage, ElMessageBox } from "element-plus";
|
|
const data = reactive({
|
formInline: {
|
num: 3, // 默认3个煤种
|
totalTonnage: 1000, // 参与配煤总吨数
|
scoopWeight: 50, // 每铲重量
|
},
|
// 约束条件
|
constraints: {
|
minCalorific: 5600, // 混合煤最低发热量
|
maxSulfur: 1.2, // 混合煤最高硫分
|
maxAsh: 15.0, // 混合煤最高灰分
|
maxMoisture: "", // 混合煤最高水分
|
},
|
coalForms: [
|
{
|
type: "未知煤",
|
name: "煤A",
|
cv: 6200, // 发热量
|
price: 450, // 价格
|
sulfur: 0.6, // 硫分
|
ash: 12.0, // 灰分
|
moisture: 8.0, // 水分
|
},
|
{
|
type: "未知煤",
|
name: "煤B",
|
cv: 5800,
|
price: 380,
|
sulfur: 1.0,
|
ash: 14.0,
|
moisture: 10.0,
|
},
|
{
|
type: "未知煤",
|
name: "煤C",
|
cv: 5400,
|
price: 320,
|
sulfur: 1.4,
|
ash: 16.0,
|
moisture: 12.0,
|
},
|
],
|
// 计算结果
|
result: {
|
show: false,
|
optimal: null,
|
alternatives: [],
|
error: null,
|
},
|
});
|
|
// 线性规划求解函数
|
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("如果设置了最大水分约束,则所有煤种都必须提供水分数据。");
|
}
|
}
|
|
// 简单的线性规划求解(最小化成本)
|
// 这里使用简化的算法,实际项目中可以集成更专业的求解器
|
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 tonnage = ratios[i] * total;
|
let scoops = tonnage / scoop;
|
return {
|
name: coal.name,
|
ratio: (ratios[i] * 100).toFixed(2),
|
tonnage: tonnage.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: "未知煤",
|
name: "煤A",
|
cv: 6200,
|
price: 450,
|
sulfur: 0.6,
|
ash: 12.0,
|
moisture: 8.0,
|
},
|
{
|
type: "未知煤",
|
name: "煤B",
|
cv: 5800,
|
price: 380,
|
sulfur: 1.0,
|
ash: 14.0,
|
moisture: 10.0,
|
},
|
{
|
type: "未知煤",
|
name: "煤C",
|
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 submitForm = () => {
|
// 数据验证
|
let validCoals = coalForms.value.filter(
|
(coal) => coal.name && 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: "未知煤",
|
name: `煤${String.fromCharCode(65 + coalForms.value.length)}`,
|
cv: 0,
|
price: 0,
|
sulfur: "",
|
ash: "",
|
moisture: "",
|
}); }
|
};
|
|
// 处理煤种类型变化
|
const handleCoalTypeChange = (index) => {
|
// 当煤种类型改变时,清空煤种名称,避免数据混乱
|
coalForms.value[index].name = '';
|
};
|
</script>
|
|
<style scoped>
|
.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;
|
}
|
|
.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;
|
}
|
}
|
</style>
|