Bladeren bron

feat(controlPage):控制页面和参数配置页面改为数据库驱动

HMY 8 maanden geleden
bovenliggende
commit
0c55381771

+ 9 - 0
ui/src/api/hnyz/flow.js

@@ -64,4 +64,13 @@ export function changeStatus(flowId, status) {
       status: status
     }
   })
+}
+
+//获取流程导航
+export function getFlowNav(attribution, pageType) {
+  return request({
+    url: '/hnyz/flow/getFlowNav',
+    method: 'get',
+    params: { attribution, pageType }
+  })
 }

+ 0 - 18
ui/src/config.js

@@ -10,24 +10,6 @@ export const wsConfig = {
   stompEndpoint: `http://${BASE_URL}/ws/configurationPage`, 
 };
 //TODO :导航栏配置改为数据库配置
-// 硫酸钠控制页面导航栏配置
-export const navItems_Na2SO4=[
-  { label: '反应分离', code: 'Reaction_Control', path: '/controlPage/flowSelect/reaction' },
-  // { label: '反应分离2', code: 'SolidLiquidSeparation_Control', path: '/controlPage/flowSelect/solidLiquidSeparation' },
-  { label: '24平真空过滤', code: 'FlatBeltFiltration_Control', path: '/controlPage/flowSelect/flatBeltFiltration' },
-  { label: 'G1分解', code: 'G1Decomposition_Control', path: '/controlPage/flowSelect/G1Decomposition' },
-  // { label: 'G1分解2', code: 'G1AccessorySeparator_Control', path: '/controlPage/flowSelect/G1AccessorySeparator' },
-  // { label: '溶晶', code: 'DissolvedCrystal_Control', path: '/controlPage/flowSelect/DissolvedCrystal' },
-  { label: 'G3分解', code: 'G3Decomposition_Control', path: '/controlPage/flowSelect/G3Decomposition' },
-]
-
-export const paramItems_Na2SO4=[
-  { label: '反应分离', code: 'Reaction_Param', path: '/controlPage/paramConfiguration/reaction' },
-  { label: '24平真空过滤', code: 'FlatBeltFiltration_Param', path: '/controlPage/paramConfiguration/flatBeltFiltration' },
-  { label: 'G1分解', code: 'G1Decomposition_Param', path: '/controlPage/paramConfiguration/G1Decomposition' },
-  { label: 'G3分解', code: 'G3Decomposition_Param', path: '/controlPage/paramConfiguration/G3Decomposition' },
-]
-
 // 硫酸钠组态页面导航栏配置
 export const pageItems_Na2SO4=[
   { label: '反应分离1', code: 'Na2SO4_FY', path: '/configuratePage/Na2SO4_FY' },

+ 14 - 59
ui/src/router/index.js

@@ -147,68 +147,23 @@ export const constantRoutes = [
     component: () => import('@/views/controlPage/flowSelect/index'),
     name: 'ControlFlowSelect',//控制页面流程选择
   },
-  {
-    path: '/controlPage/flowSelect/mgm',
-    component: () => import('@/views/controlPage/flowSelect/mgm/index'),
-    name: 'MGM_Control',//mgm控制页面
-  },
-  {
-    path: '/controlPage/flowSelect/reaction',
-    component: () => import('@/views/controlPage/flowSelect/reaction/index'),
-    name: 'Reaction_Control',//反应控制页面
-  },
-  {
-    path: '/controlPage/flowSelect/solidLiquidSeparation',
-    component: () => import('@/views/controlPage/flowSelect/solidLiquidSeparation/index'),
-    name: 'SolidLiquidSeparation_Control',//一次固液分离控制页面
-  },
-  {
-    path: '/controlPage/flowSelect/flatBeltFiltration',
-    component: () => import('@/views/controlPage/flowSelect/flatBeltFiltration/index'),
-    name: 'FlatBeltFiltration_Control',//24平带式过滤控制页面
-  },
-  {
-    path: '/controlPage/flowSelect/G1Decomposition',
-    component: () => import('@/views/controlPage/flowSelect/G1Decomposition/index'),
-    name: 'G1Decomposition_Control',//G1分解控制页面
-  },
-  {
-    path: '/controlPage/flowSelect/G1AccessorySeparator',
-    component: () => import('@/views/controlPage/flowSelect/G1AccessorySeparator/index'),
-    name: 'G1AccessorySeparator_Control',//G1附属分离
-  },
-  {
-    path: '/controlPage/flowSelect/DissolvedCrystal',
-    component: () => import('@/views/controlPage/flowSelect/DissolvedCrystal/index'),
-    name: 'DissolvedCrystal_Control',//溶晶
-  },
-  {
-    path: '/controlPage/flowSelect/G3Decomposition',
-    component: () => import('@/views/controlPage/flowSelect/G3Decomposition/index'),
-    name: 'G3Decomposition_Control',//G3分解控制页面
-  },
-  // Na2SO4参数配置页面
-  {
-    path: '/controlPage/paramConfiguration/reaction',
-    component: () => import('@/views/controlPage/paramConfiguration/reaction/index'),
-    name: 'Reaction_Param',//反应参数配置页面
-  },
-  {
-    path: '/controlPage/paramConfiguration/flatBeltFiltration',
-    component: () => import('@/views/controlPage/paramConfiguration/flatBeltFiltration/index'),
-    name: 'FlatBeltFiltration_Param',//24平带式过滤参数配置页面
-  },
-  {
-    path: '/controlPage/paramConfiguration/G1Decomposition',
-    component: () => import('@/views/controlPage/paramConfiguration/G1Decomposition/index'),
-    name: 'G1Decomposition_Param',//G1分解参数配置页面
+  // {
+  //   path: '/controlPage/flowSelect/solidLiquidSeparation',
+  //   component: () => import('@/views/controlPage/flowSelect/solidLiquidSeparation/index'),
+  //   name: 'SolidLiquidSeparation_Control',//一次固液分离控制页面
+  // },
+  //Na2SO4控制页面(动态路由)
+   {
+    path: '/controlPage/flowSelect/:flowCode',
+    name: 'FlowControlPage',
+    component: () => import('@/views/controlPage/flowSelect/FlowControlPage.vue')
   },
+  //Na2SO4参数配置页面(动态路由)
   {
-    path: '/controlPage/paramConfiguration/G3Decomposition',
-    component: () => import('@/views/controlPage/paramConfiguration/G3Decomposition/index'),
-    name: 'G3Decomposition_Param',//G3分解参数配置页面
+    path: '/controlPage/paramConfiguration/:pageCode',
+    name: 'ParamConfigPage',
+    component: () => import('@/views/controlPage/paramConfiguration/ParamConfigPage.vue')
   },
-
   //上海组态页面
   {
     path: '/m1sj',//水解:m1Hydrolyze

+ 130 - 110
ui/src/views/controlPage/flowSelect/FlowControlPage.vue

@@ -1,75 +1,74 @@
 <template>
-  <div class="dcs_tanks">
-    <!-- 顶部固定头部 -->
-    <div class="fixed-header" ref="fixedHeader">
-      <HeaderComponent title="碳酸钠-流程控制" backTo="/controlPage/flowSelect" />
-      <PageNav :items="navItems" :currentCode="flowCode" @navClick="onNavClick"/>
-    </div>
+    <div class="dcs_tanks">
+        <!-- 固定头部 -->
+        <div class="fixed-header" ref="fixedHeader">
+            <HeaderComponent title="硫酸钠-参数配置" backTo="/controlPage/flowSelect" />
+            <PageNav :items="navItems" :currentCode="flowCode" />
+        </div>
 
-    <!-- 主内容区 -->
-    <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">
-                <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)"
-                           class="equipment_item" />
-              </div>
+        <!-- 主内容区 -->
+        <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 v-if="tankName === '1#反应釜-C3001'" title="原矿进料"
+                                    :dataArr="[5, 100, 100]" 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>
+
+                <!-- 右侧导航 -->
+                <TankNavigation :tanks="allTanks" :current-index="currentNavIndex" :header-height="headerHeight"
+                    @tankClick="scrollToTank" @scrollPositionChange="handleScrollPositionChange" />
             </div>
-
-            <div v-if="isEmptyData" class="no_data">未获取到设备数据</div>
-          </div>
         </div>
-
-        <!-- 右侧导航 -->
-        <TankNavigation :tanks="allTanks"
-                        :current-index="currentNavIndex"
-                        :header-height="headerHeight"
-                        @tankClick="scrollToTank"
-                        @scrollPositionChange="handleScrollPositionChange" />
-      </div>
     </div>
-  </div>
 </template>
 
 <script setup>
-import { ref, computed, onMounted, onBeforeUnmount, watchEffect } from 'vue';
-import { useRoute, useRouter } from 'vue-router';
+import { ref, onMounted, onBeforeUnmount, watch, watchEffect, computed } from 'vue';
+import { useRoute } from 'vue-router';
 import HeaderComponent from '@/components/DCS/HeaderComponent.vue';
 import PageNav from '@/components/GeneralComponents/control/PageNavComponent.vue';
-import TankNavigation from '@/components/GeneralComponents/control/TankNavigationComponent.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 { navItems_Na2SO4 } from '@/config';
-import { stompClient } from '@/utils/ws/stompClient';
-import { getPageEquipmentGroupByTankByFlowCode } from '@/api/hnyz/equipment';
+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 { stompClient } from '@/utils/ws/stompClient';
+import { updateZTPageConfig } from '@/api/dcs/configurePage';
+import { getFlowNav } from '@/api/hnyz/flow';
 const route = useRoute();
-const router = useRouter();
-
-const navItems = navItems_Na2SO4; // 暂时前端常量
 const flowCode = computed(() => route.params.flowCode);
-const title = computed(() => navItems.find(i => i.code === flowCode.value)?.label || '');
+const navItems = ref();
 
 const isLoading = ref(true);
 const hasError = ref(false);
 const errorMessage = ref('');
 const isEmptyData = ref(false);
-
 const fixedHeader = ref(null);
 const headerHeight = ref(0);
 const allTanks = ref([]);
@@ -77,82 +76,103 @@ const currentNavIndex = ref(-1);
 
 const deviceConfigGroup = ref({});
 const deviceDataGroup = ref({});
+const pageParams = ref({});
 
-function getComponentByType(type) {
-  if ([1,5].includes(Number(type))) return ValveControlComponent;
-  if (Number(type) === 2) return PumpControlComponent;
-  if (Number(type) === 4) return SensorControl;
-  return 'div';
-}
+const { generatePageParams } = useEquipmentLayout();
 
-function getValueByCode(tankName, code) {
-  const dataArr = deviceDataGroup.value[tankName] || [];
-  const target = dataArr.find(item => item.code === code);
-  return target?.value || [];
-}
-
-async function loadInitialData() {
-  isLoading.value = true;
-  hasError.value = false;
-  errorMessage.value = '';
-  try {
-    const res = await getPageEquipmentGroupByTankByFlowCode(flowCode.value);
-    deviceConfigGroup.value = Object.fromEntries(
-      Object.entries(res.data || {}).filter(([_, e]) => e?.length)
-    );
-    isEmptyData.value = Object.keys(deviceConfigGroup.value).length === 0;
-
-    // WebSocket 订阅
-    stompClient.unsubscribeFromPage(flowCode.value);
-    stompClient.subscribeToPage(flowCode.value, data => {
-      if (data) {
-        for (const key in data) deviceDataGroup.value[key] = data[key] || [];
-      }
-    });
-  } catch (err) {
-    hasError.value = true;
-    errorMessage.value = err.message || '加载失败';
-  } finally {
-    isLoading.value = false;
-  }
-}
+// 监控路由变化
+watch(() => route.params.flowCode, loadInitialData);
 
+// 生成导航罐体列表
 watchEffect(() => {
-  allTanks.value = Object.keys(deviceConfigGroup.value).map(name => ({
-    uniqueId: `tank-${name}`,
-    tankName: name,
-    shortName: name.length > 18 ? name.slice(0,16)+'...' : name
-  }));
+    allTanks.value = Object.keys(deviceConfigGroup.value).map(tankName => ({
+        uniqueId: `tank-${tankName}`,
+        tankName,
+        shortName: tankName.length > 18 ? tankName.slice(0, 16) + '...' : tankName
+    }));
+    currentNavIndex.value = -1;
 });
 
-function calculateHeaderHeight() { headerHeight.value = fixedHeader.value?.offsetHeight || 0; }
+// 计算固定头部高度
+const calculateHeaderHeight = () => { headerHeight.value = fixedHeader.value?.offsetHeight || 0; }
 
-function scrollToTank(tank,index) {
-  currentNavIndex.value = index;
-  const el = document.getElementById(`tank-${tank.tankName}`);
-  if(el) window.scrollTo({top: el.offsetTop - headerHeight.value - 20, behavior:'smooth'});
+// 加载初始数据
+async function loadInitialData() {
+    try {
+        isLoading.value = true; hasError.value = false; errorMessage.value = '';
+
+        const res = await getPageEquipmentGroupByTankByFlowCode(flowCode.value);
+        deviceConfigGroup.value = Object.fromEntries(
+            Object.entries(res.data || {}).filter(([_, eqs]) => eqs?.length)
+        );
+        isEmptyData.value = !Object.keys(deviceConfigGroup.value).length;
+
+        pageParams.value = generatePageParams(deviceConfigGroup.value);
+        await updatePageConfig();
+    } catch (err) {
+        hasError.value = true;
+        errorMessage.value = err.message || '加载设备分组失败';
+    } finally {
+        isLoading.value = false;
+    }
 }
 
-function handleScrollPositionChange(index){ currentNavIndex.value = index; }
+// 更新页面配置并订阅 WebSocket
+async function updatePageConfig() {
+    try {
+        await updateZTPageConfig(flowCode.value, pageParams.value);
+
+        stompClient.unsubscribeFromPage(flowCode.value);
+        stompClient.subscribeToPage(flowCode.value, data => {
+            if (!data) return;
+            try {
+                for (const key in data) { deviceDataGroup.value[key] = data[key] || []; }
+            } catch (err) {
+                console.error('更新设备数据失败:', err);
+            }
+        });
+    } catch (err) { console.error('页面配置失败:', err); }
+}
 
-// 点击导航栏跳转页面
-function onNavClick(item) {
-    router.push(item.path)
+// 根据设备类型返回组件
+function getComponentByType(equipmentType) {
+    const type = Number(equipmentType);
+    if ([1, 5].includes(type)) return ValveControlComponent;
+    if (type === 2) return PumpControlComponent;
+    if (type === 4) return SensorControl;
+    return 'div';
 }
 
-onMounted(() => {
-  calculateHeaderHeight();
-  window.addEventListener('resize', calculateHeaderHeight);
-  loadInitialData();
+// 根据罐体名和 code 获取设备数据
+function getValueByCode(tankName, code) {
+    const arr = deviceDataGroup.value[tankName] || [];
+    const item = arr.find(i => i.code === code);
+    return item ? item.value : [];
+}
+
+// 导航滚动
+function scrollToTank(tank, index) {
+    currentNavIndex.value = index;
+    const el = document.getElementById(`tank-${tank.tankName}`);
+    if (!el) return;
+    window.scrollTo({ top: el.offsetTop - headerHeight.value - 20, behavior: 'smooth' });
+}
+
+function handleScrollPositionChange(index) { currentNavIndex.value = index; }
+
+onMounted(async () => {
+    const res = await getFlowNav('Na2SO4', 'flowSelect');
+    navItems.value = res.data;
+    calculateHeaderHeight();
+    window.addEventListener('resize', calculateHeaderHeight);
+    loadInitialData();
 });
 
 onBeforeUnmount(() => {
-  stompClient.unsubscribeFromPage(flowCode.value);
-  window.removeEventListener('resize', calculateHeaderHeight);
+    stompClient.unsubscribeFromPage(flowCode.value);
+    window.removeEventListener('resize', calculateHeaderHeight);
 });
 </script>
-
-
 <style scoped lang="scss">
 .dcs_tanks {
     width: 100%;

+ 8 - 10
ui/src/views/controlPage/paramConfiguration/ParamConfigPage.vue

@@ -3,7 +3,7 @@
         <!-- 固定头部 -->
         <div class="fixed_header" ref="fixedHeader">
             <HeaderComponent title="硫酸钠 - 参数配置" backTo="/controlPage/flowSelect" />
-            <PageNav :items="paramItems_Na2SO4" :currentCode="pageCode" @navClick="onNavClick" />
+            <PageNav :items="navItems" :currentCode="pageCode" @navClick="onNavClick" />
         </div>
 
         <div class="main_container">
@@ -52,16 +52,16 @@ import ParamConfigComponent from '@/components/GeneralComponents/control/paramCo
 import TankNavigation from '@/components/GeneralComponents/control/TankNavigationComponent.vue'
 import { getPageEquipmentGroupByTankByFlowCode } from '@/api/hnyz/equipment'
 import { getAllParamConfigDataByCodeList } from '@/api/hnyz/param'
-import { paramItems_Na2SO4 } from '@/config'
+import { getFlowNav } from '@/api/hnyz/flow';
 
 const route = useRoute()
-const router = useRouter()
 
+const navItems = ref()
 // 动态路由 pageCode
 const pageCode = ref(route.params.pageCode)
 
-// 自动生成 flowCode: 首字母大写 + "_Control"
-const flowCode = computed(() =>pageCode.value.charAt(0).toUpperCase() + pageCode.value.slice(1))
+// 自动生成 flowCode: 首字母大写 
+const flowCode = computed(() => pageCode.value)
 
 // 状态
 const isLoading = ref(true)
@@ -113,10 +113,6 @@ function scrollToTank(tank, index) {
     if (element) window.scrollTo({ top: element.offsetTop - headerHeight.value - 20, behavior: 'smooth' })
 }
 
-// 点击导航栏跳转页面
-function onNavClick(item) {
-    router.push(item.path)
-}
 
 // 加载设备和参数
 async function loadConfigData() {
@@ -182,7 +178,9 @@ watch(
     }
 )
 
-onMounted(() => {
+onMounted(async () => {
+    const res = await getFlowNav('Na2SO4', 'paramConfiguration');
+    navItems.value = res.data;
     calculateHeaderHeight()
     window.addEventListener('resize', calculateHeaderHeight)
     loadConfigData()