|
|
@@ -1,292 +1,462 @@
|
|
|
<template>
|
|
|
<div class="dcs_tanks">
|
|
|
- <HeaderComponent :title="title" backTo="/controlPage/flowSelect" />
|
|
|
- <PageNav :items="navItems_Na2SO4" :currentCode="flowCode" />
|
|
|
- <!-- 加载状态提示 -->
|
|
|
- <div v-if="isLoading" class="loading">
|
|
|
- 正在加载设备数据...
|
|
|
- </div>
|
|
|
- <!-- 错误状态提示 -->
|
|
|
- <div v-if="hasError" class="error">
|
|
|
- <p>加载数据失败: {{ errorMessage }}</p>
|
|
|
- <button @click="loadInitialData">重试</button>
|
|
|
+ <!-- 固定在顶部的头部区域 -->
|
|
|
+ <div class="fixed-header" ref="fixedHeader">
|
|
|
+ <HeaderComponent title="硫酸钠反应流程" backTo="/controlPage/flowSelect" />
|
|
|
+ <PageNav :items="navItems_Na2SO4" :currentCode="flowCode" />
|
|
|
</div>
|
|
|
|
|
|
- <div v-else class="tanks_container">
|
|
|
- <!-- 每个罐体占一行 -->
|
|
|
- <div v-for="(equipments, tankName) in deviceConfigGroup" :key="tankName" class="tank_row">
|
|
|
- <h2 class="tank_title">{{ tankName }}</h2>
|
|
|
- <div class="equipment_list">
|
|
|
- <WorkModeControl v-if="tankName === '兑卤器低位槽-V3001'" />
|
|
|
- <component v-for="equipment in equipments" :key="equipment.code"
|
|
|
- :is="getComponentByType(equipment.equipmentType)"
|
|
|
- :title="equipment.title + '-' + equipment.equipmentName" :code="equipment.code"
|
|
|
- :dataArr="getValueByCode(tankName, equipment.code)" />
|
|
|
- <!-- 添加其他设备组件 -->
|
|
|
- <PumpControlComponent title="原矿进料" :dataArr="[5, 100, 100]" v-if="tankName === '1#反应釜-C3001'" />
|
|
|
+ <!-- 主内容区(包含导航),添加顶部间距避免被固定头部遮挡 -->
|
|
|
+ <div class="main-container">
|
|
|
+ <div class="main-content">
|
|
|
+ <!-- 设备列表区域 -->
|
|
|
+ <div class="content-area">
|
|
|
+ <!-- 加载状态提示 -->
|
|
|
+ <div v-if="isLoading" class="loading">
|
|
|
+ 正在加载设备数据...
|
|
|
+ </div>
|
|
|
+ <!-- 错误状态提示 -->
|
|
|
+ <div v-if="hasError" class="error">
|
|
|
+ <p>加载数据失败: {{ errorMessage }}</p>
|
|
|
+ <button @click="loadInitialData">重试</button>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div v-else class="tanks_container">
|
|
|
+ <!-- 每个罐体占一块区域 -->
|
|
|
+ <div v-for="(equipments, tankName) in deviceConfigGroup" :key="tankName" class="tank_section"
|
|
|
+ :id="`tank-${tankName}`">
|
|
|
+ <h2 class="tank_title">{{ tankName }}</h2>
|
|
|
+ <div class="equipment_grid">
|
|
|
+ <!-- 添加其他设备组件 -->
|
|
|
+ <PumpControlComponent title="原矿进料" :dataArr="[5, 100, 100]"
|
|
|
+ v-if="tankName === '1#反应釜-C3001'" id="equipment-special-pump"
|
|
|
+ class="equipment_item" />
|
|
|
+ <WorkModeControl v-if="tankName === '兑卤器低位槽-V3001'" class="equipment_item" />
|
|
|
+ <component v-for="equipment in equipments" :key="equipment.code"
|
|
|
+ :is="getComponentByType(equipment.equipmentType)"
|
|
|
+ :title="equipment.title + '-' + equipment.equipmentName" :code="equipment.code"
|
|
|
+ :dataArr="getValueByCode(tankName, equipment.code)"
|
|
|
+ :id="`equipment-${equipment.code}`" class="equipment_item" />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 无数据提示 -->
|
|
|
+ <div v-if="isEmptyData" class="no_data">
|
|
|
+ 未获取到设备数据
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
- </div>
|
|
|
|
|
|
- <!-- 无数据提示 -->
|
|
|
- <div v-if="isEmptyData" class="no_data">
|
|
|
- 未获取到设备数据
|
|
|
+ <!-- 右侧导航栏 -->
|
|
|
+ <TankNavigation :tanks="allTanks" :current-index="currentNavIndex" :header-height="headerHeight"
|
|
|
+ @tankClick="scrollToTank" @scrollPositionChange="handleScrollPositionChange" />
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<script setup>
|
|
|
-import HeaderComponent from '@/components/DCS/HeaderComponent.vue'
|
|
|
-import PageNav from '@/components/GeneralComponents/control/PageNavComponent.vue'
|
|
|
-import SensorControl from '@/components/GeneralComponents/control/SensorControl2Component.vue'
|
|
|
-import PumpControlComponent from '@/components/GeneralComponents/control/PumpControl2Component.vue'
|
|
|
-import ValveControlComponent from '@/components/GeneralComponents/control/ValveControl2Component.vue'
|
|
|
-import WorkModeControl from '@/components/GeneralComponents/control/WorkModeControl2Component.vue'
|
|
|
-import { ref, onMounted, onBeforeUnmount, computed, watch } from 'vue'
|
|
|
-import { useRoute, useRouter } from 'vue-router'
|
|
|
-import { getPageEquipmentGroupByTankByFlowCode } from '@/api/hnyz/equipment'
|
|
|
-import { useEquipmentLayout } from '@/hooks/useEquipmentLayout'
|
|
|
-import { navItems_Na2SO4 } from '@/config'
|
|
|
-import { stompClient } from '@/utils/ws/stompClient'
|
|
|
-import { updateZTPageConfig } from '@/api/dcs/configurePage'
|
|
|
-
|
|
|
-const route = useRoute()
|
|
|
+import { ref, onMounted, onBeforeUnmount, watch, watchEffect } from 'vue';
|
|
|
+import { useRoute } from 'vue-router';
|
|
|
+// 导入组件
|
|
|
+import HeaderComponent from '@/components/DCS/HeaderComponent.vue';
|
|
|
+import PageNav from '@/components/GeneralComponents/control/PageNavComponent.vue';
|
|
|
+import SensorControl from '@/components/GeneralComponents/control/SensorControl2Component.vue';
|
|
|
+import PumpControlComponent from '@/components/GeneralComponents/control/PumpControl2Component.vue';
|
|
|
+import ValveControlComponent from '@/components/GeneralComponents/control/ValveControl2Component.vue';
|
|
|
+import WorkModeControl from '@/components/GeneralComponents/control/WorkModeControl2Component.vue';
|
|
|
+import TankNavigation from '@/components/GeneralComponents/control/TankNavigationComponent.vue';
|
|
|
+
|
|
|
+// 导入其他依赖
|
|
|
+import { getPageEquipmentGroupByTankByFlowCode } from '@/api/hnyz/equipment';
|
|
|
+import { useEquipmentLayout } from '@/hooks/useEquipmentLayout';
|
|
|
+import { navItems_Na2SO4 } from '@/config';
|
|
|
+import { stompClient } from '@/utils/ws/stompClient';
|
|
|
+import { updateZTPageConfig } from '@/api/dcs/configurePage';
|
|
|
+
|
|
|
+const route = useRoute();
|
|
|
|
|
|
// 状态变量
|
|
|
-const isLoading = ref(true)
|
|
|
-const hasError = ref(false)
|
|
|
-const errorMessage = ref('')
|
|
|
-const isEmptyData = ref(false)
|
|
|
+const isLoading = ref(true);
|
|
|
+const hasError = ref(false);
|
|
|
+const errorMessage = ref('');
|
|
|
+const isEmptyData = ref(false);
|
|
|
+const fixedHeader = ref(null);
|
|
|
+const headerHeight = ref(0);
|
|
|
+
|
|
|
+const flowCode = ref(route.name);
|
|
|
+const title = navItems_Na2SO4.find(i => i.code === flowCode.value)?.label || '';
|
|
|
|
|
|
-const flowCode = route.name
|
|
|
+// 导航相关变量
|
|
|
+const allTanks = ref([]); // 所有罐体列表
|
|
|
+const currentNavIndex = ref(-1); // 当前激活的导航项索引
|
|
|
|
|
|
-const title = navItems_Na2SO4.find(i => i.code === flowCode)?.label || ''
|
|
|
+// 设备配置和数据
|
|
|
+const deviceConfigGroup = ref({});// 设备配置分组
|
|
|
+const deviceDataGroup = ref({});// 设备数据分组
|
|
|
+const pageParams = ref({});
|
|
|
+
|
|
|
+const { generatePageParams } = useEquipmentLayout();
|
|
|
|
|
|
// 监控flowCode变化
|
|
|
watch(() => route.name, (newVal) => {
|
|
|
- console.log('路由变化:', newVal)
|
|
|
- flowCode = newVal
|
|
|
- loadInitialData()
|
|
|
-})
|
|
|
-const deviceConfigGroup = ref({})// 设备配置分组 - 用于渲染设备结构
|
|
|
-const deviceDataGroup = ref({})// 设备数据分组 - 用于提供设备数据
|
|
|
-const pageParams = ref({})
|
|
|
-
|
|
|
-const { generatePageParams } = useEquipmentLayout()
|
|
|
+ console.log('路由变化:', newVal);
|
|
|
+ flowCode.value = newVal;
|
|
|
+ loadInitialData();
|
|
|
+});
|
|
|
+
|
|
|
+// 生成罐体导航列表(直接取deviceConfigGroup的tankName)
|
|
|
+watchEffect(() => {
|
|
|
+ // 收集所有罐体名称
|
|
|
+ const tanks = [];
|
|
|
+
|
|
|
+ // 直接使用deviceConfigGroup的键作为罐体名称
|
|
|
+ Object.keys(deviceConfigGroup.value).forEach(tankName => {
|
|
|
+ tanks.push({
|
|
|
+ uniqueId: `tank-${tankName}`,
|
|
|
+ tankName,
|
|
|
+ shortName: shortenTankName(tankName),
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
+ allTanks.value = tanks;
|
|
|
+ currentNavIndex.value = -1;
|
|
|
+});
|
|
|
+
|
|
|
+// 计算固定头部高度
|
|
|
+const calculateHeaderHeight = () => {
|
|
|
+ if (fixedHeader.value) {
|
|
|
+ headerHeight.value = fixedHeader.value.offsetHeight;
|
|
|
+ }
|
|
|
+};
|
|
|
|
|
|
async function loadInitialData() {
|
|
|
try {
|
|
|
- isLoading.value = true
|
|
|
- hasError.value = false
|
|
|
- errorMessage.value = ''
|
|
|
+ isLoading.value = true;
|
|
|
+ hasError.value = false;
|
|
|
+ errorMessage.value = '';
|
|
|
|
|
|
- const res = await getPageEquipmentGroupByTankByFlowCode(flowCode)
|
|
|
+ const res = await getPageEquipmentGroupByTankByFlowCode(flowCode.value);
|
|
|
|
|
|
deviceConfigGroup.value = Object.fromEntries(
|
|
|
Object.entries(res.data || {}).filter(([_, equipments]) => equipments?.length > 0)
|
|
|
- )
|
|
|
- // console.log('设备配置分组:', deviceConfigGroup.value)
|
|
|
- isEmptyData.value = Object.keys(deviceConfigGroup.value).length === 0
|
|
|
+ );
|
|
|
+ isEmptyData.value = Object.keys(deviceConfigGroup.value).length === 0;
|
|
|
|
|
|
- pageParams.value = generatePageParams(deviceConfigGroup.value)
|
|
|
- await updatePageConfig()
|
|
|
+ pageParams.value = generatePageParams(deviceConfigGroup.value);
|
|
|
+ // console.log('页面参数:', pageParams.value);
|
|
|
+ await updatePageConfig();
|
|
|
} catch (err) {
|
|
|
- hasError.value = true
|
|
|
- errorMessage.value = err.message || '加载设备分组失败'
|
|
|
- console.error('加载页面设备分组失败', err)
|
|
|
+ hasError.value = true;
|
|
|
+ errorMessage.value = err.message || '加载设备分组失败';
|
|
|
+ console.error('加载页面设备分组失败', err);
|
|
|
} finally {
|
|
|
- isLoading.value = false
|
|
|
+ isLoading.value = false;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
async function updatePageConfig() {
|
|
|
try {
|
|
|
- // console.log('更新页面配置:', flowCode, pageParams.value)
|
|
|
- await updateZTPageConfig(flowCode, pageParams.value)
|
|
|
+ await updateZTPageConfig(flowCode.value, pageParams.value);
|
|
|
|
|
|
// 先取消可能存在的订阅,避免重复订阅
|
|
|
- stompClient.unsubscribeFromPage(flowCode)
|
|
|
+ stompClient.unsubscribeFromPage(flowCode.value);
|
|
|
|
|
|
- stompClient.subscribeToPage(flowCode, data => {
|
|
|
- // console.log('收到页面实时数据推送:', data)
|
|
|
+ stompClient.subscribeToPage(flowCode.value, data => {
|
|
|
if (data && Object.keys(data).length > 0) {
|
|
|
try {
|
|
|
// 按key逐个更新,避免整体替换导致页面闪烁
|
|
|
for (const key in data) {
|
|
|
- deviceDataGroup.value[key] = data[key] || []
|
|
|
+ deviceDataGroup.value[key] = data[key] || [];
|
|
|
}
|
|
|
} catch (updateErr) {
|
|
|
- console.error('更新设备数据失败:', updateErr)
|
|
|
+ console.error('更新设备数据失败:', updateErr);
|
|
|
}
|
|
|
}
|
|
|
- })
|
|
|
+ });
|
|
|
} catch (err) {
|
|
|
- console.error('页面配置失败:', err)
|
|
|
+ console.error('页面配置失败:', err);
|
|
|
// 配置失败也可以继续显示已有数据
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
/**
|
|
|
* 根据设备类型编码判断设备类型并返回对应组件
|
|
|
- * @param {number|string} equipmentType - 设备类型编码(1、5为阀门,2为泵,4为传感器)
|
|
|
- * @returns {Component} 对应的设备组件
|
|
|
*/
|
|
|
function getComponentByType(equipmentType) {
|
|
|
- // 处理空值或无效类型情况
|
|
|
if (equipmentType === undefined || equipmentType === null) {
|
|
|
- console.error('设备类型编码不能为空')
|
|
|
- return 'div'
|
|
|
+ console.error('设备类型编码不能为空');
|
|
|
+ return 'div';
|
|
|
}
|
|
|
|
|
|
- // 将输入转换为数字类型(兼容字符串形式的数字,如"1")
|
|
|
- const type = Number(equipmentType)
|
|
|
+ const type = Number(equipmentType);
|
|
|
|
|
|
- // 校验是否为有效数字
|
|
|
if (isNaN(type)) {
|
|
|
- console.error('无效的设备类型编码:', equipmentType)
|
|
|
- return 'div'
|
|
|
+ console.error('无效的设备类型编码:', equipmentType);
|
|
|
+ return 'div';
|
|
|
}
|
|
|
|
|
|
- // 根据类型编码返回对应组件
|
|
|
if (type === 1 || type === 5) {
|
|
|
- // console.log(`设备类型编码${type}判定为阀门`)
|
|
|
- return ValveControlComponent
|
|
|
+ return ValveControlComponent;
|
|
|
} else if (type === 2) {
|
|
|
- // console.log(`设备类型编码${type}判定为泵`)
|
|
|
- return PumpControlComponent
|
|
|
+ return PumpControlComponent;
|
|
|
} else if (type === 4) {
|
|
|
- // console.log(`设备类型编码${type}判定为传感器`)
|
|
|
- return SensorControl
|
|
|
+ return SensorControl;
|
|
|
} else {
|
|
|
- // 未知类型
|
|
|
- console.warn(`设备类型编码${type}无法识别,使用默认div`)
|
|
|
- return 'div'
|
|
|
+ console.warn(`设备类型编码${type}无法识别,使用默认div`);
|
|
|
+ return 'div';
|
|
|
}
|
|
|
}
|
|
|
|
|
|
function getValueByCode(tankName, code) {
|
|
|
- const dataArr = deviceDataGroup.value[tankName] || []
|
|
|
- const target = dataArr.find(item => item.code === code)
|
|
|
- return target ? target.value : []
|
|
|
+ const dataArr = deviceDataGroup.value[tankName] || [];
|
|
|
+ const target = dataArr.find(item => item.code === code);
|
|
|
+ return target ? target.value : [];
|
|
|
+}
|
|
|
+
|
|
|
+// 导航功能:滚动到指定罐体
|
|
|
+function scrollToTank(tank, index) {
|
|
|
+ currentNavIndex.value = index;
|
|
|
+ const elementId = `tank-${tank.tankName}`;
|
|
|
+
|
|
|
+ const element = document.getElementById(elementId);
|
|
|
+ if (element) {
|
|
|
+ // 计算滚动位置时考虑固定头部的高度
|
|
|
+ window.scrollTo({
|
|
|
+ top: element.offsetTop - headerHeight.value - 20, // 顶部留一些间距
|
|
|
+ behavior: 'smooth'
|
|
|
+ });
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 处理滚动位置变化事件
|
|
|
+function handleScrollPositionChange(index) {
|
|
|
+ currentNavIndex.value = index;
|
|
|
+}
|
|
|
+
|
|
|
+// 缩短罐体名称用于导航显示
|
|
|
+function shortenTankName(fullName) {
|
|
|
+ if (fullName.length > 18) {
|
|
|
+ return fullName.substring(0, 16) + '...';
|
|
|
+ }
|
|
|
+ return fullName;
|
|
|
}
|
|
|
|
|
|
onMounted(() => {
|
|
|
- console.log('组件挂载')
|
|
|
- loadInitialData()
|
|
|
-})
|
|
|
+ console.log('组件挂载');
|
|
|
+ // 计算头部高度
|
|
|
+ calculateHeaderHeight();
|
|
|
+ // 监听窗口大小变化,重新计算头部高度
|
|
|
+ window.addEventListener('resize', calculateHeaderHeight);
|
|
|
+ loadInitialData();
|
|
|
+});
|
|
|
|
|
|
onBeforeUnmount(() => {
|
|
|
- console.log('组件卸载,取消订阅')
|
|
|
- stompClient.unsubscribeFromPage(flowCode)
|
|
|
-})
|
|
|
+ console.log('组件卸载,取消订阅');
|
|
|
+ stompClient.unsubscribeFromPage(flowCode.value);
|
|
|
+ window.removeEventListener('resize', calculateHeaderHeight);
|
|
|
+});
|
|
|
</script>
|
|
|
|
|
|
<style scoped lang="scss">
|
|
|
.dcs_tanks {
|
|
|
width: 100%;
|
|
|
min-height: 100vh;
|
|
|
- padding: 24px 32px;
|
|
|
background-color: #141414;
|
|
|
+ box-sizing: border-box;
|
|
|
+}
|
|
|
+
|
|
|
+// 固定在顶部的头部区域
|
|
|
+.fixed-header {
|
|
|
+ position: fixed;
|
|
|
+ top: 0;
|
|
|
+ left: 0;
|
|
|
+ right: 0;
|
|
|
+ z-index: 1000;
|
|
|
+ background-color: #141414;
|
|
|
+ box-shadow: 0 2px 10px rgba(0, 0, 0, 0.3);
|
|
|
+}
|
|
|
+
|
|
|
+// 主容器 - 添加顶部内边距,避免被固定头部遮挡
|
|
|
+.main-container {
|
|
|
+ padding: 140px 32px 24px;
|
|
|
+ box-sizing: border-box;
|
|
|
+}
|
|
|
+
|
|
|
+// 主内容区布局
|
|
|
+.main-content {
|
|
|
+ display: flex;
|
|
|
+ gap: 20px;
|
|
|
+ width: 100%;
|
|
|
+ max-width: 1600px;
|
|
|
+ margin: 0 auto;
|
|
|
+}
|
|
|
+
|
|
|
+.content-area {
|
|
|
+ flex: 1;
|
|
|
+}
|
|
|
+
|
|
|
+// 罐体容器
|
|
|
+.tanks_container {
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
- align-items: center;
|
|
|
+ gap: 30px;
|
|
|
+ width: 100%;
|
|
|
+}
|
|
|
|
|
|
- // 罐体容器改为纵向排列
|
|
|
- .tanks_container {
|
|
|
- display: flex;
|
|
|
- flex-direction: column;
|
|
|
- gap: 30px;
|
|
|
- width: 100%;
|
|
|
- max-width: 1400px;
|
|
|
- margin-top: 20px;
|
|
|
-
|
|
|
- // 每个罐体占一行
|
|
|
- .tank_row {
|
|
|
- background: #1a1a1a;
|
|
|
- border-radius: 12px;
|
|
|
- padding: 16px 20px;
|
|
|
- width: 100%;
|
|
|
- display: flex;
|
|
|
- flex-direction: column;
|
|
|
- gap: 16px;
|
|
|
-
|
|
|
- .tank_title {
|
|
|
- font-size: 20px;
|
|
|
- color: #fff;
|
|
|
- padding-bottom: 8px;
|
|
|
- border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
|
|
- text-align: center;
|
|
|
- }
|
|
|
+// 每个罐体区域
|
|
|
+.tank_section {
|
|
|
+ background: #1a1a1a;
|
|
|
+ border-radius: 12px;
|
|
|
+ padding: 20px 24px;
|
|
|
+ width: 100%;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 20px;
|
|
|
+ box-sizing: border-box;
|
|
|
+ scroll-margin-top: 160px;
|
|
|
+ /* 用于滚动定位时预留空间 */
|
|
|
+}
|
|
|
|
|
|
- // 设备列表改为横向排列,可滚动
|
|
|
- .equipment_list {
|
|
|
- display: flex;
|
|
|
- gap: 16px;
|
|
|
- overflow-x: auto;
|
|
|
- padding: 8px 0;
|
|
|
- min-height: 120px;
|
|
|
- align-items: center;
|
|
|
- /* 确保有足够高度显示设备 */
|
|
|
-
|
|
|
- /* 滚动条美化 */
|
|
|
- &::-webkit-scrollbar {
|
|
|
- height: 6px;
|
|
|
- }
|
|
|
+.tank_title {
|
|
|
+ font-size: 20px;
|
|
|
+ color: #fff;
|
|
|
+ padding-bottom: 12px;
|
|
|
+ border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
|
|
+ text-align: center;
|
|
|
+ margin: 0;
|
|
|
+}
|
|
|
|
|
|
- &::-webkit-scrollbar-track {
|
|
|
- background: rgba(255, 255, 255, 0.05);
|
|
|
- border-radius: 3px;
|
|
|
- }
|
|
|
+// 设备网格布局
|
|
|
+.equipment_grid {
|
|
|
+ display: grid;
|
|
|
+ grid-template-columns: repeat(4, 1fr);
|
|
|
+ /* 默认4列布局 */
|
|
|
+ gap: 24px;
|
|
|
+ /* 增大间距避免拥挤 */
|
|
|
+ padding: 12px 0;
|
|
|
+}
|
|
|
|
|
|
- &::-webkit-scrollbar-thumb {
|
|
|
- background: rgba(255, 255, 255, 0.2);
|
|
|
- border-radius: 3px;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
+// 统一设备项样式
|
|
|
+.equipment_item {
|
|
|
+ min-height: 200px;
|
|
|
+ /* 固定最小高度,确保排列整齐 */
|
|
|
+ padding: 16px;
|
|
|
+ background-color: rgba(30, 30, 30, 0.9);
|
|
|
+ border-radius: 8px;
|
|
|
+ border: 1px solid rgba(114, 224, 255, 0.15);
|
|
|
+ box-sizing: border-box;
|
|
|
+}
|
|
|
|
|
|
- /* 无数据提示样式 */
|
|
|
- .no_data {
|
|
|
- color: white;
|
|
|
- margin-top: 20px;
|
|
|
- padding: 20px;
|
|
|
- border-radius: 8px;
|
|
|
- background-color: rgba(255, 255, 255, 0.1);
|
|
|
- width: 100%;
|
|
|
- max-width: 1400px;
|
|
|
- text-align: center;
|
|
|
- }
|
|
|
+/* 确保子组件正确填充容器 */
|
|
|
+:deep(.equipment_item) {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+
|
|
|
+ .title {
|
|
|
+ margin-bottom: 16px;
|
|
|
+ padding-bottom: 8px;
|
|
|
+ border-bottom: 1px solid rgba(255, 255, 255, 0.05);
|
|
|
+ white-space: nowrap;
|
|
|
+ overflow: hidden;
|
|
|
+ text-overflow: ellipsis;
|
|
|
}
|
|
|
+}
|
|
|
+
|
|
|
+/* 无数据提示样式 */
|
|
|
+.no_data {
|
|
|
+ color: white;
|
|
|
+ margin-top: 20px;
|
|
|
+ padding: 20px;
|
|
|
+ border-radius: 8px;
|
|
|
+ background-color: rgba(255, 255, 255, 0.1);
|
|
|
+ width: 100%;
|
|
|
+ text-align: center;
|
|
|
+}
|
|
|
+
|
|
|
+/* 状态提示样式 */
|
|
|
+.loading,
|
|
|
+.error {
|
|
|
+ color: white;
|
|
|
+ margin-top: 20px;
|
|
|
+ padding: 20px;
|
|
|
+ border-radius: 8px;
|
|
|
+ background-color: rgba(255, 255, 255, 0.1);
|
|
|
+ width: 100%;
|
|
|
+ text-align: center;
|
|
|
+}
|
|
|
+
|
|
|
+.error {
|
|
|
+ color: #ff4444;
|
|
|
|
|
|
- /* 状态提示样式 */
|
|
|
- .loading,
|
|
|
- .error {
|
|
|
+ button {
|
|
|
+ margin-top: 10px;
|
|
|
+ padding: 8px 16px;
|
|
|
+ background-color: #00C851;
|
|
|
color: white;
|
|
|
- margin-top: 20px;
|
|
|
- padding: 20px;
|
|
|
- border-radius: 8px;
|
|
|
- background-color: rgba(255, 255, 255, 0.1);
|
|
|
- width: 100%;
|
|
|
- max-width: 1400px;
|
|
|
- text-align: center;
|
|
|
- }
|
|
|
+ border: none;
|
|
|
+ border-radius: 4px;
|
|
|
+ cursor: pointer;
|
|
|
+ transition: background-color 0.2s;
|
|
|
|
|
|
- .error {
|
|
|
- color: #ff4444;
|
|
|
-
|
|
|
- button {
|
|
|
- margin-top: 10px;
|
|
|
- padding: 8px 16px;
|
|
|
- background-color: #00C851;
|
|
|
- color: white;
|
|
|
- border: none;
|
|
|
- border-radius: 4px;
|
|
|
- cursor: pointer;
|
|
|
- transition: background-color 0.2s;
|
|
|
-
|
|
|
- &:hover {
|
|
|
- background-color: #007E33;
|
|
|
- }
|
|
|
+ &:hover {
|
|
|
+ background-color: #007E33;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+/* 响应式布局 */
|
|
|
+@media (max-width: 1400px) {
|
|
|
+ .equipment_grid {
|
|
|
+ grid-template-columns: repeat(3, 1fr);
|
|
|
+ gap: 20px;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+@media (max-width: 1200px) {
|
|
|
+ .main-container {
|
|
|
+ padding: 140px 20px 16px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .main-content {
|
|
|
+ max-width: 100%;
|
|
|
+ }
|
|
|
+
|
|
|
+ .equipment_grid {
|
|
|
+ grid-template-columns: repeat(3, 1fr);
|
|
|
+ gap: 18px;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+@media (max-width: 992px) {
|
|
|
+ .main-content {
|
|
|
+ flex-direction: column;
|
|
|
+ }
|
|
|
+
|
|
|
+ .equipment_grid {
|
|
|
+ grid-template-columns: repeat(2, 1fr);
|
|
|
+ gap: 16px;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+@media (max-width: 768px) {
|
|
|
+ .main-container {
|
|
|
+ padding: 120px 16px 16px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .equipment_grid {
|
|
|
+ grid-template-columns: 1fr;
|
|
|
+ gap: 14px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .tank_section {
|
|
|
+ padding: 16px 18px;
|
|
|
+ gap: 16px;
|
|
|
+ scroll-margin-top: 140px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .equipment_item {
|
|
|
+ min-height: 180px;
|
|
|
+ }
|
|
|
+}
|
|
|
</style>
|