|
|
@@ -23,11 +23,12 @@
|
|
|
<el-input
|
|
|
v-model="formData.name"
|
|
|
:placeholder="$t('pleaseEnter')"
|
|
|
+ :disabled="mode === 'edit'"
|
|
|
spellcheck="false"
|
|
|
></el-input>
|
|
|
</el-form-item>
|
|
|
<el-form-item :label="$t('projectPath')" prop="path">
|
|
|
- <el-input spellcheck="false" v-model="formData.path">
|
|
|
+ <el-input spellcheck="false" v-model="formData.path" :disabled="mode === 'edit'">
|
|
|
<template #append>
|
|
|
<el-button @click="selectPath('path')" :disabled="mode === 'edit'"
|
|
|
><LuFolder :size="16" :disabled="mode === 'edit'"
|
|
|
@@ -36,16 +37,16 @@
|
|
|
</el-input>
|
|
|
</el-form-item>
|
|
|
<el-form-item :label="$t('codePath')" prop="codePath">
|
|
|
- <el-input spellcheck="false" v-model="formData.codePath">
|
|
|
+ <el-input spellcheck="false" v-model="formData.codePath" :disabled="mode === 'edit'">
|
|
|
<template #append>
|
|
|
- <el-button @click="selectPath('codePath')"
|
|
|
+ <el-button @click="selectPath('codePath')" :disabled="mode === 'edit'"
|
|
|
><LuFolder :size="16" :disabled="mode === 'edit'"
|
|
|
/></el-button>
|
|
|
</template>
|
|
|
</el-input>
|
|
|
</el-form-item>
|
|
|
<el-form-item :label="$t('projectType')" prop="type">
|
|
|
- <el-select v-model="formData.type" @change="handlChangeType">
|
|
|
+ <el-select v-model="formData.type" @change="handlChangeType" :disabled="mode === 'edit'">
|
|
|
<el-option
|
|
|
v-for="item in typeOptions"
|
|
|
:key="item.value"
|
|
|
@@ -55,7 +56,7 @@
|
|
|
</el-select>
|
|
|
</el-form-item>
|
|
|
<el-divider />
|
|
|
- <!-- 鑺墖閰嶇疆 -->
|
|
|
+ <!-- 芯片配置 -->
|
|
|
<template v-if="formData.type === 'chip'">
|
|
|
<el-row :gutter="12">
|
|
|
<el-col :span="8">
|
|
|
@@ -119,6 +120,7 @@
|
|
|
size="small"
|
|
|
fill="#6cf"
|
|
|
@change="handleChangeScreenTypeByChip"
|
|
|
+
|
|
|
>
|
|
|
<el-radio-button :label="$t('singleScreen')" value="single" />
|
|
|
<el-radio-button :label="$t('doubleScreen')" value="dual" />
|
|
|
@@ -275,7 +277,7 @@
|
|
|
</el-row>
|
|
|
</div>
|
|
|
</template>
|
|
|
- <!-- 鏉垮崱閰嶇疆 -->
|
|
|
+ <!-- 板卡配置 -->
|
|
|
<template v-if="formData.type === 'board'">
|
|
|
<div class="flex items-center justify-end gap-12px mb-12px">
|
|
|
<el-input
|
|
|
@@ -354,7 +356,7 @@
|
|
|
</template>
|
|
|
</el-row>
|
|
|
</template>
|
|
|
- <!-- 铏氭嫙鏄剧ず -->
|
|
|
+ <!-- 虚拟显示 -->
|
|
|
<template v-if="formData.type === 'analog_display'">
|
|
|
<div class="flex items-center justify-center gap-12px">
|
|
|
<el-radio-group
|
|
|
@@ -481,7 +483,7 @@
|
|
|
<template #footer>
|
|
|
<el-button @click="close()">{{ $t('cancel') }}</el-button>
|
|
|
<el-button type="primary" @click="mode === 'add' ? handleSubmit() : handleEdit()">{{
|
|
|
- $t('create')
|
|
|
+ mode === 'add' ? $t('create') : $t('edit')
|
|
|
}}</el-button>
|
|
|
</template>
|
|
|
|
|
|
@@ -517,7 +519,7 @@
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
import type { AppMeta } from '@/types/appMeta'
|
|
|
-import { ElMessage, type FormInstance } from 'element-plus'
|
|
|
+import { ElMessage, ElMessageBox, type FormInstance } from 'element-plus'
|
|
|
import { computed, reactive, ref, nextTick, watch } from 'vue'
|
|
|
import { LuFolder } from 'vue-icons-plus/lu'
|
|
|
import { useProjectStore } from '@/store/modules/project'
|
|
|
@@ -565,7 +567,7 @@ const formData = reactive<
|
|
|
capacity: '16',
|
|
|
unit: 'M'
|
|
|
},
|
|
|
- // 鍐呭瓨澶у皬
|
|
|
+ // 内存大小
|
|
|
ram_size: {
|
|
|
capacity: '4',
|
|
|
unit: 'M'
|
|
|
@@ -609,7 +611,7 @@ const formData = reactive<
|
|
|
})
|
|
|
|
|
|
/**
|
|
|
- * 璁剧疆榛樿椤圭洰鍚?
|
|
|
+ * 设置默认项目名
|
|
|
*/
|
|
|
const setProjectDefaultName = () => {
|
|
|
const index = getNextIndex(recentProject.recentProjects || [], 'projectName')
|
|
|
@@ -621,17 +623,18 @@ watch(() => recentProject.recentProjects, setProjectDefaultName, { immediate: tr
|
|
|
|
|
|
const form = ref<FormInstance>()
|
|
|
const resolutionFormRef = ref<FormInstance>()
|
|
|
-// 鏄剧ず妯℃€佹
|
|
|
+// 显示模态框
|
|
|
const showModal = ref(!props.initHide)
|
|
|
-// 鏄剧ず鑷畾涔夊睆骞曟ā鎬佹
|
|
|
+// 显示自定义屏幕模态框
|
|
|
const showScreenModal = ref(false)
|
|
|
-// 鑷畾涔夊睆骞?
|
|
|
+const hasConfirmedSingleScreenRisk = ref(false)
|
|
|
+// 自定义屏幕
|
|
|
const customScreen = ref({
|
|
|
width: 0,
|
|
|
height: 0,
|
|
|
index: 0
|
|
|
})
|
|
|
-// 椤圭洰绫诲瀷閫夐」
|
|
|
+// 项目类型选项
|
|
|
const typeOptions = computed(() => {
|
|
|
return (
|
|
|
appStore.lang && [
|
|
|
@@ -688,7 +691,7 @@ const rules = computed(() => {
|
|
|
}
|
|
|
})
|
|
|
|
|
|
-// 鍒囨崲椤圭洰绫诲瀷
|
|
|
+// 切换项目类型
|
|
|
const handlChangeType = (type: string) => {
|
|
|
if (type === 'analog_display') {
|
|
|
formData.imageCompress = getImageCompress(displayConfig.simulation_display.image_compression)
|
|
|
@@ -700,7 +703,7 @@ const handlChangeType = (type: string) => {
|
|
|
handleChangeScreenTypeByAnalog(formData.screenType)
|
|
|
}
|
|
|
|
|
|
-// 閫夋嫨鏂囦欢澶硅矾寰?
|
|
|
+// 选择文件夹路径
|
|
|
const selectPath = async (key: string) => {
|
|
|
const path = await window.electron.ipcRenderer.invoke('get-directory')
|
|
|
|
|
|
@@ -709,7 +712,7 @@ const selectPath = async (key: string) => {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-// 閫夋嫨鍒嗚鲸鐜?
|
|
|
+// 选择分辨率
|
|
|
const handleSetResolution = (str: string, index: number) => {
|
|
|
const arr = str.split('x')
|
|
|
if (arr.length === 2) {
|
|
|
@@ -726,7 +729,7 @@ const handleSetResolution = (str: string, index: number) => {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-// 璁剧疆鑷畾涔夊垎杈ㄧ巼
|
|
|
+// 设置自定义分辨率
|
|
|
const handleSetScreen = () => {
|
|
|
resolutionFormRef.value?.validate()
|
|
|
if (!customScreen.value.width || !customScreen.value.height) return
|
|
|
@@ -737,16 +740,16 @@ const handleSetScreen = () => {
|
|
|
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]
|
|
|
@@ -758,11 +761,11 @@ const getScreenOptions = (type: string) => {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-// 閫夋嫨鑺墖
|
|
|
+// 选择芯片
|
|
|
const handleSelectChip = () => {
|
|
|
- // 璁剧疆榛樿鍊?
|
|
|
+ // 设置默认值
|
|
|
|
|
|
- // 鍥剧墖鍘嬬缉鏂瑰紡
|
|
|
+ // 图片压缩方式
|
|
|
formData.imageCompress = getImageCompress(selectedChipConfig.value?.image_compression)
|
|
|
formData.videoFormats = selectedChipConfig.value?.video_formats
|
|
|
|
|
|
@@ -774,18 +777,18 @@ const handleSelectChip = () => {
|
|
|
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?.[
|
|
|
@@ -799,13 +802,19 @@ const handleSetScreenParams = (index: number) => {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-// 閫夋嫨鑺墖灞忓箷绫诲瀷
|
|
|
-const handleChangeScreenTypeByChip = (type: any) => {
|
|
|
+// 选择芯片屏幕类型
|
|
|
+const handleChangeScreenTypeByChip = async (type: any) => {
|
|
|
+ const canChange = await handleChangeScreenType(type);
|
|
|
+ if (!canChange) {
|
|
|
+ formData.screenType = type === 'single' ? 'dual' : 'single'
|
|
|
+ return
|
|
|
+ };
|
|
|
+
|
|
|
let screens =
|
|
|
type === 'single'
|
|
|
? selectedChipConfig.value?.single_screen
|
|
|
: selectedChipConfig.value?.dual_screen
|
|
|
- // 鏋勫缓鏁扮粍
|
|
|
+ // 构建数组
|
|
|
if (screens && type === 'single') {
|
|
|
screens = [screens]
|
|
|
}
|
|
|
@@ -818,21 +827,21 @@ const handleChangeScreenTypeByChip = (type: any) => {
|
|
|
screens?.forEach((screen, index) => {
|
|
|
const resolution = selectedChipConfig.value?.resolution_presets[screen?.resolutions.default]
|
|
|
formData.screens.push({
|
|
|
- // 灞忓箷绫诲瀷
|
|
|
+ // 屏幕类型
|
|
|
type: index + 1,
|
|
|
- // 鎺ュ彛绫诲瀷
|
|
|
+ // 接口类型
|
|
|
interface: screen?.interface?.default,
|
|
|
- // 灞忓箷鏂瑰悜
|
|
|
+ // 屏幕方向
|
|
|
screenDirection: screen?.screen_direction?.default,
|
|
|
- // 灞忓箷瀹?
|
|
|
+ // 屏幕宽
|
|
|
width: resolution?.width,
|
|
|
- // 灞忓箷楂?
|
|
|
+ // 屏幕高
|
|
|
height: resolution?.height,
|
|
|
- // 棰滆壊娣卞害
|
|
|
+ // 颜色深度
|
|
|
colorDepth: screen?.color_depth?.default,
|
|
|
- // 棰滆壊鏍煎紡
|
|
|
+ // 颜色格式
|
|
|
colorFormat: screen?.color_format?.default,
|
|
|
- // 灞忓箷鍙傛暟
|
|
|
+ // 屏幕参数
|
|
|
params: {
|
|
|
PCLK: resolution?.PCLK,
|
|
|
HFP: resolution?.HFP,
|
|
|
@@ -848,18 +857,18 @@ const handleChangeScreenTypeByChip = (type: any) => {
|
|
|
})
|
|
|
}
|
|
|
|
|
|
-/******************************鏉垮崱鐩稿叧*******************************/
|
|
|
-// 鏌ヨ
|
|
|
+/******************************板卡相关*******************************/
|
|
|
+// 查询
|
|
|
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 getBoardOptions = (type: string) => {
|
|
|
if (formData.screenType === 'single') {
|
|
|
return [
|
|
|
@@ -876,15 +885,18 @@ const getBoardOptions = (type: string) => {
|
|
|
return [options1, options2]
|
|
|
}
|
|
|
}
|
|
|
-// 閫夋嫨鏉垮崱
|
|
|
-const handleSetBoard = (board: any) => {
|
|
|
+// 选择板卡
|
|
|
+const handleSetBoard = async (board: any) => {
|
|
|
+ const canChange = await handleChangeScreenType(board.screen_mode)
|
|
|
+ if (!canChange) return
|
|
|
+
|
|
|
formData.board.model = board.board_name
|
|
|
formData.imageCompress = getImageCompress(board.image_compression)
|
|
|
formData.videoFormats = board.video_formats
|
|
|
formData.screenType = board.screen_mode
|
|
|
handleChangeScreenTypeByBoard(board)
|
|
|
}
|
|
|
-// 璁剧疆鏉垮崱鏄剧ず灞忓箷绫诲瀷
|
|
|
+// 设置板卡显示屏幕类型
|
|
|
const handleChangeScreenTypeByBoard = (board: any) => {
|
|
|
const screens =
|
|
|
board.screen_mode === 'single'
|
|
|
@@ -929,8 +941,8 @@ const handleChangeScreenTypeByBoard = (board: any) => {
|
|
|
})
|
|
|
}
|
|
|
|
|
|
-/******************************铏氭嫙鏄剧ず鐩稿叧*******************************/
|
|
|
-// 鑾峰彇铏氭嫙鏄剧ず灞忓箷閫夐」
|
|
|
+/******************************虚拟显示相关*******************************/
|
|
|
+// 获取虚拟显示屏幕选项
|
|
|
const getAnalogDisplayOptions = (type: string) => {
|
|
|
if (formData.screenType === 'single') {
|
|
|
return [displayConfig.simulation_display.single_screen[type].options]
|
|
|
@@ -942,8 +954,14 @@ const getAnalogDisplayOptions = (type: string) => {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-// 閫夋嫨铏氭嫙鏄剧ず灞忓箷绫诲瀷
|
|
|
-const handleChangeScreenTypeByAnalog = (type: any) => {
|
|
|
+// 选择虚拟显示屏幕类型
|
|
|
+const handleChangeScreenTypeByAnalog = async (type: any) => {
|
|
|
+ const canChange = await handleChangeScreenType(type)
|
|
|
+ if (!canChange) {
|
|
|
+ formData.screenType = type === 'single' ? 'dual' : 'single'
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
const screens =
|
|
|
type === 'single'
|
|
|
? [displayConfig.simulation_display.single_screen]
|
|
|
@@ -981,14 +999,14 @@ const handleChangeScreenTypeByAnalog = (type: any) => {
|
|
|
})
|
|
|
}
|
|
|
|
|
|
-// 鍒涘缓椤圭洰
|
|
|
+// 创建项目
|
|
|
const handleSubmit = async () => {
|
|
|
await form.value?.validate()
|
|
|
if (formData.type === 'board' && !formData.board.model) {
|
|
|
ElMessage.warning(t('selectBoard'))
|
|
|
return
|
|
|
}
|
|
|
- // 璁颁綇椤圭洰璺緞
|
|
|
+ // 记住项目路径
|
|
|
setProjectPath(formData.path)
|
|
|
setCodePath(formData.codePath)
|
|
|
|
|
|
@@ -997,12 +1015,50 @@ const handleSubmit = async () => {
|
|
|
showModal.value = false
|
|
|
}
|
|
|
|
|
|
-// 缂栬緫椤圭洰
|
|
|
+/**
|
|
|
+ * 切换单双屏幕处理
|
|
|
+ * ⚠️ 如果是编辑项目,双屏切单屏需要提示数据丢失风险
|
|
|
+ * @param type 'single' | 'dual'
|
|
|
+ * @returns Promise<boolean> true 表示允许切换,false 表示取消
|
|
|
+ */
|
|
|
+const handleChangeScreenType = async (type: string): Promise<boolean> => {
|
|
|
+ const isEditMode = mode.value === 'edit'
|
|
|
+ const wasDualScreen = projectStore.project?.meta.screenType === 'dual'
|
|
|
+ const willSingleScreen = type === 'single'
|
|
|
+
|
|
|
+ if (hasConfirmedSingleScreenRisk.value && willSingleScreen) return true
|
|
|
+ if (!isEditMode || !wasDualScreen || !willSingleScreen) return true
|
|
|
+
|
|
|
+ try {
|
|
|
+ await ElMessageBox.confirm(t('switchToSingleScreenWarning'), t('warning'), {
|
|
|
+ type: 'warning',
|
|
|
+ confirmButtonText: t('confirm'),
|
|
|
+ cancelButtonText: t('cancel')
|
|
|
+ })
|
|
|
+ hasConfirmedSingleScreenRisk.value = true
|
|
|
+ return true
|
|
|
+ } catch {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 编辑项目
|
|
|
const handleEdit = async () => {
|
|
|
await form.value?.validate()
|
|
|
+ if (formData.type === 'board' && !formData.board.model) {
|
|
|
+ ElMessage.warning(t('selectBoard'))
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ const canChange = await handleChangeScreenType(formData.screenType)
|
|
|
+ if (!canChange) return
|
|
|
+
|
|
|
+ await projectStore.editProject(klona(formData))
|
|
|
+ ElMessage.success(t('editSuccess'))
|
|
|
+ close()
|
|
|
}
|
|
|
|
|
|
-// 鍏抽棴寮圭獥
|
|
|
+// 关闭弹窗
|
|
|
const close = (done?: () => void) => {
|
|
|
// if (!projectStore.project) {
|
|
|
// ElMessage.warning(t('createProjectFirst'))
|
|
|
@@ -1010,6 +1066,7 @@ const close = (done?: () => void) => {
|
|
|
// }
|
|
|
|
|
|
done?.()
|
|
|
+ hasConfirmedSingleScreenRisk.value = false
|
|
|
form.value?.resetFields()
|
|
|
showModal.value = false
|
|
|
}
|
|
|
@@ -1018,6 +1075,7 @@ const close = (done?: () => void) => {
|
|
|
defineExpose({
|
|
|
create: async () => {
|
|
|
mode.value = 'add'
|
|
|
+ hasConfirmedSingleScreenRisk.value = false
|
|
|
showModal.value = true
|
|
|
form.value?.resetFields()
|
|
|
await nextTick()
|
|
|
@@ -1026,10 +1084,12 @@ defineExpose({
|
|
|
setProjectDefaultName()
|
|
|
},
|
|
|
edit: () => {
|
|
|
- const data = klona(projectStore.project?.meta || {})
|
|
|
+ const data = klona(projectStore.project?.meta || ({} as AppMeta))
|
|
|
Object.entries(data).forEach(([key, value]) => {
|
|
|
formData[key] = value
|
|
|
})
|
|
|
+ formData.screens = klona(data.screens || [])
|
|
|
+ hasConfirmedSingleScreenRisk.value = false
|
|
|
mode.value = 'edit'
|
|
|
showModal.value = true
|
|
|
}
|