Forráskód Böngészése

feat: 添加项目创建弹窗

jiaxing.liao 3 hete
szülő
commit
b6c4faa7c0

+ 2 - 2
project.json5

@@ -26,12 +26,12 @@
       "model": "ESP32",
       // 闪存大小
       "flash_size": {
-        "capcity": "16",
+        "capacity": "16",
         "unit": "M"
       },
       // 内存大小
       "ram_size": {
-        "capcity": "4",
+        "capacity": "4",
         "unit": "M"
       }
     },

+ 25 - 0
src/main/files.ts

@@ -0,0 +1,25 @@
+import { dialog } from 'electron'
+
+/**
+ * 打开文件夹
+ */
+export const openDirectory = async () => {
+  const { canceled, filePaths } = await dialog.showOpenDialog({
+    properties: ['openDirectory']
+  })
+
+  if (!canceled) {
+    return filePaths[0]
+  }
+}
+
+/**
+ * 打开文件
+ */
+export const openFile = async () => {
+  const { canceled, filePaths } = await dialog.showOpenDialog({})
+
+  if (!canceled) {
+    return filePaths[0]
+  }
+}

+ 5 - 0
src/main/index.ts

@@ -2,6 +2,7 @@ import { app, shell, BrowserWindow, ipcMain } from 'electron'
 import { join } from 'path'
 import { electronApp, optimizer, is } from '@electron-toolkit/utils'
 import icon from '../../resources/icon.png?asset'
+import { openDirectory, openFile } from './files'
 
 const net = require('net')
 
