Переглянути джерело

根据需求完成静态页面开发

lichunyang 1 день тому
батько
коміт
64644865f9

BIN
src/assets/icons/adaptive.png


+ 0 - 0
src/assets/icons/center.png → src/assets/icons/centerToIt.png


+ 0 - 0
src/assets/icons/vertical.png → src/assets/icons/distributeHorizontal.png


+ 0 - 0
src/assets/icons/horizontalDistribution.png → src/assets/icons/distributeVertical.png


BIN
src/assets/icons/horizontalCenterToIt.png


BIN
src/assets/icons/inputReport.png


BIN
src/assets/icons/number.png


BIN
src/assets/icons/renumber.png


BIN
src/assets/icons/resultReport.png


BIN
src/assets/icons/resultValidation.png


BIN
src/assets/icons/rotateLeft.png


BIN
src/assets/icons/rotateRight.png


+ 0 - 0
src/assets/icons/end.png → src/assets/icons/stop.png


BIN
src/assets/icons/tags.png


BIN
src/assets/icons/text.png


BIN
src/assets/icons/topo.png


+ 0 - 0
src/assets/icons/verticalReverse.png → src/assets/icons/verticalFlip.png


BIN
src/assets/icons/zoomIn.png


BIN
src/assets/icons/zoomOut.png


+ 5 - 5
src/components/layout/HeaderButtonBar.vue

@@ -22,12 +22,10 @@
       <!-- 普通标签页 -->
       <el-tab-pane v-for="tab in tabs" :key="tab.name" :name="tab.name">
         <template #label>
-          <el-tooltip :content="tab.label" placement="top">
             <span class="tab-label">
               <img :src="tab.icon" alt="icon" class="tab-icon" />
               {{ tab.label }}
             </span>
-          </el-tooltip>
         </template>
         <div class="button-group">
           <el-tooltip
