Procházet zdrojové kódy

feat: 添加打开项目、保存项目

jiaxing.liao před 1 měsícem
rodič
revize
1f865c6076

+ 18 - 1
src/renderer/src/locales/en_US.json

@@ -60,5 +60,22 @@
   "selectBoard": "Please Select Board!",
   "modifyTime": "Modify Time",
   "noFoundProject": "This Project Not Found!",
-  "readProjectError": "Read Project File Error!"
+  "readProjectError": "Read Project File Error!",
+  "project": "Project",
+  "edit": "Edit",
+  "Tools": "Tools",
+  "help": "Help",
+  "openProject": "Open Project",
+  "save": "Save",
+  "generateCode": "Generate Code",
+  "compile": "Compile",
+  "run": "Run",
+  "preview": "Preview",
+  "connect": "Connect",
+  "chooseProject": "Choose Project",
+  "open": "Open",
+  "openingProject": "open project...",
+  "openProjectSuccess": "open project success",
+  "projectNotExist": "Project Not Exist",
+  "saveSuccess": "Save Success"
 }

+ 18 - 1
src/renderer/src/locales/zh_CN.json

@@ -60,5 +60,22 @@
   "selectBoard": "请选择板卡",
   "modifyTime": "修改时间:",
   "noFoundProject": "该项目不存在, 无法打开!",
-  "readProjectError": "读取项目失败"
+  "readProjectError": "读取项目失败",
+  "project": "项目",
+  "edit": "编辑",
+  "Tools": "工具",
+  "help": "帮助",
+  "openProject": "打开项目",
+  "save": "保存",
+  "generateCode": "生成代码",
+  "compile": "编译",
+  "run": "运行",
+  "preview": "预览",
+  "connect": "连接",
+  "chooseProject": "选择项目",
+  "open": "打开",
+  "openingProject": "正在打开项目...",
+  "openProjectSuccess": "打开项目成功",
+  "projectNotExist": "项目不存在",
+  "saveSuccess": "保存成功"
 }

+ 81 - 4
src/renderer/src/store/modules/project.ts

@@ -17,6 +17,8 @@ import { useDebouncedRefHistory } from '@vueuse/core'
 import { useRecentProject } from './recentProject'
 import { v4 } from 'uuid'
 import dayjs from 'dayjs'
