feat: 添加配置导入功能
将重置参数按钮改为导入配置按钮,并实现TOML文件解析和参数导入功能
This commit is contained in:
@@ -267,11 +267,11 @@
|
|||||||
class="px-8"
|
class="px-8"
|
||||||
/>
|
/>
|
||||||
<q-btn
|
<q-btn
|
||||||
color="grey-7"
|
color="orange"
|
||||||
size="lg"
|
size="lg"
|
||||||
label="重置参数"
|
label="导入配置"
|
||||||
icon="refresh"
|
icon="upload"
|
||||||
@click="resetParams"
|
@click="importConfig"
|
||||||
class="px-8"
|
class="px-8"
|
||||||
/>
|
/>
|
||||||
<q-btn
|
<q-btn
|
||||||
@@ -284,6 +284,15 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 隐藏的文件输入 -->
|
||||||
|
<input
|
||||||
|
ref="fileInput"
|
||||||
|
type="file"
|
||||||
|
accept=".toml"
|
||||||
|
style="display: none"
|
||||||
|
@change="handleFileSelect"
|
||||||
|
/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<!-- 错误信息 -->
|
<!-- 错误信息 -->
|
||||||
@@ -355,6 +364,7 @@ const calculating = ref(false)
|
|||||||
const result = ref<string | null>(null)
|
const result = ref<string | null>(null)
|
||||||
const error = ref<string | null>(null)
|
const error = ref<string | null>(null)
|
||||||
const logRef = ref<InstanceType<typeof LogComponent> | null>(null)
|
const logRef = ref<InstanceType<typeof LogComponent> | null>(null)
|
||||||
|
const fileInput = ref<HTMLInputElement | null>(null)
|
||||||
|
|
||||||
const voltageOptions = [
|
const voltageOptions = [
|
||||||
'110kV', '220kV', '330kV', '500kV', '750kV','1000kV',
|
'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 格式
|
// 将参数转换为 TOML 格式
|
||||||
const tomlStringify = (obj: any, indent: string = ''): string => {
|
const tomlStringify = (obj: any, indent: string = ''): string => {
|
||||||
let result = ''
|
let result = ''
|
||||||
@@ -471,6 +474,72 @@ const tomlStringify = (obj: any, indent: string = ''): string => {
|
|||||||
return result
|
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 () => {
|
const exportConfig = async () => {
|
||||||
try {
|
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 类型
|
// 声明 pywebview API 类型
|
||||||
declare global {
|
declare global {
|
||||||
interface Window {
|
interface Window {
|
||||||
|
|||||||
Reference in New Issue
Block a user