Prechádzať zdrojové kódy

增加多项目管理功能

lichunyang 1 mesiac pred
rodič
commit
d27f5efd82

BIN
src/assets/icons/open.png


+ 109 - 0
src/components/dialog/RunDialog.vue

@@ -0,0 +1,109 @@
+<template>
+  <!-- 运行弹窗 -->
+  <el-dialog
+    v-model="visible"
+    align-center
+    :append-to-body="true"
+    width="700"
+    class="dialog_class"
+    draggable
+  >
+    <template #header="{ titleId, titleClass }">
+      <div class="my-header">
+        <h4 :id="titleId" :class="titleClass">实时仿真</h4>
+      </div>
+    </template>
+    <div class="dialog-content">
+      <div :size="spacesize" class="spaceclass">
+        <el-button class="custom-icon-button" @click="runProject">
+          <img :src="run" style="width: 24px" />
+        </el-button>
+        <el-button class="custom-icon-button">
+          <img :src="stop" style="width: 24px" />
+        </el-button>
+      </div>
+      <div class="echarts-container">
+        <EchartLine
+          :key="ChartKey"
+          :chart-data="runData"
+          :x-label="runxLabel"
+          :y-label="runyLabel"
+        />
+      </div>
+    </div>
+  </el-dialog>
+</template>
+
+<script setup>
+import { request, getImage } from "@/utils/request"
+import { ElMessage } from "element-plus"
+import { useProjectStore } from "@/store/project"
+import run from "@/assets/img/run.png"
+import stop from "@/assets/img/stop.png"
+
+import EchartLine from "../echarts/EchartLine.vue"
+
+const props = defineProps({
+  runData: {
+    type: Array,
+    default: () => []
+  }
+})
+
+const visible = ref(false)
+let spacesize = ref(10)
+
+const projectStore = useProjectStore()
+let pid = computed(() => projectStore.pid || "")
+
+const ChartKey = ref(0)
+const runxLabel = ref("Time")
+const runyLabel = ref("Residual")
+
+function openDialog() {
+  visible.value = true
+}
+
+function closeDialog() {
+  visible.value = false
+}
+
+const runProject = () => {
+  const params = {
+    transCode: "ES0013",
+    pid: pid.value
+  }
+  request(params)
+    .then((res) => {
+      ElMessage.success("开始运行")
+    })
+    .catch((err) => {
+      ElMessage.error(err.returnMsg)
+    })
+}
+
+// 监听 runData 变化
+watch(
+  () => props.runData,
+  (newVal) => {
+    // console.log('runData changed:', newVal);
+    ChartKey.value++; // 修改 key 强制组件重新渲染
+  },
+  { deep: true } // 深度监听数组内容变化
+);
+
+// 暴露给父组件
+defineExpose({ openDialog, closeDialog })
+</script>
+
+<style scoped>
+.dialog-content {
+  display: flex;
+  flex-direction: column;
+}
+
+.echarts-container {
+  width: 100%;
+  height: 400px;
+}
+</style>

+ 294 - 0
src/components/dialog/SLDataDialog.vue