+import { ElMessage } from 'element-plus'
+import { useI18n } from 'vue-i18n'
 
 export interface IProject {
   version: string
@@ -48,6 +50,7 @@ export const useProjectStore = defineStore('project', () => {
   })
 
   const recentProjectStore = useRecentProject()
+  const { t } = useI18n()
 
   // 项目路径
   const projectPath = ref<string>()
@@ -68,6 +71,8 @@ export const useProjectStore = defineStore('project', () => {
    */
   const createApp = async (meta: AppMeta) => {
     meta = klona(meta)
+    meta.createTime = dayjs().format('YYYY-MM-DD HH:mm:ss')
+    meta.modifyTime = dayjs().format('YYYY-MM-DD HH:mm:ss')
     // 1、应用元信息
     project.value = {
       version: '1.0.0',
@@ -139,8 +144,8 @@ export const useProjectStore = defineStore('project', () => {
       id: v4(),
       projectName: meta.name,
       projectPath: `${meta.path}\\${meta.name}`,
-      createTime: dayjs().format('YYYY-MM-DD HH:mm:ss'),
-      modifyTime: dayjs().format('YYYY-MM-DD HH:mm:ss')
+      createTime: meta.createTime,
+      modifyTime: meta.modifyTime
     })
   }
 
@@ -148,13 +153,44 @@ export const useProjectStore = defineStore('project', () => {
    * 加载项目
    * @param newProject 项目字符串
    */
-  const loadProject = (newProject: IProject) => {
+  const loadProject = (newProject: IProject, path: string) => {
     project.value = newProject
+    // 修改项目路径
+    if (project.value.meta.path !== path) {
+      project.value.meta.path = path
+    }
     // 初始化处理
     clear()
-    projectPath.value = `${newProject.meta.path}/${newProject.meta.name}`
+    projectPath.value = path
     activePageId.value = newProject.screens[0].pages?.[0]?.id
     imageCompressFormat.value = newProject.meta.imageCompress
+    recentProjectStore.addProject({
+      id: v4(),
+      projectName: newProject.meta.name,
+      projectPath: path,
+      createTime: newProject.meta.createTime,
+      modifyTime: newProject.meta.modifyTime
+    })
+  }
+
+  // 保存当前项目
+  const saveProject = async () => {
+    if (!projectPath.value) {
+      ElMessage.error(t('projectNotExist'))
+      return
+    }
+    // 1、保存项目
+    await window.electron.ipcRenderer.invoke(
+      'write-file',
+      `${projectPath.value}\\project.ui`,
+      JSON.stringify(project.value)
+    )
+    // 2、更新修改时间
+    recentProjectStore.updateProject({
+      ...recentProjectStore.currentRecord!,
+      modifyTime: dayjs().format('YYYY-MM-DD HH:mm:ss')
+    })
+    ElMessage.success(t('saveSuccess'))
   }
 
   // 删除页面
@@ -174,6 +210,45 @@ export const useProjectStore = defineStore('project', () => {
     })
   }
 
+  // 打开本地项目
+  const openLocalProject = async () => {
+    const paths = await window.electron.ipcRenderer.invoke('get-file', {
+      title: t('chooseProject'),
+      buttonLabel: t('open'),
+      properties: ['openFile'],
+      filters: [
+        {
+          name: 'Project',
+          extensions: ['ui']
+        }
+      ]
+    })
+    const path = paths?.[0]
+    if (!path) return
+    // 存在 打开项目
+    let msg
+    try {
+      msg = ElMessage.info({
+        message: t('openingProject'),
+        duration: 0,
+        plain: true
+      })
+      const result = await window.electron.ipcRenderer.invoke('read-file', path, 'utf-8')
+      if (result) {
+        loadProject(JSON.parse(result), path.slice(0, path.lastIndexOf('\\') + 1))
+        ElMessage.success({
+          message: t('openProjectSuccess'),
+          plain: true
+        })
+      } else {
+        // 读取失败 删除项目
+        ElMessage.error(t('readProjectError'))
+      }
+    } finally {
+      msg?.close()
+    }
+  }
+
   return {
     createApp,
     project,
@@ -184,6 +259,8 @@ export const useProjectStore = defineStore('project', () => {
     projectPath,
     imageCompressFormat,
     loadProject,
+    openLocalProject,
+    saveProject,
 
     // 历史记录
     history,

+ 12 - 4
src/renderer/src/store/modules/recentProject.ts

@@ -28,7 +28,6 @@ export const useRecentProject = defineStore('recentProject', () => {
   const getAllProjects = async () => {
     const db = await getDB()
     const projects = await db.getAll('projects')
-    console.log('projects', projects)
     recentProjects.value = projects
   }
 
@@ -36,9 +35,18 @@ export const useRecentProject = defineStore('recentProject', () => {
   const addProject = async (project: ProjectRecord) => {
     currentRecord.value = project
     const db = await getDB()
-    const result = await db.add('projects', project)
-    getAllProjects()
-    return result
+    // 1、查找是否是存在的项目
+    const existProject = recentProjects.value?.find(
+      (item) => item.projectName === project.projectName && item.projectPath === project.projectPath
+    )
+    if (existProject) {
+      currentRecord.value.id = existProject.id
+      return
+    } else {
+      const result = await db.add('projects', project)
+      getAllProjects()
+      return result
+    }
   }
 
   // 删除项目

+ 1 - 1
src/renderer/src/views/designer/modals/projectModal/Recent.vue

@@ -51,7 +51,7 @@ const openProject = async (project) => {
     // 存在 打开项目
     const result = await window.electron.ipcRenderer.invoke('read-file', projectPath, 'utf-8')
     if (result) {
-      projectStore.loadProject(JSON.parse(result))
+      projectStore.loadProject(JSON.parse(result), project.projectPath)
       emit('opened')
     } else {
       // 读取失败 删除项目

+ 1 - 2
src/renderer/src/views/designer/tools/Operate.vue

@@ -6,7 +6,7 @@
 </template>
 
 <script setup lang="ts">
-import { inject, computed } from 'vue'
+import { computed } from 'vue'
 import {
   LuUndo,
   LuRedo,
@@ -28,7 +28,6 @@ import { useProjectStore } from '@/store/modules/project'
 
 import type { MenuItemType } from './components/MenuItem.vue'
 
-const onMenuClick = inject<(menuKey: string) => void>('onMenuClick', () => {})
 const projectStore = useProjectStore()
 
 const projectMenu = computed((): MenuItemType[] => {

+ 0 - 1
src/renderer/src/views/designer/tools/components/MenuItem.vue

@@ -20,7 +20,6 @@
 <script setup lang="ts">
 import type { VNode } from 'vue'
 import { IconType } from 'vue-icons-plus'
-import { defineEmits, defineProps } from 'vue'
 
 export type MenuItemType = {
   type?: string

+ 4 - 4
src/renderer/src/views/designer/tools/index.vue

@@ -1,21 +1,21 @@
 <template>
   <el-tabs v-model="activeMenu" type="border-card">
-    <el-tab-pane label="项目" name="project">
+    <el-tab-pane :label="$t('project')" name="project">
       <div class="h-50px flex items-center">
         <Project />
       </div>
     </el-tab-pane>
-    <el-tab-pane label="编辑" name="page">
+    <el-tab-pane :label="$t('edit')" name="page">
       <div class="h-50px flex items-center">
         <Opreate />
       </div>
     </el-tab-pane>
-    <el-tab-pane label="工具" name="tool">
+    <el-tab-pane :label="$t('Tools')" name="tool">
       <div class="h-50px flex items-center">
         <Custom />
       </div>
     </el-tab-pane>
-    <el-tab-pane label="帮助" name="help">
+    <el-tab-pane :label="$t('help')" name="help">
       <div class="h-50px flex items-center">
         <Help />
       </div>

+ 65 - 63
src/renderer/src/views/designer/tools/project.vue

@@ -1,25 +1,14 @@
 <template>
-  <div
+  <MenuItem
     v-for="item in projectMenu"
     :key="item.label"
-    class="flex flex-col justify-center items-center w-50px h-50px cursor-pointer mr-10px hover:bg-bg-tertiary rounded-4px"
-    @click="handleClick(item.key)"
-  >
-    <div class="w-30px h-30px flex items-center justify-center">
-      <img
-        v-if="typeof item.img === 'string'"
-        :src="item.img"
-        class="w-24px h-24px"
-        :alt="item.label"
-      />
-      <component v-else :is="item.img" size="20px"></component>
-    </div>
-    <div class="text-12px">{{ item.label }}</div>
-  </div>
+    :item="item"
+    @click="item?.onClick ? item.onClick() : handleClick(item.key)"
+  />
 </template>
 
 <script setup lang="ts">
-import { reactive, inject } from 'vue'
+import { inject, computed } from 'vue'
 import {
   LuSave,
   LuSquareCode,
@@ -29,56 +18,69 @@ import {
   LuHardDriveDownload,
   LuUnplug
 } from 'vue-icons-plus/lu'
+import MenuItem from './components/MenuItem.vue'
+import { useAppStore } from '@/store/modules/app'
+import { useProjectStore } from '@/store/modules/project'
+import { useI18n } from 'vue-i18n'
 
 const onMenuClick = inject<(menuKey: string) => void>('onMenuClick', () => {})
+const appStore = useAppStore()
+const projectStore = useProjectStore()
+const { t } = useI18n()
 
-const projectMenu = reactive([
-  {
-    key: 'new',
-    label: '新建项目',
-    img: new URL('@/assets/project-add.svg', import.meta.url).href
-  },
-  {
-    key: 'open',
-    label: '打开项目',
-    img: new URL('@/assets/open-project.svg', import.meta.url).href
-  },
-  {
-    key: 'save',
-    label: '保存',
-    img: LuSave
-  },
-  {
-    key: 'generate',
-    label: '生成代码',
-    img: LuSquareCode
-  },
-  {
-    key: 'compile',
-    label: '编译',
-    img: LuTerminalSquare
-  },
-  {
-    key: 'run',
-    label: '运行',
-    img: LuPlay
-  },
-  {
-    key: 'preview',
-    label: '预览',
-    img: LuPlaySquare
-  },
-  {
-    key: 'connect',
-    label: '连接',
-    img: LuUnplug
-  },
-  {
-    key: 'download',
-    label: '下载',
-    img: LuHardDriveDownload
-  }
-])
+const projectMenu = computed(() => {
+  return (
+    appStore.lang && [
+      {
+        key: 'new',
+        label: t('createProject'),
+        img: new URL('@/assets/project-add.svg', import.meta.url).href
+      },
+      {
+        key: 'open',
+        label: t('openProject'),
+        img: new URL('@/assets/open-project.svg', import.meta.url).href,
+        onClick: projectStore.openLocalProject
+      },
+      {
+        key: 'save',
+        label: t('save'),
+        img: LuSave,
+        onClick: projectStore.saveProject
+      },
+      {
+        key: 'generate',
+        label: t('generateCode'),
+        img: LuSquareCode
+      },
+      {
+        key: 'compile',
+        label: t('compile'),
+        img: LuTerminalSquare
+      },
+      {
+        key: 'run',
+        label: t('run'),
+        img: LuPlay
+      },
+      {
+        key: 'preview',
+        label: t('preview'),
+        img: LuPlaySquare
+      },
+      {
+        key: 'connect',
+        label: t('connect'),
+        img: LuUnplug
+      },
+      {
+        key: 'download',
+        label: t('download'),
+        img: LuHardDriveDownload
+      }
+    ]
+  )
+})
 
 const handleClick = (menuKey: string) => {
   onMenuClick?.(menuKey)