@@ -84,6 +85,10 @@ app.whenReady().then(() => {
 
   // IPC test
   ipcMain.on('ping', () => console.log('pong'))
+  // Get Directory
+  ipcMain.handle('get-directory', openDirectory)
+  // Get File
+  ipcMain.handle('get-file', openFile)
 
   createWindow()
 

+ 12 - 0
src/renderer/components.d.ts

@@ -15,13 +15,25 @@ declare module 'vue' {
     ElAutocomplete: typeof import('element-plus/es')['ElAutocomplete']
     ElAutoComplete: typeof import('element-plus/es')['ElAutoComplete']
     ElButton: typeof import('element-plus/es')['ElButton']
+    ElCol: typeof import('element-plus/es')['ElCol']
     ElCollapse: typeof import('element-plus/es')['ElCollapse']
     ElCollapseItem: typeof import('element-plus/es')['ElCollapseItem']
     ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider']
     ElContainer: typeof import('element-plus/es')['ElContainer']
+    ElDialog: typeof import('element-plus/es')['ElDialog']
+    ElDivider: typeof import('element-plus/es')['ElDivider']
     ElFlex: typeof import('element-plus/es')['ElFlex']
+    ElForm: typeof import('element-plus/es')['ElForm']
+    ElFormItem: typeof import('element-plus/es')['ElFormItem']
+    ElFromItem: typeof import('element-plus/es')['ElFromItem']
     ElHeader: typeof import('element-plus/es')['ElHeader']
+    ElInput: typeof import('element-plus/es')['ElInput']
     ElMain: typeof import('element-plus/es')['ElMain']
+    ElOption: typeof import('element-plus/es')['ElOption']
+    ElRadioButton: typeof import('element-plus/es')['ElRadioButton']
+    ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
+    ElRow: typeof import('element-plus/es')['ElRow']
+    ElSelect: typeof import('element-plus/es')['ElSelect']
     ElSlider: typeof import('element-plus/es')['ElSlider']
     ElSpace: typeof import('element-plus/es')['ElSpace']
     ElSplitter: typeof import('element-plus/es')['ElSplitter']

+ 24 - 0
src/renderer/src/config/board_card_config.json

@@ -4,6 +4,13 @@
       "board_name": "DC80128M070",
       "description":" screen_resolution: 1920*720\n screen_size: 11.3英寸\n",
       "pic_path":"./images/project.jpeg",
+      "screen_mode": "single",
+      "resolutions": "800x600",
+      "color_format": "RGB",
+      "color_depth": {
+        "options": ["16bit", "24bit"],
+        "default": "24bit"
+      },      
       "image_compression": {
         "RLE": true,
         "QOI": true,
@@ -15,6 +22,23 @@
       "board_name": "DC80128M080",
       "description":" screen_resolution: 1920*720\n screen_size: 11.3英寸\n",
       "pic_path":"./images/project1.jpeg",
+      "screen_mode": "dual",
+      "screen0": {
+        "resolutions": "1920x720",
+        "color_format": "BGR",
+        "color_depth": {
+          "options": ["16bit", "24bit", "32bit"],
+          "default": "32bit"
+        }
+      },
+      "screen1": {
+        "resolutions": "800x600",
+        "color_format": "BGR",
+        "color_depth": {
+          "options": ["16bit", "24bit", "32bit"],
+          "default": "32bit"
+        }
+      },
       "image_compression": {
         "RLE": true,
         "QOI": true,

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

@@ -1,7 +1,18 @@
+import type { AppMeta } from '@/types/appMeta'
+import { ref } from 'vue'
 import { defineStore } from 'pinia'
 
-export const useProjectStore = defineStore('project', {
-  state: () => ({
-    project: {}
-  })
+export const useProjectStore = defineStore('project', () => {
+  // 应用元信息
+  const appMeta = ref<AppMeta>()
+
+  // 创建应用
+  const createApp = (meta: AppMeta) => {
+    appMeta.value = meta
+  }
+
+  return {
+    appMeta,
+    createApp
+  }
 })

+ 1 - 1
src/renderer/src/theme/vars.css

@@ -17,7 +17,7 @@
   --button-bg: #2d2d2d; /* 按钮背景 */
 
   /* VS Code 风格对 Element Plus 组件的适配 - 主要色系改为白色 */
-  --el-color-primary: #ffffff !important;
+  /* --el-color-primary: #ffffff !important; */
   --el-color-success: #eaeaea;
   --el-color-warning: #cccccc;
   --el-color-danger: #f5f5f5;

+ 18 - 6
src/renderer/src/types/appMeta.d.ts

@@ -5,8 +5,16 @@ export type ResourcePackaging = 'c' | 'c_bin'
 export type ChipConfig = {
   // 芯片型号
   model: string
-  // 接口类型
-  interface: string
+  // 闪存大小
+  flash_size: {
+    capacity: string
+    unit: string
+  }
+  // 内存大小
+  ram_size: {
+    capacity: string
+    unit: string
+  }
 }
 
 export type BoardConfig = {
@@ -16,7 +24,9 @@ export type BoardConfig = {
 
 export type ScreenConfig = {
   // 屏幕类型
-  type: 1 | 2 // 1:主屏 2:副屏
+  type: number // 1:主屏 2:副屏
+  // 接口类型
+  interface: string
   // 屏幕宽
   width: number
   // 屏幕高
@@ -24,7 +34,7 @@ export type ScreenConfig = {
   // 颜色深度
   colorDepth: string
   // 颜色格式
-  colorFormat: 'BGR' | 'RGB'
+  colorFormat: string | 'BGR' | 'RGB'
   // 屏幕参数
   params: {
     PCLK: string
@@ -34,8 +44,8 @@ export type ScreenConfig = {
     HFP: string
     HSYNC: string
     VSYNC: string
-    HSYNC_WIDTH: string
-    HSYNC_WIDTH: string
+    HsyncWidth: string
+    VsyncWidth: string
   }
 }
 
@@ -52,6 +62,8 @@ export interface AppMeta {
   chip: ChipConfig
   // 板卡配置
   board: BoardConfig
+  // 图片压缩方式
+  imageCompress: string[]
   // 屏幕配置
   screens: ScreenConfig[]
   // 创建时间

+ 2 - 0
src/renderer/src/views/designer/index.vue

@@ -24,6 +24,7 @@
       </div>
     </div>
   </div>
+  <ProjectModal />
 </template>
 
 <script setup lang="ts">
@@ -33,6 +34,7 @@ import Sidebar from './sidebar/index.vue'
 import Workspace from './workspace/index.vue'
 import Config from './config/index.vue'
 import Info from './info/index.vue'
+import ProjectModal from './modals/projectModal/index.vue'
 </script>
 
 <style lang="less" scoped></style>

+ 734 - 0
src/renderer/src/views/designer/modals/projectModal/index.vue

@@ -0,0 +1,734 @@
+<template>
+  <el-dialog
+    v-model="showModal"
+    title="新建项目"
+    width="1000px"
+    body-class="h-500px overflow-y-auto overflow-x-hidden modal-body-scroll px-12px"
+    :modal="false"
+    :close-on-click-modal="false"
+    align-center
+  >
+    <el-form
+      :model="formData"
+      ref="form"
+      label-width="80px"
+      style="display: flex; flex-direction: column"
+    >
+      <el-form-item label="项目名称">
+        <el-input v-model="formData.name" placeholder="请输入"></el-input>
+      </el-form-item>
+      <el-form-item label="项目路径">
+        <el-input v-model="formData.path" readonly>
+          <template #append>
+            <el-button @click="selectPath"><LuFolder :size="16" /></el-button>
+          </template>
+        </el-input>
+      </el-form-item>
+      <el-form-item label="项目类型">
+        <el-select v-model="formData.type" @change="handlChangeType">
+          <el-option
+            v-for="item in typeOptions"
+            :key="item.value"
+            :label="item.label"
+            :value="item.value"
+          ></el-option>
+        </el-select>
+      </el-form-item>
+      <el-divider />
+      <!-- 芯片配置 -->
+      <template v-if="formData.type === 'chip'">
+        <el-row :gutter="12">
+          <el-col :span="8">
+            <el-form-item label="芯片型号">
+              <el-select v-model="formData.chip.model" @change="handleSelectChip">
+                <el-option
+                  v-for="item in chipOptions"
+                  :key="item"
+                  :label="item"
+                  :value="item"
+                ></el-option>
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="8">
+            <el-form-item label="闪存大小">
+              <el-autocomplete
+                size="small"
+                style="width: 100%"
+                :fetch-suggestions="querySearchFlash"
+                v-model.number="formData.chip.flash_size.capacity"
+              >
+                <template #append>
+                  <el-select v-model="formData.chip.flash_size.unit" style="width: 68px">
+                    <el-option
+                      v-for="item in selectedChipConfig?.flash_size?.unit_options || []"
+                      :key="item"
+                      :label="item"
+                      :value="item"
+                    ></el-option>
+                  </el-select>
+                </template>
+              </el-autocomplete>
+            </el-form-item>
+          </el-col>
+          <el-col :span="8">
+            <el-form-item label="内存大小">
+              <el-autocomplete
+                size="small"
+                style="width: 100%"
+                :fetch-suggestions="querySearchRam"
+                v-model.number="formData.chip.ram_size.capacity"
+              >
+                <template #append>
+                  <el-select v-model="formData.chip.ram_size.unit" style="width: 68px">
+                    <el-option
+                      v-for="item in selectedChipConfig?.ram_size?.unit_options || []"
+                      :key="item"
+                      :label="item"
+                      :value="item"
+                    ></el-option>
+                  </el-select>
+                </template>
+              </el-autocomplete>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <div class="flex items-center gap-12px">
+          <span class="font-bold">屏幕参数</span>
+          <el-radio-group
+            v-model="formData.screenType"
+            size="small"
+            fill="#6cf"
+            @change="handleChangeScreenTypeByChip"
+          >
+            <el-radio-button label="单屏" value="single" />
+            <el-radio-button label="双屏" value="dual" />
+          </el-radio-group>
+        </div>
+        <div v-for="(item, index) in formData.screens" :key="index">
+          <el-divider border-style="dashed">屏幕{{ index + 1 }}</el-divider>
+          <el-row :gutter="12">
+            <el-col :span="8">
+              <el-form-item label="分辨率">
+                <el-select
+                  :model-value="`${item.width}x${item.height}`"
+                  @change="(val) => handleSetResolution(val, index)"
+                >
+                  <el-option label="自定义" value="custom" />
+                  <el-option
+                    v-for="item in getScreenOptions('resolutions')?.[index] || []"
+                    :label="item"
+                    :value="item"
+                  ></el-option>
+                </el-select>
+              </el-form-item>
+            </el-col>
+            <el-col :span="8">
+              <el-form-item label="接口类型">
+                <el-select v-model="item.interface">
+                  <el-option
+                    v-for="item in getScreenOptions('interface')?.[index] || []"
+                    :label="item"
+                    :value="item"
+                  ></el-option>
+                </el-select>
+              </el-form-item>
+            </el-col>
+            <el-col :span="8">
+              <el-form-item label="颜色格式">
+                <el-select v-model="item.colorFormat">
+                  <el-option
+                    v-for="item in getScreenOptions('color_format')?.[index] || []"
+                    :label="item"
+                    :value="item"
+                  ></el-option>
+                </el-select>
+              </el-form-item>
+            </el-col>
+            <el-col :span="8">
+              <el-form-item label="颜色深度">
+                <el-select v-model="item.colorDepth">
+                  <el-option
+                    v-for="item in getScreenOptions('color_depth')?.[index] || []"
+                    :label="item"
+                    :value="item"
+                  ></el-option>
+                </el-select>
+              </el-form-item>
+            </el-col>
+            <el-col :span="8">
+              <el-form-item label="PCLK">
+                <el-input v-model="item.params.PCLK" />
+              </el-form-item>
+            </el-col>
+            <el-col :span="8">
+              <el-form-item label="VBP">
+                <el-input v-model="item.params.VBP" />
+              </el-form-item>
+            </el-col>
+            <el-col :span="8">
+              <el-form-item label="VFP">
+                <el-input v-model="item.params.VFP" />
+              </el-form-item>
+            </el-col>
+            <el-col :span="8">
+              <el-form-item label="HFP">
+                <el-input v-model="item.params.HFP" />
+              </el-form-item>
+            </el-col>
+            <el-col :span="8">
+              <el-form-item label="HSYNC">
+                <el-input v-model="item.params.HSYNC" />
+              </el-form-item>
+            </el-col>
+            <el-col :span="8">
+              <el-form-item label="VSYNC">
+                <el-input v-model="item.params.VSYNC" />
+              </el-form-item>
+            </el-col>
+            <el-col :span="8">
+              <el-form-item label="HsyncWidth">
+                <el-input v-model="item.params.HsyncWidth" />
+              </el-form-item>
+            </el-col>
+            <el-col :span="8">
+              <el-form-item label="VsyncWidth">
+                <el-input v-model="item.params.VsyncWidth" />
+              </el-form-item>
+            </el-col>
+          </el-row>
+        </div>
+      </template>
+      <!-- 板卡配置 -->
+      <template v-if="formData.type === 'board'">
+        <div class="flex items-center justify-end gap-12px mb-12px">
+          <el-input style="width: 160px" placeholder="请输入型号查询" v-model="query" />
+          <el-button @click="searchBoard">搜索</el-button>
+        </div>
+        <div class="max-h-300px overflow-y-auto overflow-x-hidden">
+          <el-row :gutter="12">
+            <el-col v-for="item in boardOptions || []" :span="12">
+              <div
+                class="w-full h-100px bg-#fff relative cursor-pointer border1 border-transparent border-solid"
+                :class="formData.board.model === item.board_name ? 'border-#0ff!' : ''"
+                @click="handleSetBoard(item)"
+              >
+                <img :src="item.pic_path" />
+                <div class="absolute w-full h-40px left-0 bottom-0 bg-#ccc truncate text-#fff">
+                  <div class="text-center">{{ item.board_name }}</div>
+                  <div class="text-center">{{ item.description }}</div>
+                </div>
+              </div>
+            </el-col>
+          </el-row>
+        </div>
+        <div class="my-12px text-text-secondary">当前选择:{{ formData.board.model }}</div>
+        <el-row :gutter="12">
+          <template v-for="(screen, index) in formData.screens">
+            <el-col :span="12">
+              <el-form-item :label="`屏幕${index + 1}分辨率`" label-width="120px">
+                <el-input :model-value="`${screen.width}x${screen.height}`" disabled />
+              </el-form-item>
+            </el-col>
+            <el-col :span="12">
+              <el-form-item :label="`屏幕${index + 1}颜色深度`" label-width="120px">
+                <el-select v-model="screen.colorDepth">
+                  <el-option
+                    v-for="item in colorDepthOptions[index] || []"
+                    :label="item"
+                    :value="item"
+                  ></el-option>
+                </el-select>
+              </el-form-item>
+            </el-col>
+          </template>
+        </el-row>
+      </template>
+      <!-- 虚拟显示 -->
+      <template v-if="formData.type === 'analog_display'">
+        <div class="flex items-center gap-12px">
+          <span class="font-bold">屏幕参数</span>
+          <el-radio-group
+            v-model="formData.screenType"
+            size="small"
+            fill="#6cf"
+            @change="handleChangeScreenTypeByAnalog"
+          >
+            <el-radio-button label="单屏" value="single" />
+            <el-radio-button label="双屏" value="dual" />
+          </el-radio-group>
+        </div>
+        <div v-for="(item, index) in formData.screens" :key="index">
+          <el-divider border-style="dashed">屏幕{{ index + 1 }}</el-divider>
+          <el-row :gutter="12">
+            <el-col :span="8">
+              <el-form-item label="分辨率">
+                <el-select
+                  :model-value="`${item.width}x${item.height}`"
+                  @change="(val) => handleSetResolution(val, index)"
+                >
+                  <el-option label="自定义" value="custom" />
+                  <el-option
+                    v-for="item in getAnalogDisplayOptions('resolutions')?.[index] || []"
+                    :label="item"
+                    :value="item"
+                  ></el-option>
+                </el-select>
+              </el-form-item>
+            </el-col>
+            <el-col :span="8">
+              <el-form-item label="颜色格式">
+                <el-select v-model="item.colorFormat">
+                  <el-option
+                    v-for="item in getAnalogDisplayOptions('color_format')?.[index] || []"
+                    :label="item"
+                    :value="item"
+                  ></el-option>
+                </el-select>
+              </el-form-item>
+            </el-col>
+            <el-col :span="8">
+              <el-form-item label="颜色深度">
+                <el-select v-model="item.colorDepth">
+                  <el-option
+                    v-for="item in getAnalogDisplayOptions('color_depth')?.[index] || []"
+                    :label="item"
+                    :value="item"
+                  ></el-option>
+                </el-select>
+              </el-form-item>
+            </el-col>
+          </el-row>
+        </div>
+      </template>
+      <el-divider />
+      <el-row :gutter="12">
+        <el-col :span="8">
+          <el-form-item label="版本号">
+            <el-input v-model="formData.version" />
+          </el-form-item>
+        </el-col>
+        <el-col :span="8">
+          <el-form-item label="打包方式">
+            <el-select v-model="formData.resourcePackaging">
+              <el-option label="C源码" value="c" />
+              <el-option label="C源码+BIN" value="c_bin" />
+            </el-select>
+          </el-form-item>
+        </el-col>
+        <el-col :span="8">
+          <el-form-item v-if="formData.resourcePackaging === 'c_bin'" label="BIN数量">
+            <el-input v-model.number="formData.binNum" />
+          </el-form-item>
+        </el-col>
+        <el-col :span="24">
+          <el-form-item label="项目描述">
+            <el-input type="textarea" v-model="formData.description" />
+          </el-form-item>
+        </el-col>
+      </el-row>
+    </el-form>
+
+    <template #footer>
+      <el-button @click="showModal = false">取消</el-button>
+      <el-button type="primary" @click="handleSubmit">创建</el-button>
+    </template>
+
+    <el-dialog
+      title="自定义屏幕"
+      v-model="showScreenModal"
+      width="440px"
+      :modal="false"
+      align-center
+    >
+      <el-form>
+        <el-form-item label="宽">
+          <el-input v-model.number="customScreen.width" />
+        </el-form-item>
+        <el-form-item label="高">
+          <el-input v-model.number="customScreen.height" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button type="primary" @click="handleSetScreen">确定</el-button>
+      </template>
+    </el-dialog>
+  </el-dialog>
+</template>
+
+<script setup lang="ts">
+import type { AppMeta } from '@/types/appMeta'
+import type { FormInstance } from 'element-plus'
+import { computed, reactive, ref, defineExpose } from 'vue'
+import { LuFolder } from 'vue-icons-plus/lu'
+import { useProjectStore } from '@/store/modules/project'
+
+import chipConfig from '@/config/multi_chip_config.json'
+import boardConfig from '@/config/board_card_config.json'
+import displayConfig from '@/config/analog_display_config.json'
+
+const formData = reactive<
+  AppMeta & {
+    path: string
+  }
+>({
+  version: '1.0.0',
+  description: '',
+  name: '',
+  path: '',
+  type: 'chip',
+  chip: {
+    model: '',
+    flash_size: {
+      capacity: '16',
+      unit: 'M'
+    },
+    // 内存大小
+    ram_size: {
+      capacity: '4',
+      unit: 'M'
+    }
+  },
+  screenType: 'single',
+  screens: [
+    {
+      type: 1,
+      interface: '',
+      width: 800,
+      height: 480,
+      colorDepth: '16',
+      colorFormat: 'BGR',
+      params: {
+        PCLK: '',
+        VBP: '',
+        VFP: '',
+        HBP: '',
+        HFP: '',
+        HSYNC: '',
+        VSYNC: '',
+        HsyncWidth: '',
+        VsyncWidth: ''
+      }
+    }
+  ],
+  board: {
+    model: ''
+  },
+  resourcePackaging: 'c',
+  binNum: 1,
+  language: 'zh-cn',
+  createTime: '',
+  modifyTime: '',
+  imageCompress: []
+})
+
+const projectStore = useProjectStore()
+
+const form = ref<FormInstance>()
+// 显示模态框
+const showModal = ref(true)
+// 显示自定义屏幕模态框
+const showScreenModal = ref(false)
+// 自定义屏幕
+const customScreen = ref({
+  width: 0,
+  height: 0,
+  index: 0
+})
+// 项目类型选项
+const typeOptions = [
+  { label: '芯片', value: 'chip' },
+  { label: '板卡', value: 'board' },
+  { label: '模拟显示', value: 'analog_display' }
+]
+
+// 切换项目类型
+const handlChangeType = (type: string) => {
+  if (type === 'analog_display') {
+    formData.imageCompress = getImageCompress(displayConfig.simulation_display.image_compression)
+  }
+  if (type === 'board') {
+    formData.screens = []
+  }
+  handleChangeScreenTypeByAnalog(formData.screenType)
+}
+
+// 选择项目路径
+const selectPath = async () => {
+  const path = await window.electron.ipcRenderer.invoke('get-directory')
+
+  if (path) {
+    formData.path = path
+  }
+}
+
+// 选择分辨率
+const handleSetResolution = (str: string, index: number) => {
+  const arr = str.split('x')
+  if (arr.length === 2) {
+    formData.screens[index].width = Number(arr[0])
+    formData.screens[index].height = Number(arr[1])
+    handleSetScreenParams(index)
+  }
+
+  if (str === 'custom') {
+    showScreenModal.value = true
+    customScreen.value.index = index
+    customScreen.value.width = formData.screens[index].width
+    customScreen.value.height = formData.screens[index].height
+  }
+}
+
+// 设置自定义分辨率
+const handleSetScreen = () => {
+  formData.screens[customScreen.value.index].width = customScreen.value.width
+  formData.screens[customScreen.value.index].height = customScreen.value.height
+  showScreenModal.value = false
+  handleSetScreenParams(customScreen.value.index)
+}
+
+// 获取图片压缩方式
+const getImageCompress = (params: Record<string, boolean>) => {
+  return Object.keys(params || {}).filter((key) => params[key])
+}
+/******************************芯片相关*******************************/
+// 芯片选项
+const chipOptions = Object.keys(chipConfig?.chips || {})
+// 已选芯片配置
+const selectedChipConfig = computed(() => chipConfig?.chips?.[formData.chip.model])
+// 屏幕接口选项
+const getScreenOptions = (type: string) => {
+  if (formData.screenType === 'single') {
+    return [selectedChipConfig.value?.single_screen?.[type].options]
+  } else {
+    const options1 = selectedChipConfig.value?.dual_screen?.screen0?.[type].options
+    const options2 = selectedChipConfig.value?.dual_screen?.screen1?.[type].options
+
+    return [options1, options2]
+  }
+}
+
+// 选择芯片
+const handleSelectChip = () => {
+  // 设置默认值
+
+  // 图片压缩方式
+  formData.imageCompress = getImageCompress(selectedChipConfig.value?.image_compression)
+
+  formData.chip.flash_size.capacity = selectedChipConfig.value?.flash_size?.capacity_default
+  formData.chip.flash_size.unit = selectedChipConfig.value?.flash_size?.unit_default
+  formData.chip.ram_size.capacity = selectedChipConfig.value?.ram_size?.capacity_default
+  formData.chip.ram_size.unit = selectedChipConfig.value?.ram_size?.unit_default
+  formData.screenType = selectedChipConfig.value?.screen_mode?.default || 'single'
+  handleChangeScreenTypeByChip(formData.screenType)
+}
+
+// 闪存选项
+const querySearchFlash = (_query: string, cb: (data: any) => void) => {
+  const list = selectedChipConfig.value?.flash_size?.capacity_options || []
+  cb(list.map((item) => ({ value: item })))
+}
+
+// 内存选项
+const querySearchRam = (_query: string, cb: (data: any) => void) => {
+  const list = selectedChipConfig.value?.ram_size?.capacity_options || []
+  cb(list.map((item) => ({ value: item })))
+}
+// 设置屏幕参数
+const handleSetScreenParams = (index: number) => {
+  const params =
+    selectedChipConfig.value?.resolution_presets?.[
+      `${formData.screens[index].width}x${formData.screens[index].height}`
+    ]
+
+  if (params) {
+    Object.keys(formData.screens[index].params).forEach((key) => {
+      ;(formData.screens[index].params as any)[key] = params[key]
+    })
+  }
+}
+
+// 选择芯片屏幕类型
+const handleChangeScreenTypeByChip = (type: any) => {
+  let screens =
+    type === 'single'
+      ? selectedChipConfig.value?.single_screen
+      : selectedChipConfig.value?.dual_screen
+  // 构建数组
+  if (screens && type === 'single') {
+    screens = [screens]
+  }
+  if (screens?.screen0) {
+    screens = [screens.screen0, screens.screen1]
+  }
+  if (screens.length) {
+    formData.screens = []
+  }
+  screens?.forEach((screen, index) => {
+    const resolution = selectedChipConfig.value?.resolution_presets[screen?.resolutions.default]
+    formData.screens.push({
+      // 屏幕类型
+      type: index + 1,
+      // 接口类型
+      interface: screen?.interface?.default,
+      // 屏幕宽
+      width: resolution?.width,
+      // 屏幕高
+      height: resolution?.height,
+      // 颜色深度
+      colorDepth: screen?.color_depth?.default,
+      // 颜色格式
+      colorFormat: screen?.color_format?.default,
+      // 屏幕参数
+      params: {
+        PCLK: resolution?.PCLK,
+        VBP: resolution?.VBP,
+        VFP: resolution?.VFP,
+        HBP: resolution?.HBP,
+        HFP: resolution?.HFP,
+        HSYNC: resolution?.HSYNC,
+        VSYNC: resolution?.VSYNC,
+        HsyncWidth: resolution?.HsyncWidth,
+        VsyncWidth: resolution?.VsyncWidth
+      }
+    })
+  })
+}
+
+/******************************板卡相关*******************************/
+// 查询
+const query = ref('')
+// 板卡列表
+const boardOptions = ref(boardConfig.boards || [])
+// 颜色深度选项
+const colorDepthOptions = ref<string[][]>([])
+// 搜索板卡
+const searchBoard = () => {
+  boardOptions.value = boardConfig.boards.filter((item) => item.board_name.includes(query.value))
+}
+// 选择板卡
+const handleSetBoard = (board: any) => {
+  formData.board.model = board.board_name
+  formData.imageCompress = getImageCompress(board.image_compression)
+  handleChangeScreenTypeByBoard(board)
+}
+// 设置板卡显示屏幕类型
+const handleChangeScreenTypeByBoard = (board: any) => {
+  const screens =
+    board.screen_mode === 'single'
+      ? [
+          {
+            resolutions: board.resolutions,
+            color_format: board.color_format,
+            color_depth: board.color_depth
+          }
+        ]
+      : [board.screen0, board.screen1]
+
+  if (screens) {
+    formData.screens = []
+    colorDepthOptions.value = []
+  }
+  screens.forEach((screen, index) => {
+    const resolutions = screen.resolutions
+    const arr = resolutions.split('x')
+    colorDepthOptions.value.push(screen.color_depth.options)
+    formData.screens.push({
+      type: index + 1,
+      width: Number(arr[0]),
+      height: Number(arr[1]),
+      colorDepth: screen?.color_depth?.default,
+      colorFormat: screen?.color_format,
+      interface: '',
+      params: {
+        PCLK: '',
+        VBP: '',
+        VFP: '',
+        HBP: '',
+        HFP: '',
+        HSYNC: '',
+        VSYNC: '',
+        HsyncWidth: '',
+        VsyncWidth: ''
+      }
+    })
+  })
+}
+
+/******************************虚拟显示相关*******************************/
+// 获取虚拟显示屏幕选项
+const getAnalogDisplayOptions = (type: string) => {
+  if (formData.screenType === 'single') {
+    return [displayConfig.simulation_display.single_screen[type].options]
+  } else {
+    const options1 = displayConfig.simulation_display?.dual_screen?.screen0?.[type].options
+    const options2 = displayConfig.simulation_display?.dual_screen?.screen1?.[type].options
+
+    return [options1, options2]
+  }
+}
+
+// 选择虚拟显示屏幕类型
+const handleChangeScreenTypeByAnalog = (type: any) => {
+  const screens =
+    type === 'single'
+      ? [displayConfig.simulation_display.single_screen]
+      : [
+          displayConfig.simulation_display.dual_screen.screen0,
+          displayConfig.simulation_display.dual_screen.screen1
+        ]
+
+  if (screens) {
+    formData.screens = []
+  }
+  screens.forEach((screen, index) => {
+    const resolutions = screen.resolutions
+    const arr = resolutions.default.split('x')
+    formData.screens.push({
+      type: index + 1,
+      width: Number(arr[0]),
+      height: Number(arr[1]),
+      colorDepth: screen?.color_depth?.default,
+      colorFormat: screen?.color_format?.default,
+      interface: '',
+      params: {
+        PCLK: '',
+        VBP: '',
+        VFP: '',
+        HBP: '',
+        HFP: '',
+        HSYNC: '',
+        VSYNC: '',
+        HsyncWidth: '',
+        VsyncWidth: ''
+      }
+    })
+  })
+}
+
+// 创建项目
+const handleSubmit = async () => {
+  await form.value?.validate()
+  projectStore.createApp(formData)
+  form.value?.resetFields()
+  showModal.value = false
+}
+
+// expose
+defineExpose({
+  open: () => {
+    showModal.value = true
+  },
+  close: () => {
+    showModal.value = false
+  }
+})
+</script>
+
+<style lang="less" scoped>
+.modal-body-scroll {
+  &::-webkit-scrollbar {
+    display: none;
+  }
+}
+</style>