@@ -0,0 +1,294 @@
+<template>
+  <!-- 模拟数据弹窗 -->
+  <el-dialog
+    v-model="visible"
+    align-center
+    :append-to-body="true"
+    width="500"
+    class="dialog_class"
+    draggable
+  >
+    <template #header="{ titleId, titleClass }">
+      <div class="my-header">
+        <h4 :id="titleId" :class="titleClass">模拟数据</h4>
+      </div>
+    </template>
+
+    <el-table :data="tableSLData" border height="300" style="width: 100%">
+      <el-table-column type="index" width="40" label="" />
+      <el-table-column prop="name" label="属性"> </el-table-column>
+      <el-table-column prop="value" label="值">
+        <template #default="{ row }">
+          <el-select
+            v-if="row.valueType === 1"
+            v-model="row.value"
+            placeholder="请选择"
+            class="full-width-select"
+          >
+            <el-option
+              v-for="option in row.options"
+              :key="option.val"
+              :label="option.tag"
+              :value="option.val"
+            />
+          </el-select>
+          <el-input
+            v-else-if="row.valueType === 2"
+            v-model="row.value"
+            class="full-width-input"
+          />
+          <el-button v-else-if="row.valueType === 3" />
+          <div v-else>{{ row.valueDef }}</div>
+        </template>
+      </el-table-column>
+      <el-table-column prop="unit" label="单位" width="100">
+        <template #default="{ row }">
+          <el-select
+            v-if="row.unitType !== '无'"
+            v-model="row.unitDef"
+            placeholder="请选择"
+            :loading="moreOptionsLoading"
+            @focus="fetchUnitsForRow(row)"
+            @change="(newUnit) => handleUnitChange(row, newUnit)"
+            class="full-width-select"
+          >
+            <el-option
+              v-for="option in row.unitoptions"
+              :key="option.utId"
+              :label="option.value"
+              :value="option.value"
+            />
+          </el-select>
+          <div v-else>{{ row.unitType }}</div>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <template #footer>
+      <span class="lastbtn">
+        <el-button @click="closeDialog">{{ $t('dialog.cancel') }}</el-button>
+        <el-button type="primary" @click="saveSLTabelDialog">
+          {{ $t('dialog.ok') }}
+        </el-button>
+      </span>
+    </template>
+  </el-dialog>
+</template>
+
+<script setup>
+import { request, getImage } from "@/utils/request"
+import { ElMessage } from "element-plus"
+const visible = ref(false)
+const tableSLData = ref([])
+
+const runtype = ref('') // 用于分开不同求解运行
+
+// 单位选项缓存
+const unitOptionsCache = ref({});
+// 更多选项加载状态
+const moreOptionsLoading = ref(false);
+
+let emit = defineEmits(['selectRunType']);
+
+function openDialog(pid) {
+  getSLData(pid);
+  visible.value = true
+}
+
+function closeDialog() {
+  visible.value = false
+}
+
+// 组件参数点击下拉框调用接口获取单位
+const fetchUnitsForRow = async (row) => {
+  row.prevUnitDef = row.unitDef || row.unit || "无"
+  if (unitOptionsCache.value[row.unitType]) {
+    row.unitoptions  = unitOptionsCache.value[row.unitType]
+    if (!row.unitDef && row.unitOptions.length > 0) {
+      row.unitDef = row.unitOptions[0].value
+    }
+    return
+  }
+  moreOptionsLoading.value = true;
+
+  const params = {
+    transCode: "ES0019",
+    gutId: row.unitType
+  }
+  try {
+    const res = await request(params)
+    row.unitoptions  = res.rows || []
+    console.log("单位选项获取成功", row.unitoptions );
+    
+    unitOptionsCache.value[row.unitType] = row.unitoptions 
+    if (!row.unitType && row.unitoptions .length > 0) {
+      row.unitDef  = row.unitoptions [0].value
+    }
+    moreOptionsLoading.value = false;
+  } catch (err) {
+    moreOptionsLoading.value = false;
+    ElMessage.error(err.returnMsg || t("error.fetchFailed"))
+    row.unitoptions  = []
+    unitOptionsCache.value[row.unitType] = []
+  } finally {
+    moreOptionsLoading.value = false;
+  }
+}
+
+const handleUnitChange = (row, newUnit) => {
+  console.log("handleUnitChange called with row:", row, "and newUnit:", newUnit);
+  
+  if (!row || !row.value || isNaN(row.value)) {
+    ElMessage.warning('请输入有效数值')
+    return
+  }
+
+  const unitOptions = row.unitoptions
+  if (!unitOptions) {
+    console.error('单位选项未定义', col || row)
+    ElMessage.error('单位选项未定义')
+    return
+  }
+
+  const currentUnit = unitOptions.find(opt => opt.value === row.prevUnitDef)
+  if (!currentUnit) {
+    console.error('未找到当前单位信息', row.prevUnitDef)
+    ElMessage.error('未找到当前单位信息')
+    return
+  }
+
+  const targetUnit = unitOptions.find(opt => opt.value === newUnit)
+  if (!targetUnit) {
+    console.error('未找到目标单位信息', newUnit)
+    ElMessage.error('未找到目标单位信息')
+    return
+  }
+
+  const currentValue = parseFloat(row.value)
+  const newValue = ((currentValue - currentUnit.utOffset) / currentUnit.factor) * targetUnit.factor + targetUnit.utOffset
+  row.value = newValue.toFixed(6) // 提高精度
+  row.unit = newUnit
+  if (row) {
+    row.prevUnitDef = newUnit
+    tableSLData.value = [...tableSLData.value]
+  } else {
+    row.prevUnitDef = newUnit
+  }
+}
+
+
+const getSLData = async ( pid ) => {
+  const params = {
+    transCode: "ES0012",
+    pid: pid
+  }
+
+  try {
+    const res = await request(params) // 使用 await 等待请求完成
+    tableSLData.value = res.rows.map((item) => ({
+      ...item,
+      isVisible: true, // 默认所有行都可见
+      unitDef: item.unit || item.unitDef || ""
+    }))
+    console.log("tableSLData.value", tableSLData.value)
+
+    // 使用 for...of 循环以便使用 await
+    for (const item of tableSLData.value) {
+      if (!item.pcaId) continue
+
+      // 并行处理 value 和 unit 的初始化
+      const promises = []
+
+      if (item.valueType === 1) {
+        promises.push(getlistopt(item, "value"))
+      }
+
+      if (item.unitType !== "无") {
+        // promises.push(getlistopt(item, "unit"))
+      } else {
+        item.unit = "无"
+      }
+
+      // 等待当前 item 的所有初始化完成
+      await Promise.all(promises)
+    }
+
+    console.log("所有数据初始化完成")
+  } catch (err) {
+    console.error("初始化失败:", err)
+    ElMessage.error("初始化失败")
+  }
+}
+
+const saveSLTabelDialog = () => {
+  console.log("tableSLData:", tableSLData.value)
+  runtype.value = tableSLData.value.find((item)=>item.code === "SimulationType")?.value || '';
+  emit('selectRunType',runtype.value);
+  // dataType 为 -1 的数据
+  const validItems = tableSLData.value.filter((item) => item.dataType === -1)
+  const pcavals = validItems
+    .map((item) => {
+      const pcaId = item.pcaId ?? ""
+      const value = item.value ?? ""
+      const unit = item.unitDef != '' ? item.unitDef : "无"
+      return `${pcaId},${value},${unit}`
+    })
+    .join(";")
+  const params = {
+    transCode: "ES0008",
+    pcavals: pcavals
+  }
+  request(params)
+    .then((res) => {
+      ElMessage.success("保存成功")
+      // 执行保存逻辑
+      closeDialog()
+    })
+    .catch((err) => {
+      console.error("err", err)
+      ElMessage.error("保存失败")
+    })
+}
+
+const getlistopt = async (item, gettype) => {
+  let params = {}
+  if (gettype === "value") {
+    params = {
+      transCode: "BES001",
+      type: item.valueDef
+    }
+  } else {
+    params = {
+      transCode: "BES001",
+      type: item.unitType
+    }
+  }
+
+  try {
+    const res = await request(params)
+    // console.log("选项获取成功", res)
+
+    if (gettype === "value") {
+      if (
+        item.value === undefined ||
+        item.value === null ||
+        item.value === ""
+      ) {
+        item.value = res.rows?.[0]?.val ?? ""
+      }
+      item.options = res.rows || []
+    } else if (gettype === "unit") {
+      if (item.unit === undefined || item.unit === null || item.unit === "") {
+        item.unit = res.rows?.[0]?.val ?? ""
+      }
+      item.unitoptions = res.rows || []
+    }
+  } catch (err) {
+    console.error("err", err)
+    ElMessage.error("选项初始化失败")
+  }
+}
+
+// 暴露给父组件
+defineExpose({ openDialog, closeDialog })
+</script>

+ 250 - 10
src/components/layout/HeaderButtonBar.vue

@@ -1,7 +1,7 @@
 <template>
   <div class="header-button-bar" style="position: relative">
     <el-tabs v-model="activeTab" type="border-card" @tab-click="handleTabClick">
-      <!-- 特殊按钮 tab 退出 保存 -->
+      <!-- 特殊按钮 tab 退出 打开 保存 -->
       <el-tab-pane v-for="btn in fakeTabs" :key="btn.name" :name="btn.name">
         <template #label>
           <span @click.stop>
@@ -217,22 +217,115 @@
       </span>
     </template>
   </el-dialog>