@@ -282,7 +280,7 @@ const showSecondContent = ref(false)
 const fakeTabs = [
   {
     name: "exit",
-    label: "退出",
+    label: t("buttons.exit"),
     type: "",
     icon: exitIcon,
     onClick: () => {
@@ -291,7 +289,7 @@ const fakeTabs = [
   },
   {
     name: "save",
-    label: "保存",
+    label: t("buttons.save"),
     type: "",
     icon: saveProjectIcon,
     onClick: () => {
@@ -303,7 +301,7 @@ const fakeTabs = [
 const fakeTabs2 = [
   {
     name: "help",
-    label: "帮助",
+    label: t("tabs.help"),
     type: "",
     icon: helpIcon,
     onClick: () => {
@@ -500,6 +498,8 @@ const handleTabClick = (tab) => {
   flex-wrap: wrap;
   gap: 4px;
   padding: 6px;
+  border: 1px solid #d0cdcd;
+  border-radius: 5px;
 }
 
 .button-group .el-button {

+ 148 - 49
src/components/layout/TopoButtonBar.vue

@@ -18,6 +18,22 @@
           </span>
         </template>
         <template #default></template>
+      </el-tab-pane>
+            <!-- 新增开关 tab -->
+      <el-tab-pane name="modeSwitch" key="modeSwitch">
+        <template #label>
+          <span @click.stop>
+            <el-switch
+              v-model="modeSwitch"
+              :active-text="$t('buttons.selectMode')"
+              :inactive-text="$t('buttons.connectMode')"
+              inline-prompt
+              class="mode-switch"
+              @change="handleModeSwitch"
+            />
+          </span>
+        </template>
+        <template #default></template>
       </el-tab-pane>
       <!-- 普通标签页 -->
       <el-tab-pane v-for="tab in tabs" :key="tab.name" :name="tab.name">
@@ -32,15 +48,14 @@
             v-for="button in tab.buttons"
             :key="button.action"
             :content="button.label"
-            placement="top"
+            placement="bottom"
           >
-            <el-button
-              type=""
-              @click="emitButtonClick(button.action)"
+<el-button
+              :class="{ 'is-active': buttonStates[button.action] }"
+              @click="handleButtonClick(button)"
               class="icon-text-button"
             >
               <img :src="button.icon" alt="icon" class="button-icon" />
-              <!-- {{ button.label }} -->
             </el-button>
           </el-tooltip>
         </div>
@@ -56,35 +71,62 @@ const { t } = useI18n()
 
 import libraryIcon from "@/assets/icons/library.png" // 图层
 import addLayerIcon from "@/assets/icons/addLayer.png" // 新建图层
-import editIcon from "@/assets/icons/edit.png" // 编辑
-import importProIcon from "@/assets/icons/importPro.png" // 导入
-import tutorialIcon from "@/assets/icons/tutorial.png" // 配置
-import copyIcon from "@/assets/icons/copy.png" // 复制
-import cutIcon from "@/assets/icons/cut.png" // 剪切
-import pasteIcon from "@/assets/icons/paste.png" // 粘贴
+// 显示
+import gridIcon from "@/assets/icons/grid.png" // 显示网格
+import fixedGridIcon from "@/assets/icons/fixedGrid.png" // 固定到网格
+import rulerIcon from "@/assets/icons/ruler.png" // 显示标尺
+import textIcon from "@/assets/icons/text.png" // 显示文本
+import numberIcon from "@/assets/icons/number.png" // 显示节点编号
+import nodeNameIcon from "@/assets/icons/nodeName.png" // 显示节点名称
+import nodeIdIcon from "@/assets/icons/nodeId.png" // 显示节点ID
+import renumberIcon from "@/assets/icons/renumber.png" // 重新编号
+import previewWindowIcon from "@/assets/icons/previewWindow.png" // 预览窗口
+import displayConfigIcon from "@/assets/icons/displayConfiguration.png" // 显示配置
+// 视图
+import zoomInIcon from "@/assets/icons/zoomIn.png" // 放大
+import zoomOutIcon from "@/assets/icons/zoomOut.png" // 缩小
+import adaptiveIcon from "@/assets/icons/adaptive.png" // 适应窗口
+import actualSizeIcon from "@/assets/icons/actualSize.png" // 实际大小
+import dragIcon from "@/assets/icons/drag.png" // 拖拽
+// 排列
 import alignLeftIcon from "@/assets/icons/alignLeft.png" // 左对齐
 import alignRightIcon from "@/assets/icons/alignRight.png" // 右对齐
 import topToItIcon from "@/assets/icons/topToIt.png" // 顶部对齐
-import bottomToItIcon from "@/assets/icons/bottomToIt.png" // 底部
-import horizontalDistributionIcon from "@/assets/icons/horizontalDistribution.png" // 水平分布
-import verticalDistributionIcon from "@/assets/icons/vertical.png" // 垂直分布
-import rotateRightIcon from "@/assets/icons/rotateRight.png" // 右旋转
+import bottomToItIcon from "@/assets/icons/bottomToIt.png" // 底部对齐
+import centerToItIcon from "@/assets/icons/centerToIt.png" // 垂直居中
+import horizontalCenterToItIcon from "@/assets/icons/horizontalCenterToIt.png" // 水平居中
+import distributeVerticalIcon from "@/assets/icons/distributeVertical.png" // 垂直分布
+import distributeHorizontalIcon from "@/assets/icons/distributeHorizontal.png" // 水平分布
 import rotateLeftIcon from "@/assets/icons/rotateLeft.png" // 左旋转
+import rotateRightIcon from "@/assets/icons/rotateRight.png" // 右旋转
 import horizontalFlipIcon from "@/assets/icons/horizontalFlip.png" // 水平翻转
-import verticalFlipIcon from "@/assets/icons вертикReverse.png" // 垂直翻转
-import imageIcon from "@/assets/icons/image.png" // 图片
+import verticalFlipIcon from "@/assets/icons/verticalFlip.png" // 垂直翻转
+// 插入
 import graphicIcon from "@/assets/icons/graphic.png" // 图形
+import imageIcon from "@/assets/icons/image.png" // 图片
 import documentIcon from "@/assets/icons/document.png" // 文档
 import commentIcon from "@/assets/icons/comment.png" // 注释
+// 仿真
+import runIcon from "@/assets/icons/run.png" // 运行
+import preprocessingIcon from "@/assets/icons/preprocessing.png" // 预处理
+import stopIcon from "@/assets/icons/stop.png" // 停止
+// 结果
+import dashboardIcon from "@/assets/icons/dashboard.png" // 仪表盘
+import resultDisplayIcon from "@/assets/icons/resultDisplay.png" // 结果显示
+import resultValidationIcon from "@/assets/icons/resultValidation.png" // 结果校验
+import sensorsIcon from "@/assets/icons/sensors.png" // 传感器
+import tagsIcon from "@/assets/icons/tags.png" // 标签
+import thermodynamicsChartIcon from "@/assets/icons/thermodynamicsChart.png" // 热力学图表
+import hydraulicDiagramIcon from "@/assets/icons/hydraulicDiagram.png" // 水力图
+import resultReportIcon from "@/assets/icons/resultReport.png" // 结果报告
+import InputReportIcon from "@/assets/icons/inputReport.png" // 输入报告
+// 工具
 import toolIcon from "@/assets/icons/tool.png" // 工具
-import undoIcon from "@/assets/icons/withDrawing.png" // 撤销
-import redoIcon from "@/assets/icons/redo.png" // 重做
-import configIcon from "@/assets/icons/tutorial.png" // 配置
-import navigationIcon from "@/assets/icons/navigation.png" // 导航
-import parameterAssistantIcon from "@/assets/icons/parameterAssistant.png" // 参数助手
+import visualizationIcon from "@/assets/icons/visualization.png" // 可视化
+import splitIcon from "@/assets/icons/split.png" // 切分
 
 const emit = defineEmits(["button-click"])
-
+const modeSwitch = ref(false) // 开关状态,默认关闭(连接模式)
 const fakeTabs = [
   {
     name: "layer",
@@ -112,11 +154,16 @@ const tabs = [
     label: t("tabs.show"),
     icon: "",
     buttons: [
-      { action: "cut", label: t("buttons.cut"), icon: cutIcon },
-      { action: "copy", label: t("buttons.copy"), icon: copyIcon },
-      { action: "paste", label: t("buttons.paste"), icon: pasteIcon },
-      { action: "undo", label: t("buttons.undo"), icon: undoIcon },
-      { action: "redo", label: t("buttons.redo"), icon: redoIcon }
+      { action: "showGrid", label: t("buttons.showGrid"), icon: gridIcon, isToggle: true },
+      { action: "fixedGrid", label: t("buttons.fixedGrid"), icon: fixedGridIcon, isToggle: true },
+      { action: "showRuler", label: t("buttons.showRuler"), icon: rulerIcon, isToggle: true },
+      { action: "showText", label: t("buttons.showText"), icon: textIcon, isToggle: true },
+      { action: "showNumber", label: t("buttons.showNumber"), icon: numberIcon, isToggle: true },
+      { action: "showNodeName", label: t("buttons.showNodeName"), icon: nodeNameIcon, isToggle: true },
+      { action: "showNodeId", label: t("buttons.showNodeId"), icon: nodeIdIcon, isToggle: true },
+      { action: "renumber", label: t("buttons.renumber"), icon: renumberIcon },
+      { action: "previewWindow", label: t("buttons.previewWindow"), icon: previewWindowIcon },
+      { action: "displayConfig", label: t("buttons.displayConfig"), icon: displayConfigIcon }
     ]
   },
   {
@@ -124,47 +171,67 @@ const tabs = [
     label: t("tabs.view"),
     icon: "",
     buttons: [
-      { action: "importProject", label: t("buttons.importProject"), icon: "" },
-      {action: "importFMU",label: t("buttons.importFMU"),icon: ""},
-      {action: "importCAD",label: t("buttons.importCAD"),icon: ""},
-      {action: "exportXML",label: t("buttons.exportXML"),icon: ""},
-      {action: "exportFMU",label: t("buttons.exportFMU"),icon: ""}
+      { action: "zoomOut", label: t("buttons.zoomOut"), icon: zoomOutIcon },
+      {action: "zoomIn",label: t("buttons.zoomIn"),icon: zoomInIcon},
+      {action: "fitView",label: t("buttons.fitView"),icon: adaptiveIcon},
+      {action: "actualSize",label: t("buttons.actualSize"),icon: actualSizeIcon},
+      {action: "grag",label: t("buttons.grag"),icon: dragIcon}
     ]
   },
   {
     name: "arrange",
     label: t("tabs.arrange"),
-    icon: configIcon,
+    icon: "",
     buttons: [
-      {action: "unitManagement",label: t("buttons.unitManagement"),icon: ""},
-      {action: "preference",label: t("buttons.preference"),icon: ""}
+      {action: "alignLeft",label: t("buttons.alignLeft"),icon: alignLeftIcon},
+      {action: "alignRight",label: t("buttons.alignRight"),icon: alignRightIcon},
+      {action: "alignTop",label: t("buttons.alignTop"),icon: topToItIcon},
+      {action: "alignBottom",label: t("buttons.alignBottom"),icon: bottomToItIcon},
+      {action: "alignMiddle",label: t("buttons.alignMiddle"),icon: centerToItIcon},
+      {action: "alignCenter",label: t("buttons.alignCenter"),icon: horizontalCenterToItIcon},
+      {action: "distributeVertical",label: t("buttons.distributeVertical"),icon: distributeVerticalIcon},
+      {action: "distributeHorizontal",label: t("buttons.distributeHorizontal"),icon: distributeHorizontalIcon},
+      {action: "rotateLeft",label: t("buttons.rotateLeft"),icon: rotateLeftIcon},
+      {action: "rotateRight",label: t("buttons.rotateRight"),icon: rotateRightIcon},
+      {action: "horizontalFlip",label: t("buttons.horizontalFlip"),icon: horizontalFlipIcon},
+      {action: "verticalFlip",label: t("buttons.verticalFlip"),icon: verticalFlipIcon}
     ]
   },
   {
     name: "insert",
     label: t("tabs.insert"),
-    icon: configIcon,
+    icon: "",
     buttons: [
-      {action: "unitManagement",label: t("buttons.unitManagement"),icon: ""},
-      {action: "preference",label: t("buttons.preference"),icon: ""}
+      {action: "graphic",label: t("buttons.graphic"),icon: graphicIcon},
+      {action: "image",label: t("buttons.image"),icon: imageIcon},
+      {action: "document",label: t("buttons.document"),icon: documentIcon},
+      {action: "comment",label: t("buttons.comment"),icon: commentIcon}
     ]
   },
   {
     name: "simulation",
     label: t("tabs.simulation"),
-    icon: configIcon,
+    icon: "",
     buttons: [
-      {action: "unitManagement",label: t("buttons.unitManagement"),icon: ""},
-      {action: "preference",label: t("buttons.preference"),icon: ""}
+      {action: "run", label: t("buttons.run"), icon: runIcon},
+      {action: "preprocess", label: t("buttons.preprocess"), icon: preprocessingIcon},
+      {action: "stop", label: t("buttons.stop"), icon: stopIcon}
     ]
   },
   {
     name: "result",
     label: t("tabs.result"),
-    icon: configIcon,
+    icon: "",
     buttons: [
-      {action: "unitManagement",label: t("buttons.unitManagement"),icon: ""},
-      {action: "preference",label: t("buttons.preference"),icon: ""}
+      {action: "dashboard", label: t("buttons.dashboard"), icon: dashboardIcon},
+      {action: "resultDisplay", label: t("buttons.resultDisplay"), icon: resultDisplayIcon},
+      {action: "resultValidation", label: t("buttons.resultValidation"), icon: resultValidationIcon},
+      {action: "sensors", label: t("buttons.sensors"), icon: sensorsIcon},
+      {action: "tags", label: t("buttons.tags"), icon: tagsIcon},
+      {action: "thermodynamicsChart", label: t("buttons.thermodynamicsChart"), icon: thermodynamicsChartIcon},
+      {action: "hydraulicDiagram", label: t("buttons.hydraulicDiagram"), icon: hydraulicDiagramIcon},
+      {action: "resultReport", label: t("buttons.resultReport"), icon: resultReportIcon},
+      {action: "inputReport", label: t("buttons.inputReport"), icon: InputReportIcon}
     ]
   },
   {
@@ -172,21 +239,41 @@ const tabs = [
     label: t("tabs.tools"),
     icon: toolIcon,
     buttons: [
-      { action: "wizard", label: t("buttons.wizard"), icon: navigationIcon },
-      {action: "parameterAssisant",label: t("buttons.parameterAssistant"),icon: parameterAssistantIcon}
+      { action: "visualization", label: t("buttons.visualization"), icon: visualizationIcon },
+      {action: "split", label: t("buttons.split"), icon: splitIcon}
     ]
   }
 ]
 
 const activeTab = ref(tabs[0].name)
 
-const emitButtonClick = (action) => {
-  emit("button-click", action)
+const buttonStates = ref({
+  showGrid: false,
+  fixedGrid: false,
+  showRuler: false,
+  showText: false,
+  showNumber: false,
+  showNodeName: false,
+  showNodeId: false
+})
+
+const handleButtonClick = (button) => {
+  if (button.isToggle) {
+    buttonStates.value[button.action] = !buttonStates.value[button.action]
+    emit("button-click", { action: button.action, isActive: buttonStates.value[button.action] })
+  } else {
+    emit("button-click", { action: button.action })
+  }
 }
 
 const handleTabClick = (tab) => {
   console.log("Tab switched to:", tab.name)
 }
+
+const handleModeSwitch = (value) => {
+  console.log(value ? '切换到选择模式' : '切换到连接模式');
+  // 在这里添加切换模式的逻辑
+};
 </script>
 
 <style scoped>
@@ -325,6 +412,12 @@ const handleTabClick = (tab) => {
   padding: 0 !important;
 }
 
+:deep(#tab-modeSwitch) {
+  padding: 0 !important;
+  display: flex;
+  align-items: center;
+}
+
 .open-page-header-icons {
   width: 150px;
   display: flex;
@@ -337,4 +430,10 @@ const handleTabClick = (tab) => {
   border: none;
   margin: 0;
 }
+
+.icon-text-button.is-active {
+  background-color: #409eff;
+  color: #fff;
+  border-color: #409eff;
+}
 </style>

+ 176 - 76
src/components/layout/home.vue

@@ -1,22 +1,42 @@
 <template>
   <div class="home-page">
     <el-header>
-        <myheader />
+      <myheader />
     </el-header>
     <el-main>
       <div class="home-page-container">
         <div class="home-page-header">
           <HeaderTabs @button-click="handleButtonClick" />
-          <!-- <el-tabs v-model="activeTab"> -->
-            <!-- <el-tab-pane label="FILE" name="file"></el-tab-pane> -->
-            <!-- <el-tab-pane label="MODEL" name="model"></el-tab-pane> -->
-            <!-- <el-tab-pane label="RUN" name="run"></el-tab-pane>
-            <el-tab-pane label="RESULT" name="result"></el-tab-pane> -->
-          <!-- </el-tabs> -->
-          <!-- <HeaderIcons /> -->
         </div>
         <div class="home-page-content">
-          <router-view />
+          <router-view :key="activeProjectId" />
+        </div>
+        <!-- 底部项目标签栏 -->
+        <div class="project-tabs">
+          <el-tabs
+            v-model="activeProjectId"
+            type="card"
+            closable
+            class="project-tabs-bar"
+            @tab-click="handleProjectTabClick"
+            @tab-remove="handleProjectTabRemove"
+          >
+            <el-tab-pane
+              v-for="project in projectStore.projects"
+              :key="project.projectId"
+              :name="project.projectId"
+            >
+              <template #label>
+                <span class="project-tab-label">
+                  <img :src="modelIcon" alt="Project Icon" class="tab-icon" />
+                  {{ project.projectName }}
+                </span>
+              </template>
+            </el-tab-pane>
+          </el-tabs>
+          <el-button class="add-project-btn" type="text" @click="addNewProject">
+            <el-icon><Plus /></el-icon>
+          </el-button>
         </div>
       </div>
     </el-main>
@@ -24,34 +44,36 @@
 </template>
 
 <script setup>
-import myheader from '@/components/layout/header.vue'
-import HeaderIcons from '@/components/layout/HeaderIcons.vue'
-import HeaderTabs  from "./HeaderButtonBar.vue"
-
-import { RouterView, RouterLink,useRouter, useRoute } from "vue-router"
-import { request, enPassword } from "@/utils/request";
-import { ElMessage } from 'element-plus'
-
-import { useI18n } from 'vue-i18n'
-
-const { t, locale } = useI18n()
+import myheader from '@/components/layout/header.vue';
+import HeaderTabs from './HeaderButtonBar.vue';
+import { RouterView, useRouter, useRoute } from 'vue-router';
+import { ElMessage, ElIcon } from 'element-plus';
+import { Plus } from '@element-plus/icons-vue';
+import { useProjectStore } from '@/store/project';
+import { useI18n } from 'vue-i18n';
+import modelIcon from '@/assets/icons/model.png';
+import { ref, computed, watch, onMounted, nextTick } from 'vue';
 
+const { t } = useI18n();
 const router = useRouter();
-// 获取路由实例
-const route = useRoute()
-
+const route = useRoute();
+const projectStore = useProjectStore();
+const addButtonLeft = ref(70); // 默认位置
 
 // 根据当前路由路径设置 activeTab
-let activeTab = ref(route.path.slice(1) || 'model');
-let pid = ref('')
+const activeTab = ref(route.path.slice(1) || 'model');
+const activeProjectId = computed(() => projectStore.activeProjectId);
 
 // 监听路由变化,同步 activeTab
-watch(() => route.path, (newPath) => {
-  const tabName = newPath.slice(1);
-  if (tabName) {
-    activeTab.value = tabName;
+watch(
+  () => route.path,
+  (newPath) => {
+    const tabName = newPath.slice(1);
+    if (tabName) {
+      activeTab.value = tabName;
+    }
   }
-});
+);
 
 // 监听 activeTab 变化,导航到对应路由
 watch(activeTab, (newTab) => {
@@ -60,29 +82,78 @@ watch(activeTab, (newTab) => {
   }
 });
 
+// 更新 + 按钮位置
+const updateAddButtonPosition = () => {
+  nextTick(() => {
+    const navWrap = document.querySelector('.project-tabs-bar .el-tabs__nav-wrap');
+    const nav = document.querySelector('.project-tabs-bar .el-tabs__nav');
+    if (nav && navWrap) {
+      const tabs = nav.querySelectorAll('.el-tabs__item');
+      const lastTab = tabs[tabs.length - 1];
+      if (lastTab) {
+        const rect = lastTab.getBoundingClientRect();
+        const navWrapRect = navWrap.getBoundingClientRect();
+        addButtonLeft.value = rect.right - navWrapRect.left + 40;
+        
+      } else {
+        addButtonLeft.value = 70; // 默认位置
+      }
+    } else {
+      addButtonLeft.value = 70; // 默认位置
+    }
+  });
+};
+
+// 监听 projects 和 activeProjectId 变化,更新按钮位置
+watch(() => projectStore.projects.length, updateAddButtonPosition);
+watch(() => projectStore.activeProjectId, updateAddButtonPosition);
+
+onMounted(() => {
+  updateAddButtonPosition();
+});
+
 const handleButtonClick = (action) => {
-  console.log("Button clicked:", action)
-  // 这里处理各个按钮的点击事件
   switch (action) {
-    case 'exit':
-      // 退出逻辑
-      break
-    case 'save':
-      // 保存逻辑
-      break
-    case 'undo':
-      // 撤销逻辑
-      break
-    // 其他操作...
+    case 'importProject':
+      const newProject = {
+        projectId: `proj-${Date.now()}`,
+        projectName: `Project ${projectStore.projects.length + 1}`,
+      };
+      projectStore.addProject(newProject);
+      ElMessage.success(t('message.importSuccess'));
+      nextTick(() => {
+        updateAddButtonPosition(); // 手动触发位置更新
+      });
+      break;
   }
-}
+};
+
+const handleProjectTabClick = (tab) => {
+  projectStore.setActiveProject(tab.props.name);
+};
 
+const handleProjectTabRemove = (projectId) => {
+  projectStore.removeProject(projectId);
+  nextTick(() => {
+    updateAddButtonPosition(); // 手动触发位置更新
+  });
+};
 
+const addNewProject = () => {
+  const newProject = {
+    projectId: `proj-${Date.now()}`,
+    projectName: `Project ${projectStore.projects.length + 1}`,
+  };
+  projectStore.addProject(newProject);
+  ElMessage.success(t('message.projectAdded'));
+  nextTick(() => {
+    updateAddButtonPosition(); // 手动触发位置更新
+  });
+};
 </script>
 
 <style scoped>
-
-.el-header{
+.el-header {
   background-color: #075679;
   margin: 0 2px 0 0 !important;
 }
@@ -90,60 +161,55 @@ const handleButtonClick = (action) => {
 .home-page {
   width: 100%;
   height: 100vh;
+  overflow: hidden;
   overflow: auto;
 }
 
 .home-page-container {
   width: 100%;
   height: 100%;
+  overflow: hidden;
   overflow: auto;
 }
 
 .home-page-header {
   display: flex;
-  flex-direction: column; /* 改为垂直布局 */
-  justify-content: flex-start; /* 顶部对齐 */
-  align-items: flex-start; /* 左对齐 */
+  flex-direction: column;
+  justify-content: flex-start;
+  align-items: flex-start;
   width: 100%;
-  min-height: 80px; /* 最小高度,允许扩展 */
-  background: #FFFFFF;
-}
-
-.home-page-header-title{
-  margin-left: 10px;
-  font-size: 15px;
-  color: #333333;
+  min-height: 80px;
+  background: #ffffff;
 }
 
-.home-page-header-icons .el-button {
-  padding: 0;
-  border: none;
-  margin: 0;
+.project-tabs {
+  width: 100%;
+  padding: 0 10px;
+  background: #f5f7fa;
+  border-top: 1px solid #e4e7ed;
+  position: relative;
 }
 
-.home-page-content{
+.home-page-content {
   display: flex;
   width: 100%;
-  height: calc(100vh - 32px - 89px);
-
+  height: calc(100vh - 32px - 89px - 39px); /* 减去顶部和底部的高度 */
 }
 
-.home-page-leftside{
-  width: 200px;
-  height: 100%;
-  background: #FFFFFF;
-  border-radius: 0px 0px 0px 0px;
-  border: 1px solid #EEEEEE;
-}
-
-.home-page-rightside{
-  flex: 1;
-  height: 100%;
+.add-project-btn {
+  position: absolute;
+  top: 6px;
+  left: v-bind('`${addButtonLeft}px`');
+  color: #000000;
+  font-size: 16px;
+  padding: 0;
+  height: 32px;
+  line-height: 32px;
 }
 
 :deep(.el-header) {
-  padding: 0; 
-  margin: 0; 
+  padding: 0;
+  margin: 0;
   height: auto;
 }
 
@@ -153,4 +219,38 @@ const handleButtonClick = (action) => {
   flex: 1;
 }
 
+:deep(.project-tabs-bar .el-tabs__content) {
+  display: none; /* 仅隐藏 project-tabs-bar 的标签内容 */
+}
+
+:deep(.project-tabs-bar .el-tabs__header) {
+  margin: 3px 0 1px;
+  background: #f5f7fa;
+  position: relative;
+}
+
+:deep(.project-tabs-bar .el-tabs__item) {
+  color: #000000;
+  background: #e3e3e5;
+  border: 1px solid #c5c7ca;
+  height: 32px;
+  line-height: 32px;
+  padding: 0 12px;
+  display: flex;
+  align-items: center;
+}
+
+:deep(.project-tabs-bar .el-tabs__item.is-active) {
+  background: #ffffff;
+  color: #000000;
+}
+
+:deep(.el-tabs--card>.el-tabs__header .el-tabs__item.is-active.is-closable){
+  padding: 0 3px 0 0 !important;
+  margin: 0 !important;
+}
+
+:deep(.project-tabs-bar .el-tabs__nav-wrap) {
+  overflow-x: auto;
+}
 </style>

+ 50 - 1
src/locales/en.json

@@ -157,7 +157,56 @@
     "unitManagement": "Unit Management",
     "preference": "Preference",
     "wizard": "Wizard",
-    "parameterAssistant": "Parameter Assistant"
+    "parameterAssistant": "Parameter Assistant",
+    "library": "Library",
+    "addLayer": "AddLayer",
+    "showGrAid": "ShowGrAid",
+    "fixedGSrid": "FixedGSrid",
+    "showRuler": "ShowRuler",
+    "showText": "ShowText",
+    "showNumber": "ShowNumber",
+    "previewWSindow": "PreviewWSindow",
+    "showNodeName": "ShowNodeName",
+    "showNoSdeId": "ShowNoSdeId",
+    "renumber": "Renumber",
+    "displayConfig": "DisplayConfig",
+    "zooDmIn": "ZooDmIn",
+    "zoomZOut": "ZoomZOut",
+    "fitZView": "FitZView",
+    "actualSize": "ActualSize",
+    "grag": "Grag",
+    "alignLeft": "AlignLeft",
+    "alignCenter": "AlignCenter",
+    "alignRight": "AlignRight",
+    "alignTop": "AlignTop",
+    "alignMiddle": "AlignMiddle",
+    "alignBottom": "AlignBottom",
+    "distributeHorizontal": "DistributeHorizontal",
+    "distributeVertical": "DistributeVertical",
+    "rotateRight": "RotateRight",
+    "rotateLeft": "RotateLeft",
+    "horizontalFlip": "HorizontalFlip",
+    "verticalFlip": "VerticalFlip",
+    "graphic": "Graphic",
+    "image": "Image",
+    "document": "Document",
+    "comment" : "Comment",
+    "run" : "Run",
+    "preprocess": "Preprocess",
+    "stop" : "Stop",
+    "dashboard" : "Dashboard",
+    "resultDisplay" : "ResultDisplay",
+    "resultValidation" : "ResultValidation",
+    "sensors" : "Sensors",
+    "tags" : "Tags",
+    "thermodynamicsChart" :"ThermodynamicsChart",
+    "hydraulicDiagram" : "HydraulicDiagram",
+    "resultReport" : "ResultReport",
+    "inputReport" : "InputReport",
+    "visualization" : "Visualization",
+    "split" : "Split",
+    "selectMode" : "SelectModel",
+    "connectMode" : "ConnectModel"
   },
     "tabs":{
     "edit":"Edit",

+ 51 - 1
src/locales/zh-CN.json

@@ -157,7 +157,57 @@
     "unitManagement": "单位管理",
     "preference": "偏好设置",
     "wizard": "向导",
-    "parameterAssistant": "参数助手"
+    "parameterAssistant": "参数助手",
+    "library": "图层",
+    "addLayer": "新建图层",
+    "showGrid": "显示网格",
+    "fixedGrid": "固定到网格",
+    "showRuler": "显示标尺",
+    "showText" : "显示文本",
+    "showNumber": "显示节点编号",
+    "previewWindow": "预览窗口",
+    "showNodeName": "显示节点名称",
+    "showNodeId": "显示节点ID",
+    "renumber": "重新编号",
+    "displayConfig": "显示配置",
+    "zoomIn": "放大",
+    "zoomOut": "缩小",
+    "fitView": "适应视图",
+    "actualSize": "实际大小",
+    "grag": "拖拽",
+    "alignLeft": "左对齐",
+    "alignCenter": "水平居中",
+    "alignRight": "右对齐",
+    "alignTop": "顶端对齐",
+    "alignMiddle": "垂直居中",
+    "alignBottom": "底端对齐",
+    "distributeHorizontal": "水平分布",
+    "distributeVertical": "垂直分布",
+    "rotateRight": "右旋转",
+    "rotateLeft": "左旋转",
+    "horizontalFlip": "水平翻转",
+    "verticalFlip": "垂直翻转",
+    "graphic": "图形",
+    "image": "图片",
+    "document": "文档",
+    "comment" : "注释",
+    "run" : "运行",
+    "preprocess": "预处理",
+    "stop" : "停止",
+    "dashboard" : "仪表盘",
+    "resultDisplay" : "结果显示",
+    "resultValidation" : "结果校验",
+    "sensors" : "传感器",
+    "tags" : "标签",
+    "thermodynamicsChart" : "热力学图表",
+    "hydraulicDiagram" : "水力图",
+    "resultReport" : "结果报告",
+    "inputReport" : "输入报告",
+    "visualization" : "可视化",
+    "split" : "切分",
+    "selectMode" : "选择模式",
+    "connectMode" : "连接模式"
+
   },
   "tabs":{
     "edit":"编辑",

+ 92 - 33
src/store/project.js

@@ -1,39 +1,98 @@
-import { defineStore } from 'pinia'
-import { ref } from 'vue'
+import { defineStore } from 'pinia';
+import { ref } from 'vue';
+import { ElMessage } from 'element-plus';
 
-export const useProjectStore = defineStore('project', () => {
-  const pid = ref('');
-  const runtype = ref("");
+export const useProjectStore = defineStore(
+  'project',
+  () => {
+    const pid = ref('proj-default');
+    const runtype = ref('');
+    const projectInfo = ref(null);
+    const projects = ref([
+      {
+        projectId: 'proj-default',
+        projectName: 'Default Project',
+      },
+    ]); // 初始化时添加默认项目
+    const activeProjectId = ref('proj-default'); // 默认激活项目
 
-  const projectInfo = ref(null);
-  // 项目信息
-  const setProjectInfo = (info) => {
-    projectInfo.value = info;
-  }
+    const addProject = (project) => {
+      projects.value.push(project);
+      activeProjectId.value = project.projectId; // 激活新添加的项目
+      pid.value = project.projectId;
+    };
 
-  const setpid = (val) => {
-    pid.value = val
-  }
+    const removeProject = (projectId) => {
+      if (projects.value.length <= 1) {
+        ElMessage.warning('至少保留一个项目!');
+        return;
+      }
+      projects.value = projects.value.filter((p) => p.projectId !== projectId);
+      if (activeProjectId.value === projectId) {
+        activeProjectId.value = projects.value[0]?.projectId || '';
+        pid.value = activeProjectId.value;
+      }
+    };
 
-  const setruntype = (val) => {
-    runtype.value = val
-  }
+    const setActiveProject = (projectId) => {
+      if (projects.value.some((p) => p.projectId === projectId)) {
+        activeProjectId.value = projectId;
+        pid.value = projectId;
+      }
+    };
 
-  const clearproject = () => {
-    pid.value = ''
-    projectInfo.value = null; // 清除项目信息
-    runtype.value = ""; // 清除运行类型
-  }
-  
-  return {
-    pid,
-    setpid,
-    runtype,
-    setruntype,
-    clearproject,
-    projectInfo,
-    setProjectInfo,
+    const getActiveProject = () => {
+      return projects.value.find((p) => p.projectId === activeProjectId.value) || null;
+    };
+
+    const setProjectInfo = (info) => {
+      projectInfo.value = info;
+      if (info && info.projectId) {
+        addProject({
+          projectId: info.projectId,
+          projectName: info.projectName || `Project ${projects.value.length + 1}`,
+        });
+      }
+    };
+
+    const setpid = (val) => {
+      pid.value = val;
+    };
+
+    const setruntype = (val) => {
+      runtype.value = val;
+    };
+
+    const clearproject = () => {
+      pid.value = 'proj-default';
+      runtype.value = '';
+      projectInfo.value = null;
+      projects.value = [
+        {
+          projectId: 'proj-default',
+          projectName: 'Default Project',
+        },
+      ]; // 重置时保留默认项目
+      activeProjectId.value = 'proj-default';
+    };
+
+    return {
+      pid,
+      runtype,
+      projectInfo,
+      projects,
+      activeProjectId,
+      setpid,
+      setruntype,
+      setProjectInfo,
+      addProject,
+      removeProject,
+      setActiveProject,
+      getActiveProject,
+      clearproject,
+    };
+  },
+  {
+    persist: true,
   }
-},{
-  persist: true, // 启用持久化
-})
+);

+ 13 - 14
src/views/model/index.vue

@@ -97,10 +97,10 @@
             <div class="main-header">
               <div class="header-content">
                 <div class="image-container">
-                  <img :src="expandIcon" alt="Topology Image" />
+                  <img :src="topoIcon" alt="Topology Image" />
                 </div>
                 <div class="text-container">
-                  <h2>Topology</h2>
+                  <span>Topology</span>
                 </div>
               </div>
               <TopoButtonBar @button-click="btnfunc" />
@@ -216,7 +216,7 @@
         </splitpanes>
       </pane>
     </splitpanes>
-
+  </el-container>
     <!-- 模拟数据弹窗 -->
     <SLDataDialog ref="SLdatadialogref" @selectRunType="handleSelectRunType" />
     <!-- 运行弹窗 -->
@@ -234,7 +234,6 @@
       @update:visible="showPersonUnitDialog = $event"
       @confirm="handlePersonUnitConfirm"
     />
-  </el-container>
 </template>
 
 <script setup>
@@ -269,6 +268,7 @@ import { useVueFlow } from "@vue-flow/core"
 import SystemUnitDialog from "@/components/SystemUnitDialog.vue"
 import PersonUnitDialog from "@/components/PersonUnitDialog.vue"
 import systemIcon from "@/assets/img/treeSystemIcon.png"
+import topoIcon from "@/assets/icons/topo.png"
 import expandIcon from "@/assets/img/treeExpand.png"
 import collapseIcon from "@/assets/img/treeCollapse.png"
 
@@ -697,7 +697,7 @@ onMounted(() => {
 }
 
 .main-header {
-  background: #eeeeee;
+  background: #edf2fa;
   border-radius: 0px 0px 0px 0px;
   border: 2px solid #eeeeee;
 }
@@ -706,23 +706,22 @@ onMounted(() => {
   display: flex;
   align-items: center;
   justify-content: space-between;
-  max-width: 140px;
+  max-width: 110px;
   background-color: #ffffff;
-  padding: 0;
+  padding: 3px;
   margin: 0;
-  border-top: #77c5e6 3px solid;
+  border-top: #075679 3px solid;
 }
 
 .image-container {
   flex: 1;
-  max-width: 50%;
-  padding-right: 10px;
+  max-width: 45%;
+  padding-right: 5px;
 }
 
-.image-container img {
-  width: 100%;
-  height: auto;
-  object-fit: cover;
+.text-container{
+  padding: 0 5px 0 0;
+  font-size: 16px;
 }
 
 .diysplitpanes {