diff --git a/webui/src/components/ParameterForm.vue b/webui/src/components/ParameterForm.vue index 58842b5..8d1f76d 100644 --- a/webui/src/components/ParameterForm.vue +++ b/webui/src/components/ParameterForm.vue @@ -267,11 +267,11 @@ class="px-8" /> + + + @@ -355,6 +364,7 @@ const calculating = ref(false) const result = ref(null) const error = ref(null) const logRef = ref | null>(null) +const fileInput = ref(null) const voltageOptions = [ '110kV', '220kV', '330kV', '500kV', '750kV','1000kV', @@ -440,13 +450,6 @@ const calculate = async () => { } } -// 重置参数 -const resetParams = () => { - Object.assign(params, JSON.parse(JSON.stringify(defaultParams))) - result.value = null - error.value = null -} - // 将参数转换为 TOML 格式 const tomlStringify = (obj: any, indent: string = ''): string => { let result = '' @@ -471,6 +474,72 @@ const tomlStringify = (obj: any, indent: string = ''): string => { return result } +// 解析 TOML 格式字符串 +const parseToml = (tomlStr: string): any => { + const result: any = {} + let currentSection: any = result + let currentSectionName = '' + + const lines = tomlStr.split('\n') + + for (let line of lines) { + line = line.trim() + + // 跳过空行和注释 + if (!line || line.startsWith('#')) continue + + // 匹配 section [xxx] + const sectionMatch = line.match(/^\[([^\]]+)\]$/) + if (sectionMatch) { + currentSectionName = sectionMatch[1] + currentSection = {} + result[currentSectionName] = currentSection + continue + } + + // 匹配 key = value + const kvMatch = line.match(/^([^=]+)=(.*)$/) + if (kvMatch) { + const key = kvMatch[1].trim() + let value: any = kvMatch[2].trim() + + // 解析数组 [1, 2, 3] + if (value.startsWith('[') && value.endsWith(']')) { + const arrStr = value.slice(1, -1).trim() + if (arrStr) { + value = arrStr.split(',').map((s: string) => { + s = s.trim() + if (s.startsWith('"') && s.endsWith('"')) { + return s.slice(1, -1) + } + return isNaN(Number(s)) ? s : Number(s) + }) + } else { + value = [] + } + } + // 解析字符串 "xxx" + else if (value.startsWith('"') && value.endsWith('"')) { + value = value.slice(1, -1) + } + // 解析布尔值 + else if (value === 'true') { + value = true + } else if (value === 'false') { + value = false + } + // 解析数字 + else if (!isNaN(Number(value))) { + value = Number(value) + } + + currentSection[key] = value + } + } + + return result +} + // 导出配置 const exportConfig = async () => { try { @@ -500,6 +569,44 @@ const exportConfig = async () => { } } +// 导入配置 - 触发文件选择 +const importConfig = () => { + fileInput.value?.click() +} + +// 处理文件选择 +const handleFileSelect = async (event: Event) => { + const input = event.target as HTMLInputElement + const file = input.files?.[0] + if (!file) return + + try { + const content = await file.text() + const importedParams = parseToml(content) + + // 合并导入的参数到当前参数 + if (importedParams.parameter) { + Object.assign(params.parameter, importedParams.parameter) + } + if (importedParams.advance) { + Object.assign(params.advance, importedParams.advance) + } + if (importedParams.optional) { + Object.assign(params.optional, importedParams.optional) + } + + logRef.value?.addLog('info', `成功导入配置: ${file.name}`) + result.value = null + error.value = null + } catch (e: any) { + error.value = e.message || '导入失败' + logRef.value?.addLog('error', e.message || '导入失败') + } + + // 清空 input 以便可以重复选择同一个文件 + input.value = '' +} + // 声明 pywebview API 类型 declare global { interface Window {