+
+<el-dialog
+  v-model="dialog.projectListDialog"
+  align-center
+  :append-to-body="true"
+  width="800"
+  class="dialog_class"
+  draggable
+>
+  <template #header="{ titleId, titleClass }">
+    <div class="my-header">
+      <h4 :id="titleId" :class="titleClass">
+        {{ $t("dialog.projectlist") }}
+      </h4>
+    </div>
+  </template>
+
+  <template #default>
+    <div class="project-main-content custom-table">
+      <el-table
+        :data="projectlists"
+        style="width: 100%; height: 540px; overflow: auto"
+        @row-click="handleRowClick"
+        :row-class-name="tableRowClassName"
+      >
+        <el-table-column
+          type="index"
+          :label="$t('project.number')"
+          width="100"
+        ></el-table-column>
+        <el-table-column
+          prop="name"
+          :label="$t('project.name')"
+        ></el-table-column>
+        <el-table-column
+          prop="dirsize"
+          :label="$t('project.dirsize')"
+        ></el-table-column>
+        <el-table-column
+          prop="updateTime"
+          :label="$t('project.updateTime')"
+          min-width="100"
+        ></el-table-column>
+        <el-table-column
+          prop="uname"
+          :label="$t('project.uname')"
+        ></el-table-column>
+        <el-table-column
+          prop="keywords"
+          :label="$t('project.keywords')"
+        ></el-table-column>
+        <el-table-column
+          prop="remark"
+          :label="$t('project.description')"
+        ></el-table-column>
+      </el-table>
+    </div>
+    <div class="project-main-pagination">
+      <div class="custom-pagination">
+        <el-pagination
+          v-model:current-page="gd.currentPage4"
+          v-model:page-size="gd.pageSize4"
+          :page-sizes="[5, 10, 20, 50]"
+          background
+          size="small"
+          layout="prev, slot, sizes, pager, next"
+          :total="parseInt(gd.total)"
+          class="mt-4"
+          @size-change="handleSizeChange"
+          @current-change="handleCurrentChange2"
+        >
+          <template #default>
+            <span>{{ $t("project.total") }} {{ gd.total }}</span>
+          </template>
+        </el-pagination>
+      </div>
+    </div>
+  </template>
+
+  <template #footer>
+    <span class="lastbtn">
+      <el-button @click="dialog.projectListDialog = false">{{
+        $t("dialog.cancel")
+      }}</el-button>
+      <el-button @click="confirmSelected" :disabled="selectedRows.length === 0">
+        {{ $t("dialog.ok") }}
+      </el-button>
+    </span>
+  </template>
+</el-dialog>
 </template>
 
 <script setup>
 import { ref, computed } from "vue"
+import { request } from "@/utils/request"
 import { useRouter } from "vue-router"
 import { useI18n } from "vue-i18n"
 import { useUserStore } from "@/store/user"
 import { removeToken, removeUserId } from "@/utils/token"
 import { ElMessageBox, ElMessage } from "element-plus"
 import { ArrowDown, User, Key, SwitchButton } from '@element-plus/icons-vue'
+import { useProjectStore } from "@/store/project"
+import exitIcon from "@/assets/icons/exit.png"
 const userStore = useUserStore()
 const router = useRouter()
 const { t } = useI18n()
 const nickName = computed(() => userStore.userInfo.nickName || "User")
-
-import exitIcon from "@/assets/icons/exit.png" // 退出
+ // 退出
+import openIcon from "@/assets/icons/open.png" // 打开文件
 import saveFileIcon from "@/assets/icons/save.png" // 保存文件
 import saveProjectIcon from "@/assets/icons/saveProject.png" // 保存项目
 import editIcon from "@/assets/icons/edit.png" // 编辑
@@ -272,11 +365,13 @@ import navigationIcon from "@/assets/icons/navigation.png" // 导航
 import parameterAssistantIcon from "@/assets/icons/parameterAssistant.png" // 参数助手
 import helpIcon from "@/assets/icons/help.png" // 帮助
 import user from "@/assets/img/user.png" // 用户头像
-
-const showProfileDialog = ref(false)
-const showPasswordDialog = ref(false)
 const emit = defineEmits(["button-click"])
 const showSecondContent = ref(false)
