|
@@ -9,7 +9,7 @@
|
|
|
<h3>产品模型库</h3>
|
|
<h3>产品模型库</h3>
|
|
|
<el-form style="display: flex;">
|
|
<el-form style="display: flex;">
|
|
|
<el-form-item label="产品">
|
|
<el-form-item label="产品">
|
|
|
- <el-input v-model="searchQuery" placeholder="搜索产品" style="margin-bottom: 20px;" />
|
|
|
|
|
|
|
+ <el-input v-model="searchQuery" placeholder="搜索产品" style="margin-bottom: 20px;"/>
|
|
|
</el-form-item>
|
|
</el-form-item>
|
|
|
<el-form-item label="行业">
|
|
<el-form-item label="行业">
|
|
|
<el-select v-model="selectedIndustry" placeholder="请选择行业" style="width: 300px;margin-bottom: 20px;">
|
|
<el-select v-model="selectedIndustry" placeholder="请选择行业" style="width: 300px;margin-bottom: 20px;">
|
|
@@ -22,10 +22,42 @@
|
|
|
</el-select>
|
|
</el-select>
|
|
|
</el-form-item>
|
|
</el-form-item>
|
|
|
</el-form>
|
|
</el-form>
|
|
|
|
|
+ <el-button type="primary" @click="addProductModal">添加产品模型</el-button>
|
|
|
|
|
+ <el-dialog v-model="addProductModalVisible">
|
|
|
|
|
+ <template #header>
|
|
|
|
|
+ 添加产品模型
|
|
|
|
|
+ </template>
|
|
|
|
|
+ <template #default>
|
|
|
|
|
+ <el-form ref="addForm">
|
|
|
|
|
+ <el-form-item label="行业" required>
|
|
|
|
|
+ <el-select v-model="addProdForm.industry" placeholder="请选择行业">
|
|
|
|
|
+ <el-option
|
|
|
|
|
+ v-for="industry in industries"
|
|
|
|
|
+ :key="industry.value"
|
|
|
|
|
+ :label="industry.label"
|
|
|
|
|
+ :value="industry.value"
|
|
|
|
|
+ />
|
|
|
|
|
+ </el-select>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="产品名称" required>
|
|
|
|
|
+ <el-input v-model="addProdForm.name" placeholder="请输入产品名称"/>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="描述">
|
|
|
|
|
+ <el-input v-model="addProdForm.description" placeholder="请输入描述"/>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="碳足迹">
|
|
|
|
|
+ <el-input-number min="0" step="0.1" v-model="addProdForm.carbonFootprint" placeholder="请输入碳足迹"/>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </el-form>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ <template #footer>
|
|
|
|
|
+ <el-button type="primary" @click="addProduct">确定</el-button>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-dialog>
|
|
|
<el-table :data="filteredProducts" style="width: 100%" @row-click="showProductDetails">
|
|
<el-table :data="filteredProducts" style="width: 100%" @row-click="showProductDetails">
|
|
|
- <el-table-column prop="name" label="产品名称" />
|
|
|
|
|
- <el-table-column prop="description" label="描述" />
|
|
|
|
|
- <el-table-column prop="carbonFootprint" label="碳足迹" />
|
|
|
|
|
|
|
+ <el-table-column prop="name" label="产品名称"/>
|
|
|
|
|
+ <el-table-column prop="description" label="描述"/>
|
|
|
|
|
+ <el-table-column prop="carbonFootprint" label="碳足迹"/>
|
|
|
</el-table>
|
|
</el-table>
|
|
|
<el-dialog v-model="productDialogVisible" title="产品详细信息">
|
|
<el-dialog v-model="productDialogVisible" title="产品详细信息">
|
|
|
<p><strong>产品名称:</strong> {{ selectedProduct.name }}</p>
|
|
<p><strong>产品名称:</strong> {{ selectedProduct.name }}</p>
|
|
@@ -51,52 +83,116 @@
|
|
|
</template>
|
|
</template>
|
|
|
|
|
|
|
|
<script setup>
|
|
<script setup>
|
|
|
-import { ref, onMounted, watch, computed } from 'vue'
|
|
|
|
|
|
|
+import {ref, onMounted, watch, computed} from 'vue'
|
|
|
import * as echarts from 'echarts'
|
|
import * as echarts from 'echarts'
|
|
|
-import { ElHeader, ElMain, ElSelect, ElOption, ElTable, ElTableColumn, ElTabs, ElTabPane, ElInput, ElDialog } from 'element-plus'
|
|
|
|
|
|
|
+import {
|
|
|
|
|
+ ElHeader,
|
|
|
|
|
+ ElMain,
|
|
|
|
|
+ ElSelect,
|
|
|
|
|
+ ElOption,
|
|
|
|
|
+ ElTable,
|
|
|
|
|
+ ElTableColumn,
|
|
|
|
|
+ ElTabs,
|
|
|
|
|
+ ElTabPane,
|
|
|
|
|
+ ElInput,
|
|
|
|
|
+ ElDialog,
|
|
|
|
|
+ ElMessage
|
|
|
|
|
+} from 'element-plus'
|
|
|
|
|
|
|
|
const activeTab = ref('productLibrary')
|
|
const activeTab = ref('productLibrary')
|
|
|
const selectedIndustry = ref('')
|
|
const selectedIndustry = ref('')
|
|
|
const searchQuery = ref('')
|
|
const searchQuery = ref('')
|
|
|
const productDialogVisible = ref(false)
|
|
const productDialogVisible = ref(false)
|
|
|
const selectedProduct = ref({})
|
|
const selectedProduct = ref({})
|
|
|
-
|
|
|
|
|
|
|
+const addProductModalVisible = ref(false);
|
|
|
|
|
+const addForm = reactive({});
|
|
|
|
|
+const addProdForm = ref({
|
|
|
|
|
+ industry: '',
|
|
|
|
|
+ name: '',
|
|
|
|
|
+ description: '',
|
|
|
|
|
+ carbonFootprint: 0
|
|
|
|
|
+})
|
|
|
|
|
+const addProduct = () => {
|
|
|
|
|
+ if (addProdForm.value.industry && addProdForm.value.name) {
|
|
|
|
|
+ const industry = addProdForm.value.industry;
|
|
|
|
|
+ industryData[industry].push({
|
|
|
|
|
+ name: addProdForm.value.name,
|
|
|
|
|
+ description: addProdForm.value.description,
|
|
|
|
|
+ carbonFootprint: addProdForm.value.carbonFootprint
|
|
|
|
|
+ });
|
|
|
|
|
+ ElMessage.success('产品添加成功');
|
|
|
|
|
+ addProductModalVisible.value = false;
|
|
|
|
|
+ addProdForm.value = {
|
|
|
|
|
+ industry: '',
|
|
|
|
|
+ name: '',
|
|
|
|
|
+ description: '',
|
|
|
|
|
+ carbonFootprint: 0
|
|
|
|
|
+ };
|
|
|
|
|
+ } else {
|
|
|
|
|
+ ElMessage.error('请填写完整信息');
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+const addProductModal = () => {
|
|
|
|
|
+ addProductModalVisible.value = true;
|
|
|
|
|
+}
|
|
|
const industries = [
|
|
const industries = [
|
|
|
- { label: '钢铁', value: 'steel' },
|
|
|
|
|
- { label: '水泥', value: 'cement' },
|
|
|
|
|
- { label: '动力电池', value: 'battery' }
|
|
|
|
|
|
|
+ {label: '钢铁', value: 'steel'},
|
|
|
|
|
+ {label: '有色金属', value: 'nonferrous'},
|
|
|
|
|
+ {label: '石化', value: 'petrochemical'},
|
|
|
|
|
+ {label: '化工', value: 'chemical'},
|
|
|
|
|
+ {label: '建材', value: 'construction'}
|
|
|
]
|
|
]
|
|
|
|
|
|
|
|
const industryData = {
|
|
const industryData = {
|
|
|
steel: [
|
|
steel: [
|
|
|
- { name: '高炉炼铁', description: '高炉炼铁过程', carbonFootprint: 1.5 },
|
|
|
|
|
- { name: '电炉炼钢', description: '电炉炼钢过程', carbonFootprint: 0.8 },
|
|
|
|
|
- { name: '轧钢', description: '轧钢过程', carbonFootprint: 0.5 },
|
|
|
|
|
- { name: '热处理', description: '钢材热处理过程', carbonFootprint: 0.6 },
|
|
|
|
|
- { name: '冷轧', description: '冷轧过程', carbonFootprint: 0.4 },
|
|
|
|
|
- { name: '涂层', description: '钢材表面涂层过程', carbonFootprint: 0.3 },
|
|
|
|
|
- { name: '检验', description: '钢材质量检验过程', carbonFootprint: 0.2 },
|
|
|
|
|
- { name: '包装', description: '钢材包装过程', carbonFootprint: 0.1 }
|
|
|
|
|
|
|
+ {name: '生铁模型', description: '生铁生产过程', carbonFootprint: 1.5},
|
|
|
|
|
+ {name: '粗钢模型', description: '粗钢生产过程', carbonFootprint: 0.8},
|
|
|
|
|
+ {name: '钢板模型', description: '钢板生产过程', carbonFootprint: 0.5},
|
|
|
|
|
+ {name: '螺纹钢模型', description: '螺纹钢生产过程', carbonFootprint: 0.6},
|
|
|
|
|
+ {name: '型钢模型', description: '型钢生产过程', carbonFootprint: 0.4},
|
|
|
|
|
+ {name: '无缝钢管模型', description: '无缝钢管生产过程', carbonFootprint: 0.3},
|
|
|
|
|
+ {name: '特种钢模型', description: '特种钢生产过程', carbonFootprint: 0.2},
|
|
|
|
|
+ {name: '高强度钢模型', description: '高强度钢生产过程', carbonFootprint: 0.1}
|
|
|
],
|
|
],
|
|
|
- cement: [
|
|
|
|
|
- { name: '石灰石开采', description: '石灰石开采过程', carbonFootprint: 0.3 },
|
|
|
|
|
- { name: '熟料烧成', description: '熟料烧成过程', carbonFootprint: 1.2 },
|
|
|
|
|
- { name: '水泥粉磨', description: '水泥粉磨过程', carbonFootprint: 0.4 },
|
|
|
|
|
- { name: '包装', description: '水泥包装过程', carbonFootprint: 0.2 },
|
|
|
|
|
- { name: '运输', description: '水泥运输过程', carbonFootprint: 0.1 },
|
|
|
|
|
- { name: '存储', description: '水泥存储过程', carbonFootprint: 0.1 },
|
|
|
|
|
- { name: '检验', description: '水泥质量检验过程', carbonFootprint: 0.2 },
|
|
|
|
|
- { name: '销售', description: '水泥销售过程', carbonFootprint: 0.1 }
|
|
|
|
|
|
|
+ nonferrous: [
|
|
|
|
|
+ {name: '电解铝模型', description: '电解铝生产过程', carbonFootprint: 0.3},
|
|
|
|
|
+ {name: '铜冶炼模型', description: '铜冶炼生产过程', carbonFootprint: 1.2},
|
|
|
|
|
+ {name: '铝合金模型', description: '铝合金生产过程', carbonFootprint: 0.4},
|
|
|
|
|
+ {name: '铅锌冶炼模型', description: '铅锌冶炼生产过程', carbonFootprint: 0.2},
|
|
|
|
|
+ {name: '镍冶炼模型', description: '镍冶炼生产过程', carbonFootprint: 0.1},
|
|
|
|
|
+ {name: '稀土分离模型', description: '稀土分离生产过程', carbonFootprint: 0.1},
|
|
|
|
|
+ {name: '钨钼产品模型', description: '钨钼产品生产过程', carbonFootprint: 0.2},
|
|
|
|
|
+ {name: '铝型材模型', description: '铝型材生产过程', carbonFootprint: 0.1}
|
|
|
],
|
|
],
|
|
|
- battery: [
|
|
|
|
|
- { name: '锂矿开采', description: '锂矿开采过程', carbonFootprint: 0.5 },
|
|
|
|
|
- { name: '正极材料生产', description: '正极材料生产过程', carbonFootprint: 1.0 },
|
|
|
|
|
- { name: '负极材料生产', description: '负极材料生产过程', carbonFootprint: 0.8 },
|
|
|
|
|
- { name: '电解液制备', description: '电解液制备过程', carbonFootprint: 0.6 },
|
|
|
|
|
- { name: '电池组装', description: '电池组装过程', carbonFootprint: 0.7 },
|
|
|
|
|
- { name: '性能测试', description: '电池性能测试过程', carbonFootprint: 0.3 },
|
|
|
|
|
- { name: '包装', description: '电池包装过程', carbonFootprint: 0.2 },
|
|
|
|
|
- { name: '运输', description: '电池运输过程', carbonFootprint: 0.1 }
|
|
|
|
|
|
|
+ petrochemical: [
|
|
|
|
|
+ {name: '原油加工模型', description: '原油加工生产过程', carbonFootprint: 0.5},
|
|
|
|
|
+ {name: '乙烯模型', description: '乙烯生产过程', carbonFootprint: 1.0},
|
|
|
|
|
+ {name: '丙烯模型', description: '丙烯生产过程', carbonFootprint: 0.8},
|
|
|
|
|
+ {name: '聚乙烯模型', description: '聚乙烯生产过程', carbonFootprint: 0.6},
|
|
|
|
|
+ {name: '聚丙烯模型', description: '聚丙烯生产过程', carbonFootprint: 0.7},
|
|
|
|
|
+ {name: '合成橡胶模型', description: '合成橡胶生产过程', carbonFootprint: 0.3},
|
|
|
|
|
+ {name: '合成纤维模型', description: '合成纤维生产过程', carbonFootprint: 0.2},
|
|
|
|
|
+ {name: '精炼润滑油模型', description: '精炼润滑油生产过程', carbonFootprint: 0.1}
|
|
|
|
|
+ ],
|
|
|
|
|
+ chemical: [
|
|
|
|
|
+ {name: '氯碱化工模型', description: '氯碱化工生产过程', carbonFootprint: 0.3},
|
|
|
|
|
+ {name: '烧碱模型', description: '烧碱生产过程', carbonFootprint: 1.2},
|
|
|
|
|
+ {name: '聚氯乙烯(PVC)模型', description: '聚氯乙烯(PVC)生产过程', carbonFootprint: 0.4},
|
|
|
|
|
+ {name: '合成氨模型', description: '合成氨生产过程', carbonFootprint: 0.2},
|
|
|
|
|
+ {name: '尿素模型', description: '尿素生产过程', carbonFootprint: 0.1},
|
|
|
|
|
+ {name: '硝酸模型', description: '硝酸生产过程', carbonFootprint: 0.1},
|
|
|
|
|
+ {name: '磷肥模型', description: '磷肥生产过程', carbonFootprint: 0.2},
|
|
|
|
|
+ {name: '染料中间体模型', description: '染料中间体生产过程', carbonFootprint: 0.1}
|
|
|
|
|
+ ],
|
|
|
|
|
+ construction: [
|
|
|
|
|
+ {name: '熟料模型', description: '熟料生产过程', carbonFootprint: 0.3},
|
|
|
|
|
+ {name: '水泥模型', description: '水泥生产过程', carbonFootprint: 1.2},
|
|
|
|
|
+ {name: '预拌混凝土模型', description: '预拌混凝土生产过程', carbonFootprint: 0.4},
|
|
|
|
|
+ {name: '平板玻璃模型', description: '平板玻璃生产过程', carbonFootprint: 0.2},
|
|
|
|
|
+ {name: '玻璃纤维模型', description: '玻璃纤维生产过程', carbonFootprint: 0.1},
|
|
|
|
|
+ {name: '陶瓷制品模型', description: '陶瓷制品生产过程', carbonFootprint: 0.1},
|
|
|
|
|
+ {name: '轻质墙板模型', description: '轻质墙板生产过程', carbonFootprint: 0.2},
|
|
|
|
|
+ {name: '防水材料模型', description: '防水材料生产过程', carbonFootprint: 0.1}
|
|
|
]
|
|
]
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -114,81 +210,78 @@ const filteredProducts = computed(() => {
|
|
|
const processChart = ref(null)
|
|
const processChart = ref(null)
|
|
|
|
|
|
|
|
const initProcessChart = () => {
|
|
const initProcessChart = () => {
|
|
|
- const chart = echarts.init(processChart.value)
|
|
|
|
|
|
|
+ const chart = echarts.init(processChart.value);
|
|
|
|
|
+ const selectedIndustryData = industryData[selectedIndustry.value] || [];
|
|
|
|
|
+ const treeData = {
|
|
|
|
|
+ name: industries.find(item => item.value === selectedIndustry.value).label,
|
|
|
|
|
+ children: selectedIndustryData.map(product => ({
|
|
|
|
|
+ name: product.name,
|
|
|
|
|
+ value: product.carbonFootprint,
|
|
|
|
|
+ itemStyle: {
|
|
|
|
|
+ color: getColorForCarbonFootprint(product.carbonFootprint),
|
|
|
|
|
+ borderColor: getColorForCarbonFootprint(product.carbonFootprint)
|
|
|
|
|
+ }
|
|
|
|
|
+ }))
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
const option = {
|
|
const option = {
|
|
|
|
|
+ backgroundColor: '#fff',
|
|
|
title: {
|
|
title: {
|
|
|
- text: '工序积木图',
|
|
|
|
|
|
|
+ subtext: '碳排放路径图',
|
|
|
left: 'center'
|
|
left: 'center'
|
|
|
},
|
|
},
|
|
|
- tooltip: {
|
|
|
|
|
- trigger: 'item',
|
|
|
|
|
- formatter: function (params) {
|
|
|
|
|
- if (params.seriesType === 'sankey') {
|
|
|
|
|
- if (params.dataType === 'node') {
|
|
|
|
|
- const product = filteredProducts.value.find(p => p.name === params.name)
|
|
|
|
|
- return `<strong>${product.name}</strong><br>碳足迹: ${product.carbonFootprint} kg CO2e<br>描述: ${product.description}`
|
|
|
|
|
- } else if (params.dataType === 'edge') {
|
|
|
|
|
- const sourceProduct = filteredProducts.value.find(p => p.name === params.data.source)
|
|
|
|
|
- const targetProduct = filteredProducts.value.find(p => p.name === params.data.target)
|
|
|
|
|
- return `${sourceProduct.name} -> ${targetProduct.name}<br>碳足迹: ${params.data.value} kg CO2e`
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- return ''
|
|
|
|
|
- }
|
|
|
|
|
- },
|
|
|
|
|
- legend: {
|
|
|
|
|
- data: ['低', '中', '高'],
|
|
|
|
|
- top: 'bottom'
|
|
|
|
|
- },
|
|
|
|
|
series: [
|
|
series: [
|
|
|
{
|
|
{
|
|
|
- type: 'sankey',
|
|
|
|
|
- layout: 'none',
|
|
|
|
|
- data: filteredProducts.value.map(product => ({
|
|
|
|
|
- name: product.name,
|
|
|
|
|
- itemStyle: {
|
|
|
|
|
- color: getColorForCarbonFootprint(product.carbonFootprint)
|
|
|
|
|
- }
|
|
|
|
|
- })),
|
|
|
|
|
- links: filteredProducts.value.map((product, index) => ({
|
|
|
|
|
- source: index === 0 ? '原料' : product.name,
|
|
|
|
|
- target: index === filteredProducts.value.length - 1 ? '成品' : filteredProducts.value[index + 1].name,
|
|
|
|
|
- value: product.carbonFootprint,
|
|
|
|
|
- lineStyle: {
|
|
|
|
|
- color: 'source'
|
|
|
|
|
|
|
+ type: 'tree',
|
|
|
|
|
+ id: 0,
|
|
|
|
|
+ name: 'tree',
|
|
|
|
|
+ data: [treeData],
|
|
|
|
|
+ top: '18%',
|
|
|
|
|
+ bottom: '14%',
|
|
|
|
|
+ // 修改布局为横向
|
|
|
|
|
+ layout: 'orthogonal',
|
|
|
|
|
+ symbol: 'emptyCircle',
|
|
|
|
|
+ symbolSize: 7,
|
|
|
|
|
+ initialTreeDepth: 3,
|
|
|
|
|
+ animationDurationUpdate: 750,
|
|
|
|
|
+ label: {
|
|
|
|
|
+ position: 'left',
|
|
|
|
|
+ verticalAlign: 'middle',
|
|
|
|
|
+ align: 'right',
|
|
|
|
|
+ fontSize: 10
|
|
|
|
|
+ },
|
|
|
|
|
+ leaves: {
|
|
|
|
|
+ label: {
|
|
|
|
|
+ position: 'right',
|
|
|
|
|
+ verticalAlign: 'middle',
|
|
|
|
|
+ align: 'left'
|
|
|
}
|
|
}
|
|
|
- })),
|
|
|
|
|
- lineStyle: {
|
|
|
|
|
- curveness: 0.5
|
|
|
|
|
},
|
|
},
|
|
|
- emphasis: {
|
|
|
|
|
- focus: 'adjacency'
|
|
|
|
|
|
|
+ expandAndCollapse: true,
|
|
|
|
|
+ roam: true,
|
|
|
|
|
+ itemStyle: {
|
|
|
|
|
+ color: '#1f77b4',
|
|
|
|
|
+ borderColor: '#1f77b4'
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
- ]
|
|
|
|
|
- }
|
|
|
|
|
- chart.setOption(option)
|
|
|
|
|
-
|
|
|
|
|
- // 直接设置高亮
|
|
|
|
|
- if (selectedIndustry.value) {
|
|
|
|
|
- chart.dispatchAction({
|
|
|
|
|
- type: 'highlight',
|
|
|
|
|
- seriesIndex: 0,
|
|
|
|
|
- dataIndex: 0
|
|
|
|
|
- })
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ ],
|
|
|
|
|
+ tooltip: {
|
|
|
|
|
+ trigger: 'item'
|
|
|
|
|
+ }
|
|
|
|
|
+ };
|
|
|
|
|
+ chart.setOption(option);
|
|
|
|
|
|
|
|
// 添加点击事件
|
|
// 添加点击事件
|
|
|
chart.on('click', (params) => {
|
|
chart.on('click', (params) => {
|
|
|
if (params.dataType === 'node') {
|
|
if (params.dataType === 'node') {
|
|
|
- const product = filteredProducts.value.find(p => p.name === params.name)
|
|
|
|
|
|
|
+ const product = selectedIndustryData.find(p => p.name === params.name);
|
|
|
if (product) {
|
|
if (product) {
|
|
|
- selectedProduct.value = product
|
|
|
|
|
- productDialogVisible.value = true
|
|
|
|
|
|
|
+ selectedProduct.value = product;
|
|
|
|
|
+ productDialogVisible.value = true;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
- })
|
|
|
|
|
-}
|
|
|
|
|
|
|
+ });
|
|
|
|
|
+};
|
|
|
|
|
|
|
|
const getColorForCarbonFootprint = (carbonFootprint) => {
|
|
const getColorForCarbonFootprint = (carbonFootprint) => {
|
|
|
if (carbonFootprint < 0.5) return '#67C23A' // 绿色
|
|
if (carbonFootprint < 0.5) return '#67C23A' // 绿色
|