+const projectStore = useProjectStore()
+
+onMounted(() => {
+  getprojectlist()
+})
 
 const fakeTabs = [
   {
@@ -285,7 +380,16 @@ const fakeTabs = [
     type: "",
     icon: exitIcon,
     onClick: () => {
-      console.log("点击了 退出")
+      handleExit();
+    }
+  },
+  {
+    name: "open",
+    label: t("buttons.open"),
+    type: "",
+    icon: openIcon,
+    onClick: () => {
+      handleOpenProject();
     }
   },
   {
@@ -294,7 +398,7 @@ const fakeTabs = [
     type: "",
     icon: saveProjectIcon,
     onClick: () => {
-      console.log("点击了 导入文件")
+      handleSaveProject();
     }
   }
 ]
@@ -390,11 +494,24 @@ const tabs = [
 
 const activeTab = ref(tabs[0].name)
 
+// 弹窗开关
 let dialog = ref({
   userinfoDialog: false,
-  changePassword: false
+  changePassword: false,
+  projectListDialog: false
 })
 
+// 分页
+let gd = ref({
+  total: 1,
+  currentPage4: 1,
+  pageSize4: 5,
+  searchtag: ""
+})
+
+let projectlists = ref([])
+let selectedRows = ref([])
+
 let pwd = ref({
   oldPassword: "",
   newPassword: "",
@@ -427,6 +544,105 @@ const emitButtonClick = (action) => {
 const handleTabClick = (tab) => {
   console.log("Tab switched to:", tab.name)
 }
+ // 退出项目
+const handleExit = () => {
+  router.push("/project");
+}
+
+// 打开项目
+const handleOpenProject = () => {
+  dialog.value.projectListDialog = true;
+  getprojectlist();
+}
+
+// 保存项目
+const handleSaveProject = () => {
+  console.log("Saving project...");
+}
+
+// 获取项目列表
+const getprojectlist = () => {
+  const params = {
+    transCode: "ES0001",
+    count: gd.value.pageSize4,
+    page: gd.value.currentPage4,
+    searchtag: gd.value.searchtag
+  }
+  request(params)
+    .then((res) => {
+      gd.value.total = res.total
+      projectlists.value = res.rows.map((item) => ({
+        ...item,
+        updateTime: item.updateTime.split(" +")[0]
+      }))
+    })
+    .catch((err) => {
+      console.error(err)
+      ElMessage.error(err.returnMsg)
+    })
+}
+
+// 处理行点击事件
+const handleRowClick = (row) => {
+  const index = selectedRows.value.findIndex((item) => item.pid === row.pid)
+  if (index === -1) {
+    selectedRows.value = [row] // 单选模式,只保留一个选中项
+    // selectedRows.value.push(row); // 如果是多选模式,使用 push
+  } else {
+    selectedRows.value.splice(index, 1) // 如果已选中,则取消选中
+  }
+}
+
+// 为行添加类名
+const tableRowClassName = ({ row }) => {
+  return selectedRows.value.some((item) => item.pid === row.pid)
+    ? "selected-row"
+    : ""
+}
+
+// 处理分页变化
+const handleSizeChange = (newSize) => {
+  gd.value.pageSize4 = newSize
+  gd.value.currentPage4 = 1
+  getprojectlist()
+}
+
+// 获取项目
+const handleCurrentChange2 = (val) => {
+  getprojectlist()
+}
+
+// 确认选中行
+const confirmSelected = () => {
+  if (selectedRows.value.length !== 1) {
+    ElMessage.warning('请选择一个项目!');
+    return;
+  }
+
+  const selected = selectedRows.value[0];
+  
+  const project = {
+    projectId: selected.pid,
+    projectName: selected.name || `Project ${projectStore.projects.length + 1}`,
+    keywords: selected.keywords || '',
+    remark: selected.remark || '',
+    flow: selected.flow ? selected.flow : { "nodes": [], "edges": [] },
+  };
+
+  // 如果项目已存在,更新信息;否则添加新项目
+  if (projectStore.projects.some((p) => p.projectId === selected.pid)) {
+    projectStore.updateProjectInfo(selected.pid, project);
+  } else {
+    projectStore.addProject(project);
+  }
+
+  // 设置激活项目
+  projectStore.setActiveProject(selected.pid);
+
+  // 跳转到首页
+  router.push({ path: '/home' });
+  dialog.value.projectListDialog = false;
+};
 </script>
 
 <style scoped>
@@ -530,7 +746,7 @@ const handleTabClick = (tab) => {
 .icon-only-button {
   padding: 0;
   min-width: 32px;
-  height: 44px;
+  height: 39px;
   display: flex;
   align-items: center;
   justify-content: center;
@@ -568,6 +784,10 @@ const handleTabClick = (tab) => {
   padding: 0 !important;
 }
 
+:deep(#tab-open) {
+  padding: 0 !important;
+}
+
 :deep(#tab-save) {
   padding: 0 !important;
 }
@@ -609,4 +829,24 @@ const handleTabClick = (tab) => {
   display: flex;
   justify-content: center;
 }
+
+/* 选中行的样式 */
+:deep(.el-table .selected-row) {
+  background-color: #f3f8fb !important;
+  position: relative;
+}
+
+:deep(.el-table .selected-row td:first-child) {
+  border-left: 4px solid #12739e !important;
+}
+
+:deep(.el-table .selected-row td) {
+  border-top: 1px solid #12739e !important;
+  /* border-right: 1px solid #12739E !important; */
+  border-bottom: 1px solid #12739e !important;
+}
+
+:deep(.el-table .selected-row td:last-child) {
+  border-right: 1px solid #12739e !important;
+}
 </style>

+ 20 - 7
src/components/layout/TopoButtonBar.vue

@@ -64,12 +64,12 @@
       </el-tab-pane>
     </el-tabs>
   </div>
+
 </template>
 
 <script setup>
 import { ref, computed } from "vue"
 import { useI18n } from "vue-i18n"
-const { t } = useI18n()
 
 import libraryIcon from "@/assets/icons/library.png" // 图层
 import addLayerIcon from "@/assets/icons/addLayer.png" // 新建图层
@@ -126,7 +126,7 @@ import InputReportIcon from "@/assets/icons/inputReport.png" // 输入报告
 import toolIcon from "@/assets/icons/tool.png" // 工具
 import visualizationIcon from "@/assets/icons/visualization.png" // 可视化
 import splitIcon from "@/assets/icons/split.png" // 切分
-
+const { t } = useI18n()
 const emit = defineEmits(["button-click"])
 const modeSwitch = ref(false) // 开关状态,默认关闭(连接模式)
 const fakeTabs = [
@@ -307,11 +307,7 @@ const tabs = [
     icon: "",
     buttons: [
       { action: "run", label: t("buttons.run"), icon: runIcon },
-      {
-        action: "preprocess",
-        label: t("buttons.preprocess"),
-        icon: preprocessingIcon
-      },
+      { action: "preprocess", label: t("buttons.preprocess"), icon: preprocessingIcon},
       { action: "stop", label: t("buttons.stop"), icon: stopIcon }
     ]
   },
@@ -386,6 +382,7 @@ const buttonStates = ref({
   showNodeId: false
 })
 
+
 const handleButtonClick = (button) => {
   if (button.isToggle) {
     buttonStates.value[button.action] = !buttonStates.value[button.action]
@@ -396,6 +393,21 @@ const handleButtonClick = (button) => {
   } else {
     emit("button-click", { action: button.action })
   }
+
+  switch (button.action) {
+    case "run":
+      console.log("点击了 运行 按钮")
+      break
+    case "stop":
+      console.log("点击了 停止 按钮")
+      break
+    case "preprocess":
+      
+      break
+    default:
+      break
+  }
+  
 }
 
 const handleTabClick = (tab) => {
@@ -406,6 +418,7 @@ const handleModeSwitch = (value) => {
   console.log(value ? "切换到选择模式" : "切换到连接模式")
   // 在这里添加切换模式的逻辑
 }
+
 </script>
 
 <style scoped>

+ 131 - 14
src/components/layout/home.vue

@@ -34,13 +34,77 @@
               </template>
             </el-tab-pane>
           </el-tabs>
-          <el-button class="add-project-btn" type="text" @click="addNewProject">
+          <el-button class="add-project-btn" type="text" @click="addProjectdialog = true">
             <el-icon><Plus /></el-icon>
           </el-button>
         </div>
       </div>
     </el-main>
   </div>
+    <el-dialog
+    v-model="addProjectdialog"
+    align-center
+    :append-to-body="true"
+    width="500"
+    class="dialog_class"
+    draggable
+  >
+    <template #header="{ titleId, titleClass }">
+      <div class="my-header">
+        <!-- <el-image :src="icon" fit="contain"></el-image> -->
+        <h4 :id="titleId" :class="titleClass">{{ $t("dialog.new") }}</h4>
+      </div>
+    </template>
+    <el-form
+      :model="newproject"
+      :rules="rules"
+      ref="projectForm"
+      :label-width="labelWidth"
+    >
+      <el-row :gutter="20">
+        <el-col :span="24">
+          <el-form-item :label="`${$t('project.name')}:`" prop="name">
+            <el-input
+              v-model="newproject.name"
+              class="w-50 m-2"
+              :placeholder="$t('project.inputProjectName')"
+              maxlength="100"
+            />
+          </el-form-item>
+        </el-col>   
+        <el-col :span="24">
+          <el-form-item :label="`${$t('project.keywords')}:`" prop="keywords">
+            <el-input
+              v-model="newproject.keywords"
+              class="w-50 m-2"
+              :placeholder="$t('project.inputProjectKeywords')"
+              maxlength="100"
+            />
+          </el-form-item>
+        </el-col>
+        <el-col :span="24">
+          <el-form-item :label="`${$t('project.description')}:`" prop="description">
+            <el-input
+              v-model="newproject.description"
+              class="w-50 m-2"
+              :placeholder="$t('project.inputProjectDescription')"
+              maxlength="500"
+            />
+          </el-form-item>
+        </el-col>
+      </el-row>
+    </el-form>
+    <template #footer>
+      <span class="lastbtn">
+        <el-button @click="addProjectdialog = false">{{
+          $t("dialog.cancel")
+        }}</el-button>
+        <el-button type="primary" @click="submitForm">
+          {{ $t("dialog.ok") }}
+        </el-button>
+      </span>
+    </template>
+  </el-dialog>
 </template>
 
 <script setup>
@@ -53,8 +117,8 @@ 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();
+import { request } from "@/utils/request"
+const { t, locale } = useI18n()
 const router = useRouter();
 const route = useRoute();
 const projectStore = useProjectStore();
@@ -64,6 +128,20 @@ const addButtonLeft = ref(70); // 默认位置
 const activeTab = ref(route.path.slice(1) || 'model');
 const activeProjectId = computed(() => projectStore.activeProjectId);
 
+// 控制添加项目对话框显示
+let addProjectdialog = ref(false)
+// 表单引用
+const projectForm = ref(null)
+
+let newproject = ref({
+  name: "",
+  description: "",
+  keywords: ""
+})
+
+const labelWidth = computed(() => {
+  return locale.value === "zh-CN" ? "80px" : "120px"
+})
 // 监听路由变化,同步 activeTab
 watch(
   () => route.path,
@@ -112,6 +190,30 @@ onMounted(() => {
   updateAddButtonPosition();
 });
 
+// 提交表单
+const submitForm = () => {
+  projectForm.value.validate((valid) => {
+    if (valid) {
+      addProject()
+    } else {
+      return false
+    }
+  })
+}
+
+// 表单校验规则
+const rules = ref({
+  name: [
+    { required: true, message: t('project.inputProjectName'), trigger: 'blur' }
+  ],
+  keywords: [
+    { required: false, message: t('project.inputProjectKeywords'), trigger: 'blur' }
+  ],
+  description: [
+    { required: false, message: t('project.inputProjectDescription'), trigger: 'blur' }
+  ]
+})
+
 const handleButtonClick = (action) => {
   switch (action) {
     case 'importProject':
@@ -139,17 +241,32 @@ const handleProjectTabRemove = (projectId) => {
   });
 };
 
-const addNewProject = () => {
-  const newProject = {
-    projectId: `proj-${Date.now()}`,
-    projectName: `Project ${projectStore.projects.length + 1}`,
-  };
-  projectStore.addProject(newProject);
-  ElMessage.success(t('message.projectAdded'));
-  nextTick(() => {
-    updateAddButtonPosition(); // 手动触发位置更新
-  });
-};
+// 添加项目
+const addProject = () => {
+  const params = {
+    transCode: "ES0002",
+    name: newproject.value.name,
+    remark: newproject.value.description,
+    keywords: newproject.value.keywords
+  }
+  request(params)
+    .then((res) => {
+        projectStore.addProject({
+          projectId: res.pid, // 使用后端返回的 pid
+          projectName: params.name,
+          keywords: params.keywords,
+          remark: params.remark,
+          flow: '{"nodes":[],"edges":[]}',
+        });
+      ElMessage.success("添加成功")
+      addProjectdialog.value = false
+      // 设置激活项目
+      projectStore.setActiveProject(res.pid);
+    })
+    .catch((err) => {
+      ElMessage.error(err.returnMsg)
+    })
+}
 </script>
 
 <style scoped>

+ 3 - 1
src/locales/en.json

@@ -135,6 +135,7 @@
   },
   "buttons" : {
     "exit": "Exit",
+    "open": "Open",
     "save": "Save",
     "undo": "Undo",
     "redo": "Redo",
@@ -206,7 +207,8 @@
     "visualization" : "Visualization",
     "split" : "Split",
     "selectMode" : "SelectModel",
-    "connectMode" : "ConnectModel"
+    "connectMode" : "ConnectModel",
+    "projectlist" : "ProjectList"
   },
     "tabs":{
     "edit":"Edit",

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

@@ -107,7 +107,8 @@
     "nickname": "昵称",
     "email": "邮箱",
     "regTime": "注册时间",
-    "mobileNo": "手机号码"
+    "mobileNo": "手机号码",
+    "projectlist": "项目列表"
   },
   "treeData":{
     "system": "系统",
@@ -135,6 +136,7 @@
   },
   "buttons" : {
     "exit": "退出",
+    "open": "打开",
     "save": "保存",
     "undo": "撤销",
     "redo": "重做",

+ 44 - 19
src/store/project.js

@@ -5,23 +5,39 @@ import { ElMessage } from 'element-plus';
 export const useProjectStore = defineStore(
   'project',
   () => {
-    const pid = ref('proj-default');
-    const runtype = ref('');
-    const projectInfo = ref(null);
+    const pid = ref('proj-default'); // 当前激活项目的 ID
+    const runtype = ref(''); // 运行类型
     const projects = ref([
       {
         projectId: 'proj-default',
         projectName: 'Default Project',
+        keywords: '',
+        remark: '',
+        flow: { "nodes": [], "edges": [] }, // 每个项目存储自己的 vueflow 数据
       },
-    ]); // 初始化时添加默认项目
-    const activeProjectId = ref('proj-default'); // 默认激活项目
+    ]); // 项目列表,每个项目包含 flow 数据
+    const activeProjectId = ref('proj-default'); // 当前激活的项目 ID
 
+    // 添加新项目
     const addProject = (project) => {
-      projects.value.push(project);
-      activeProjectId.value = project.projectId; // 激活新添加的项目
+      // 确保 projectId 唯一
+      if (projects.value.some((p) => p.projectId === project.projectId)) {
+        ElMessage.error('项目 ID 已存在!');
+        return;
+      }
+      console.log("Adding project: ", project);
+      projects.value.push({
+        projectId: project.projectId,
+        projectName: project.projectName || `Project ${projects.value.length + 1}`,
+        keywords: project.keywords || '',
+        remark: project.remark || '',
+        flow: project.flow || { "nodes": [], "edges": [] }, // 初始化空的 flow 数据
+      });
+      activeProjectId.value = project.projectId; // 激活新项目
       pid.value = project.projectId;
     };
 
+    // 删除项目
     const removeProject = (projectId) => {
       if (projects.value.length <= 1) {
         ElMessage.warning('至少保留一个项目!');
@@ -34,65 +50,74 @@ export const useProjectStore = defineStore(
       }
     };
 
+    // 设置激活项目
     const setActiveProject = (projectId) => {
       if (projects.value.some((p) => p.projectId === projectId)) {
         activeProjectId.value = projectId;
         pid.value = projectId;
+      } else {
+        ElMessage.error('项目不存在!');
       }
     };
 
+    // 获取当前激活项目
     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 updateProjectInfo = (projectId, info) => {
+      const project = projects.value.find((p) => p.projectId === projectId);
+      if (project) {
+        project.projectName = info.name || project.projectName;
+        project.keywords = info.keywords || project.keywords;
+        project.remark = info.remark || project.remark;
+        project.flow = info.flow || project.flow;
       }
     };
 
+    // 设置 pid
     const setpid = (val) => {
       pid.value = val;
     };
 
+    // 设置 runtype
     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',
+          keywords: '',
+          remark: '',
+          flow: { "nodes": [], "edges": [] },
         },
-      ]; // 重置时保留默认项目
+      ];
       activeProjectId.value = 'proj-default';
     };
 
     return {
       pid,
       runtype,
-      projectInfo,
       projects,
       activeProjectId,
       setpid,
       setruntype,
-      setProjectInfo,
       addProject,
       removeProject,
       setActiveProject,
       getActiveProject,
+      updateProjectInfo,
       clearproject,
     };
   },
   {
-    persist: true,
+    persist: true, // 持久化存储
   }
 );

+ 0 - 17
src/views/model/index.vue

@@ -1,22 +1,5 @@
 <template>
   <el-container>
-    <!-- <el-header class="custom-header">
-      <el-space :size="spacesize" class="spaceclass">
-        <template v-for="(item, index) in headerbuttons" :key="index">
-          <el-button
-            v-if="item.type === 'button'"
-            class="custom-icon-button"
-            @click="btnfunc(item.name)"
-          >
-            <img :src="getImgPath(item.img)" style="width: 24px" />
-          </el-button>
-          <el-divider
-            v-else-if="item.type === 'divider'"
-            direction="vertical"
-          ></el-divider>
-        </template>
-      </el-space>
-    </el-header> -->
     <splitpanes class="default-theme diysplitpanes">
       <pane min-size="10" size="20" max-size="50" class="custom-aside">
         <el-tabs

+ 135 - 123
src/views/model/vueflow/index.vue

@@ -167,6 +167,19 @@ const debouncedSave = debounce(() => {
   saveproject()
 }, 500) // 500ms 防抖
 
+onMounted(() => {
+  flowInit()
+  // 点击其他区域取消线段选中
+  if (vueFlowRef.value) {
+    vueFlowRef.value.$el.addEventListener("click", (event) => {
+      // 确保点击的不是边缘
+      if (seledge.value && !event.target.closest(".vue-flow__edge")) {
+        cleanEdgeselect()
+      }
+    })
+  }
+})
+
 
 // 处理节点拖动时的网格吸附
 onNodeDrag(({ node }) => {
@@ -186,82 +199,97 @@ onNodeDrag(({ node }) => {
 let prevNodes = [...nodes.value]
 let prevEdges = [...edges.value]
 
+// 标记是否为初始化
+let isInitializing = ref(true);
+
 // 监听节点变化
 watch(
   nodes,
   (newNodes) => {
-    const deletedNodes = prevNodes.filter(
+    console.log("节点变化:", newNodes);
+    
+    if (isInitializing.value) {
+      prevNodes.value = [...newNodes];
+      isInitializing.value = false;
+      return;
+    }
+
+    const deletedNodes = prevNodes.value.filter(
       (node) => !newNodes.some((n) => n.id === node.id)
-    )
+    );
     const addedNodes = newNodes.filter(
-      (node) => !prevNodes.some((n) => n.id === node.id)
-    )
+      (node) => !prevNodes.value.some((n) => n.id === node.id)
+    );
 
     if (deletedNodes.length > 0) {
-      console.log("Deleted Nodes:", deletedNodes)
+      console.log('Deleted Nodes:', deletedNodes);
       deletedNodes.forEach((node) => {
         if (node.data?.pcId) {
-          comdelete(node.data.pcId)
+          comdelete(node.data.pcId);
         }
-      })
+      });
     }
     if (addedNodes.length > 0) {
-      // console.log('Added Nodes:', addedNodes);
+      console.log('Added Nodes:', addedNodes);
     }
 
-    // 更新快照
-    prevNodes = [...newNodes]
-
-    debouncedSave();
+    prevNodes.value = [...newNodes];
   },
   { deep: true }
-)
+);
 
 // 监听连线变化
 watch(
   edges,
   (newEdges) => {
-    const deletedEdges = prevEdges.filter(
+    if (isInitializing.value) {
+      console.log("初始化连线数据:", newEdges);
+      
+      prevEdges.value = [...newEdges];
+      isInitializing.value = false;
+      return;
+    }
+
+    const deletedEdges = prevEdges.value.filter(
       (edge) => !newEdges.some((e) => e.id === edge.id)
-    )
+    );
     const addedEdges = newEdges.filter(
-      (edge) => !prevEdges.some((e) => e.id === edge.id)
-    )
+      (edge) => !prevEdges.value.some((e) => e.id === edge.id)
+    );
 
     if (deletedEdges.length > 0) {
-      console.log("Deleted Edges:", deletedEdges)
+      console.log('Deleted Edges:', deletedEdges);
       deletedEdges.forEach((edge) => {
-        const sourceNode = prevNodes.find((n) => n.id === edge.source)
-        const targetNode = prevNodes.find((n) => n.id === edge.target)
+        const sourceNode = prevNodes.value.find((n) => n.id === edge.source);
+        const targetNode = prevNodes.value.find((n) => n.id === edge.target);
 
-        if (!sourceNode || !targetNode) return
+        if (!sourceNode || !targetNode) return;
 
-        let npcId = ""
-        let pcId = ""
+        let npcId = '';
+        let pcId = '';
 
-        // 判断 source 或 target 是否为中间点 comId === '3'
-        if (sourceNode.data?.comId === "3") {
-          npcId = sourceNode.data.pcId
-          pcId = targetNode.data?.pcId
-        } else if (targetNode.data?.comId === "3") {
-          npcId = targetNode.data.pcId
-          pcId = sourceNode.data?.pcId
+        if (sourceNode.data?.comId === '3') {
+          npcId = sourceNode.data.pcId;
+          pcId = targetNode.data?.pcId;
+        } else if (targetNode.data?.comId === '3') {
+          npcId = targetNode.data.pcId;
+          pcId = sourceNode.data?.pcId;
         }
 
         if (npcId && pcId) {
-          comlinedelete(npcId, pcId)
+          comlinedelete(npcId, pcId);
         }
-      })
+      });
     }
     if (addedEdges.length > 0) {
+      console.log('Added Edges:', addedEdges);
     }
 
-    // 更新快照
-    prevEdges = [...newEdges]
+    prevEdges.value = [...newEdges];
     debouncedSave();
   },
   { deep: true }
-)
+);
 
 const resetTransform = () => {
   setViewport({ x: 0, y: 0, zoom: 1 })
@@ -278,41 +306,48 @@ const toggleDarkMode = () => {
 
 const saveproject = async () => {
   try {
-    let obj = {
+    const obj = {
       nodes: toObject().nodes,
-      edges: toObject().edges
-    }
-    // 移除每个节点的 image 字段,保留 imageIdentify
-    obj.nodes = obj.nodes.map(node => ({
+      edges: toObject().edges,
+    };
+    obj.nodes = obj.nodes.map((node) => ({
       ...node,
       data: {
         ...node.data,
-        image: undefined // 移除 image 字段
-      }
-    }))
-    
-    mergedObj.value = JSON.stringify(obj)
+        image: undefined, // 移除 image 字段
+      },
+    }));
+
+    mergedObj.value = JSON.stringify(obj);
+
+    const activeProject = projectStore.getActiveProject();
+    if (!activeProject) {
+      throw new Error('当前没有激活的项目');
+    }
 
     const params = {
-      transCode: "ES0002",
+      transCode: 'ES0002',
       pid: pid.value,
       flow: mergedObj.value,
-      name: projectStore.projectInfo.name || "",
-      remark: projectStore.projectInfo.remark || "",
-      keywords: projectStore.projectInfo.keywords || ""
-    }
-    
-    await request(params)
-    projectStore.setProjectInfo({
-      ...projectStore.projectInfo,
-      flow: mergedObj.value || ""
-    })
-    console.log('保存成功')
+      name: activeProject.projectName || '',
+      remark: activeProject.remark || '',
+      keywords: activeProject.keywords || '',
+    };
+
+    await request(params);
+
+    projectStore.updateProjectInfo(projectStore.activeProjectId, {
+      ...activeProject,
+      flow: mergedObj.value,
+    });
+
+    console.log('保存成功');
+    ElMessage.success('保存成功');
   } catch (error) {
-    console.error("保存失败:", error)
-    ElMessage.error("保存失败,请稍后重试")
+    console.error('保存失败:', error);
+    ElMessage.error('保存失败,请稍后重试');
   }
-}
+};
 
 const removeNode = () => {
   if (!noid.value) {
@@ -756,59 +791,69 @@ const createCom3 = async (pid) => {
   }
 }
 
+// 在 flowInit 后重置 isInitializing
 const flowInit = async () => {
   try {
-    const flow = JSON.parse(projectStore.projectInfo.flow || '{"nodes":[],"edges":[]}')
-    if (!flow.nodes || !flow.edges) {
-      throw new Error("无效的 flow 数据")
+    const activeProject = projectStore.getActiveProject();
+    console.log("Active Project:", activeProject);
+    
+    if (!activeProject) {
+      throw new Error('当前没有激活的项目');
     }
 
+    let flow;
+    try {
+      console.log('解析 flow 数据:', activeProject.flow);
+      console.log('解析 flow 数据aaaaa:', typeof activeProject.flow);
+      console.log('Raw flow data:', JSON.parse(activeProject.flow));
+      
+      
+      flow = JSON.parse(activeProject.flow || '{"nodes":[], "edges":[] }' );
+    } catch (error) {
+      console.error('解析 flow 数据失败:', activeProject.flow, error);
+      flow = JSON.parse('{"nodes": [], "edges": [] }');
+    }
+    
     const updatedNodes = await Promise.all(
       flow.nodes.map(async (node) => {
         if (node.data?.imageIdentify) {
-          if (node.data.imageIdentify === "point-only") {
-            // point-only 节点使用本地图片
+          if (node.data.imageIdentify === 'point-only') {
             return {
               ...node,
-              data: {
-                ...node.data,
-                image: nodePoint
-              }
-            }
+              data: { ...node.data, image: nodePoint },
+            };
           }
-          // 普通节点调用接口获取图片
           try {
-            const imageData = await getImageBase64(node.data.imageIdentify)
+            const imageData = await getImageBase64(node.data.imageIdentify);
             return {
               ...node,
-              data: {
-                ...node.data,
-                image: imageData || tempic
-              }
-            }
+              data: { ...node.data, image: imageData || tempic },
+            };
           } catch (err) {
-            console.error(`获取图片 ${node.data.imageIdentify} 失败:`, err.message)
+            console.error(`获取图片 ${node.data.imageIdentify} 失败:`, err.message);
             return {
               ...node,
-              data: {
-                ...node.data,
-                image: tempic
-              }
-            }
+              data: { ...node.data, image: tempic },
+            };
           }
         }
-        return node // 如果没有 imageIdentify,直接返回
+        return node;
       })
-    )
+    );
 
-    nodes.value = updatedNodes
-    edges.value = flow.edges
+    nodes.value = updatedNodes;
+    edges.value = flow.edges;
+    prevNodes.value = [...updatedNodes];
+    prevEdges.value = [...flow.edges];
+    isInitializing.value = true; // 标记为初始化
 
-    console.log("加载项目成功")
+    console.log('加载项目成功');
   } catch (error) {
-    console.error("加载项目失败:", error)
+    console.error('加载项目失败:', error);
+    ElMessage.error('加载项目失败,请检查项目数据');
   }
-}
+};
+
 const cleanEdgeselect = () => {
   if (seledge.value) {
     // 恢复选中边缘的原始样式
@@ -825,19 +870,6 @@ const cleanEdgeselect = () => {
   }
 }
 
-onMounted(() => {
-  flowInit()
-
-  // 点击其他区域取消线段选中
-  if (vueFlowRef.value) {
-    vueFlowRef.value.$el.addEventListener("click", (event) => {
-      // 确保点击的不是边缘
-      if (seledge.value && !event.target.closest(".vue-flow__edge")) {
-        cleanEdgeselect()
-      }
-    })
-  }
-})
 
 const closePanel = () => {
   setTimeout(() => {
@@ -845,26 +877,6 @@ const closePanel = () => {
   }, 100) // 延迟 100ms 销毁组件,避免 Splitpanes 销毁错误
 }
 
-// 路由切换前保存
-// router.beforeEach(async (to, from, next) => {
-//   if (to.path !== from.path) {
-//     try {
-//       await saveproject() // 直接调用 saveproject
-//       next()
-//     } catch (error) {
-//       console.error("路由切换保存失败:", error)
-//       next() // 即使保存失败也允许切换路由
-//     }
-//   } else {
-//     next()
-//   }
-// })
-
-// // 组件卸载前保存
-// onBeforeUnmount(async () => {
-//   await saveproject() // 直接调用 saveproject
-// })
-
 defineExpose({
   resetTransform,
   toggleDarkMode,

+ 53 - 35
src/views/project/index.vue

@@ -308,6 +308,7 @@ const submitForm = () => {
   })
 }
 
+// 添加项目
 const addProject = () => {
   const params = {
     transCode: "ES0002",
@@ -317,7 +318,13 @@ const addProject = () => {
   }
   request(params)
     .then((res) => {
-      console.log(res)
+        projectStore.addProject({
+          projectId: res.pid, // 使用后端返回的 pid
+          projectName: params.name,
+          keywords: params.keywords,
+          remark: params.remark,
+          flow: {"nodes":[],"edges":[]},
+        });
       ElMessage.success("添加成功")
       addProjectdialog.value = false
       getprojectlist()
@@ -357,59 +364,70 @@ const selectAll = () => {
 // 删除选中行
 const deleteSelected = () => {
   ElMessageBox.confirm(
-    t("message.confirmDelete"), // "确定要删除选中的项目吗?"
-    t("common.tip"), // "提示"
+    t('message.confirmDelete'),
+    t('common.tip'),
     {
-      confirmButtonText: t("common.ok"), // "确定"
-      cancelButtonText: t("common.cancel"), // "取消"
-      type: "warning"
+      confirmButtonText: t('common.ok'),
+      cancelButtonText: t('common.cancel'),
+      type: 'warning',
     }
   )
     .then(() => {
-      const selectedIds = selectedRows.value.map((row) => row.pid)
-      // console.log('删除的项目ID:', selectedIds)
+      const selectedIds = selectedRows.value.map((row) => row.pid);
       const params = {
-        transCode: "ES0003",
-        pid: selectedIds.join(",")
-      }
+        transCode: 'ES0003',
+        pid: selectedIds.join(','),
+      };
 
       request(params)
         .then((res) => {
-          ElMessage.success(t("message.deleteSuccess")) // "删除成功"
-          selectedRows.value = []
-          getprojectlist()
+          ElMessage.success(t('message.deleteSuccess'));
+          // 从 Store 中移除已删除的项目
+          selectedIds.forEach((pid) => {
+            projectStore.removeProject(pid);
+          });
+          selectedRows.value = [];
+          getprojectlist();
         })
         .catch((err) => {
-          ElMessage.error(err.returnMsg || t("message.deleteFailed")) // "删除失败"
-        })
+          ElMessage.error(err.returnMsg || t('message.deleteFailed'));
+        });
     })
     .catch(() => {
-      ElMessage.info(t("message.deleteCancelled")) // "已取消删除"
-    })
-}
+      ElMessage.info(t('message.deleteCancelled'));
+    });
+};
 
 // 确认选中行
 const confirmSelected = () => {
   if (selectedRows.value.length !== 1) {
-    ElMessage.warning(t("message.oneSelection")) // "请选择一个项目"
-    return
+    ElMessage.warning('请选择一个项目!');
+    return;
   }
 
-  // 获取第一个选中项的pid(或根据需要处理多个选中项)
-  const pid = selectedRows.value[0].pid
-  // console.log('选中的项目:', selectedRows.value[0])
-  projectStore.setpid(pid)
-  projectStore.setProjectInfo({
-    name: selectedRows.value[0].name,
-    keywords: selectedRows.value[0].keywords,
-    remark: selectedRows.value[0].remark,
-    flow: selectedRows.value[0].flow || ""
-  })
+  const selected = selectedRows.value[0];
+  
+  const project = {
+    projectId: selected.pid,
+    projectName: selected.name || `Project ${projectStore.projects.length + 1}`,
+    keywords: selected.keywords || '',
+    remark: selected.remark || '',
+    flow: selected.flow ? selected.flow : { "nodes": [], "edges": [] },
+  };
+
+  // 如果项目已存在,更新信息;否则添加新项目
+  if (projectStore.projects.some((p) => p.projectId === selected.pid)) {
+    projectStore.updateProjectInfo(selected.pid, project);
+  } else {
+    projectStore.addProject(project);
+  }
 
-  router.push({
-    path: "/home"
-  })
-}
+  // 设置激活项目
+  projectStore.setActiveProject(selected.pid);
+
+  // 跳转到首页
+  router.push({ path: '/home' });
+};
 
 onMounted(() => {
   getprojectlist()