Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
phsl
/
new-api
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Snippets
Members
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Commit
a1b796b7
authored
Aug 08, 2025
by
HynoR
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: optimized Json Visual Editor(JSONEditor) when detected duplicate key
parent
b49274ea
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
299 additions
and
314 deletions
+299
-314
web/src/components/common/ui/JSONEditor.js
+299
-314
No files found.
web/src/components/common/ui/JSONEditor.js
View file @
a1b796b7
import
React
,
{
useState
,
useEffect
,
useCallback
}
from
'react'
;
import
React
,
{
useState
,
useEffect
,
useCallback
,
useMemo
}
from
'react'
;
import
{
useTranslation
}
from
'react-i18next'
;
import
{
Button
,
...
...
@@ -15,12 +15,14 @@ import {
Row
,
Col
,
Divider
,
Tooltip
,
}
from
'@douyinfe/semi-ui'
;
import
{
IconCode
,
IconPlus
,
IconDelete
,
IconRefresh
,
IconAlertTriangle
,
}
from
'@douyinfe/semi-icons'
;
const
{
Text
}
=
Typography
;
...
...
@@ -43,24 +45,44 @@ const JSONEditor = ({
})
=>
{
const
{
t
}
=
useTranslation
();
// 初始化JSON数据
const
[
jsonData
,
setJsonData
]
=
useState
(()
=>
{
// 初始化时解析JSON数据
// 将对象转换为键值对数组(包含唯一ID)
const
objectToKeyValueArray
=
useCallback
((
obj
)
=>
{
if
(
!
obj
||
typeof
obj
!==
'object'
)
return
[];
return
Object
.
entries
(
obj
).
map
(([
key
,
value
],
index
)
=>
({
id
:
`
${
Date
.
now
()}
_
${
index
}
_
${
Math
.
random
()}
`
,
// 唯一ID
key
,
value
}));
},
[]);
// 将键值对数组转换为对象(重复键时后面的会覆盖前面的)
const
keyValueArrayToObject
=
useCallback
((
arr
)
=>
{
const
result
=
{};
arr
.
forEach
(
item
=>
{
if
(
item
.
key
)
{
result
[
item
.
key
]
=
item
.
value
;
}
});
return
result
;
},
[]);
// 初始化键值对数组
const
[
keyValuePairs
,
setKeyValuePairs
]
=
useState
(()
=>
{
if
(
typeof
value
===
'string'
&&
value
.
trim
())
{
try
{
const
parsed
=
JSON
.
parse
(
value
);
return
parsed
;
return
objectToKeyValueArray
(
parsed
)
;
}
catch
(
error
)
{
return
{}
;
return
[]
;
}
}
if
(
typeof
value
===
'object'
&&
value
!==
null
)
{
return
value
;
return
objectToKeyValueArray
(
value
)
;
}
return
{}
;
return
[]
;
});
// 手动模式下的本地文本缓冲
,避免无效 JSON 时被外部值重置
// 手动模式下的本地文本缓冲
const
[
manualText
,
setManualText
]
=
useState
(()
=>
{
if
(
typeof
value
===
'string'
)
return
value
;
if
(
value
&&
typeof
value
===
'object'
)
return
JSON
.
stringify
(
value
,
null
,
2
);
...
...
@@ -69,22 +91,38 @@ const JSONEditor = ({
// 根据键数量决定默认编辑模式
const
[
editMode
,
setEditMode
]
=
useState
(()
=>
{
// 如果初始JSON数据的键数量大于10个,则默认使用手动模式
if
(
typeof
value
===
'string'
&&
value
.
trim
())
{
try
{
const
parsed
=
JSON
.
parse
(
value
);
const
keyCount
=
Object
.
keys
(
parsed
).
length
;
return
keyCount
>
10
?
'manual'
:
'visual'
;
}
catch
(
error
)
{
// JSON无效时默认显示手动编辑模式
return
'manual'
;
}
}
return
'visual'
;
});
const
[
jsonError
,
setJsonError
]
=
useState
(
''
);
// 数据同步 - 当value变化时总是更新jsonData(如果JSON有效)
// 计算重复的键
const
duplicateKeys
=
useMemo
(()
=>
{
const
keyCount
=
{};
const
duplicates
=
new
Set
();
keyValuePairs
.
forEach
(
pair
=>
{
if
(
pair
.
key
)
{
keyCount
[
pair
.
key
]
=
(
keyCount
[
pair
.
key
]
||
0
)
+
1
;
if
(
keyCount
[
pair
.
key
]
>
1
)
{
duplicates
.
add
(
pair
.
key
);
}
}
});
return
duplicates
;
},
[
keyValuePairs
]);
// 数据同步 - 当value变化时更新键值对数组
useEffect
(()
=>
{
try
{
let
parsed
=
{};
...
...
@@ -93,16 +131,20 @@ const JSONEditor = ({
}
else
if
(
typeof
value
===
'object'
&&
value
!==
null
)
{
parsed
=
value
;
}
setJsonData
(
parsed
);
// 只在外部值真正改变时更新,避免循环更新
const
currentObj
=
keyValueArrayToObject
(
keyValuePairs
);
if
(
JSON
.
stringify
(
parsed
)
!==
JSON
.
stringify
(
currentObj
))
{
setKeyValuePairs
(
objectToKeyValueArray
(
parsed
));
}
setJsonError
(
''
);
}
catch
(
error
)
{
console
.
log
(
'JSON解析失败:'
,
error
.
message
);
setJsonError
(
error
.
message
);
// JSON格式错误时不更新jsonData
}
},
[
value
]);
// 外部 value 变化时,若不在手动模式,则同步手动文本
;在手动模式下不打断用户输入
// 外部 value 变化时,若不在手动模式,则同步手动文本
useEffect
(()
=>
{
if
(
editMode
!==
'manual'
)
{
if
(
typeof
value
===
'string'
)
setManualText
(
value
);
...
...
@@ -112,45 +154,47 @@ const JSONEditor = ({
},
[
value
,
editMode
]);
// 处理可视化编辑的数据变化
const
handleVisualChange
=
useCallback
((
newData
)
=>
{
setJsonData
(
newData
);
const
handleVisualChange
=
useCallback
((
newPairs
)
=>
{
setKeyValuePairs
(
newPairs
);
const
jsonObject
=
keyValueArrayToObject
(
newPairs
);
const
jsonString
=
Object
.
keys
(
jsonObject
).
length
===
0
?
''
:
JSON
.
stringify
(
jsonObject
,
null
,
2
);
setJsonError
(
''
);
const
jsonString
=
Object
.
keys
(
newData
).
length
===
0
?
''
:
JSON
.
stringify
(
newData
,
null
,
2
);
// 通过formApi设置值
(如果提供的话)
// 通过formApi设置值
if
(
formApi
&&
field
)
{
formApi
.
setValue
(
field
,
jsonString
);
}
onChange
?.(
jsonString
);
},
[
onChange
,
formApi
,
field
]);
},
[
onChange
,
formApi
,
field
,
keyValueArrayToObject
]);
// 处理手动编辑的数据变化
(无效 JSON 不阻断输入,也不立刻回传上游)
// 处理手动编辑的数据变化
const
handleManualChange
=
useCallback
((
newValue
)
=>
{
setManualText
(
newValue
);
if
(
newValue
&&
newValue
.
trim
())
{
try
{
JSON
.
parse
(
newValue
);
const
parsed
=
JSON
.
parse
(
newValue
);
setKeyValuePairs
(
objectToKeyValueArray
(
parsed
));
setJsonError
(
''
);
onChange
?.(
newValue
);
}
catch
(
error
)
{
setJsonError
(
error
.
message
);
// 无效 JSON 时不回传,避免外部值把输入重置
}
}
else
{
setKeyValuePairs
([]);
setJsonError
(
''
);
onChange
?.(
''
);
}
},
[
onChange
]);
},
[
onChange
,
objectToKeyValueArray
]);
// 切换编辑模式
const
toggleEditMode
=
useCallback
(()
=>
{
if
(
editMode
===
'visual'
)
{
// 从可视化模式切换到手动模式
setManualText
(
Object
.
keys
(
json
Data
).
length
===
0
?
''
:
JSON
.
stringify
(
jsonData
,
null
,
2
));
const
jsonObject
=
keyValueArrayToObject
(
keyValuePairs
);
setManualText
(
Object
.
keys
(
json
Object
).
length
===
0
?
''
:
JSON
.
stringify
(
jsonObject
,
null
,
2
));
setEditMode
(
'manual'
);
}
else
{
// 从手动模式切换到可视化模式,需要验证JSON
try
{
let
parsed
=
{};
if
(
manualText
&&
manualText
.
trim
())
{
...
...
@@ -160,177 +204,74 @@ const JSONEditor = ({
}
else
if
(
typeof
value
===
'object'
&&
value
!==
null
)
{
parsed
=
value
;
}
set
JsonData
(
parsed
);
set
KeyValuePairs
(
objectToKeyValueArray
(
parsed
)
);
setJsonError
(
''
);
setEditMode
(
'visual'
);
}
catch
(
error
)
{
setJsonError
(
error
.
message
);
// JSON格式错误时不切换模式
return
;
}
}
},
[
editMode
,
value
,
manualText
,
jsonData
]);
},
[
editMode
,
value
,
manualText
,
keyValuePairs
,
keyValueArrayToObject
,
objectToKeyValueArray
]);
// 添加键值对
const
addKeyValue
=
useCallback
(()
=>
{
const
new
Data
=
{
...
jsonData
}
;
const
keys
=
Object
.
keys
(
newData
);
const
new
Pairs
=
[...
keyValuePairs
]
;
const
existingKeys
=
newPairs
.
map
(
p
=>
p
.
key
);
let
counter
=
1
;
let
newKey
=
`field_
${
counter
}
`
;
while
(
newData
.
hasOwnProperty
(
newKey
))
{
while
(
existingKeys
.
includes
(
newKey
))
{
counter
+=
1
;
newKey
=
`field_
${
counter
}
`
;
}
newData
[
newKey
]
=
''
;
handleVisualChange
(
newData
);
},
[
jsonData
,
handleVisualChange
]);
newPairs
.
push
({
id
:
`
${
Date
.
now
()}
_
${
Math
.
random
()}
`
,
key
:
newKey
,
value
:
''
});
handleVisualChange
(
newPairs
);
},
[
keyValuePairs
,
handleVisualChange
]);
// 删除键值对
const
removeKeyValue
=
useCallback
((
keyToRemove
)
=>
{
const
newData
=
{
...
jsonData
};
delete
newData
[
keyToRemove
];
handleVisualChange
(
newData
);
},
[
jsonData
,
handleVisualChange
]);
const
removeKeyValue
=
useCallback
((
id
)
=>
{
const
newPairs
=
keyValuePairs
.
filter
(
pair
=>
pair
.
id
!==
id
);
handleVisualChange
(
newPairs
);
},
[
keyValuePairs
,
handleVisualChange
]);
// 更新键名
const
updateKey
=
useCallback
((
oldKey
,
newKey
)
=>
{
if
(
oldKey
===
newKey
||
!
newKey
)
return
;
const
newData
=
{};
Object
.
entries
(
jsonData
).
forEach
(([
k
,
v
])
=>
{
if
(
k
===
oldKey
)
{
newData
[
newKey
]
=
v
;
}
else
{
newData
[
k
]
=
v
;
}
});
handleVisualChange
(
newData
);
},
[
jsonData
,
handleVisualChange
]);
const
updateKey
=
useCallback
((
id
,
newKey
)
=>
{
const
newPairs
=
keyValuePairs
.
map
(
pair
=>
pair
.
id
===
id
?
{
...
pair
,
key
:
newKey
}
:
pair
);
handleVisualChange
(
newPairs
);
},
[
keyValuePairs
,
handleVisualChange
]);
// 更新值
const
updateValue
=
useCallback
((
key
,
newValue
)
=>
{
const
newData
=
{
...
jsonData
};
newData
[
key
]
=
newValue
;
handleVisualChange
(
newData
);
},
[
jsonData
,
handleVisualChange
]);
const
updateValue
=
useCallback
((
id
,
newValue
)
=>
{
const
newPairs
=
keyValuePairs
.
map
(
pair
=>
pair
.
id
===
id
?
{
...
pair
,
value
:
newValue
}
:
pair
);
handleVisualChange
(
newPairs
);
},
[
keyValuePairs
,
handleVisualChange
]);
// 填入模板
const
fillTemplate
=
useCallback
(()
=>
{
if
(
template
)
{
const
templateString
=
JSON
.
stringify
(
template
,
null
,
2
);
// 通过formApi设置值(如果提供的话)
if
(
formApi
&&
field
)
{
formApi
.
setValue
(
field
,
templateString
);
}
// 同步内部与外部值,避免出现杂字符
setManualText
(
templateString
);
set
JsonData
(
template
);
set
KeyValuePairs
(
objectToKeyValueArray
(
template
)
);
onChange
?.(
templateString
);
// 清除错误状态
setJsonError
(
''
);
}
},
[
template
,
onChange
,
editMode
,
formApi
,
field
]);
// 渲染键值对编辑器
const
renderKeyValueEditor
=
()
=>
{
if
(
typeof
jsonData
!==
'object'
||
jsonData
===
null
)
{
return
(
<
div
className
=
"text-center py-6 px-4"
>
<
div
className
=
"text-gray-400 mb-2"
>
<
IconCode
size
=
{
32
}
/
>
<
/div
>
<
Text
type
=
"tertiary"
className
=
"text-gray-500 text-sm"
>
{
t
(
'无效的JSON数据,请检查格式'
)}
<
/Text
>
<
/div
>
);
}
const
entries
=
Object
.
entries
(
jsonData
);
return
(
<
div
className
=
"space-y-1"
>
{
entries
.
length
===
0
&&
(
<
div
className
=
"text-center py-6 px-4"
>
<
Text
type
=
"tertiary"
className
=
"text-gray-500 text-sm"
>
{
t
(
'暂无数据,点击下方按钮添加键值对'
)}
<
/Text
>
<
/div
>
)}
},
[
template
,
onChange
,
formApi
,
field
,
objectToKeyValueArray
]);
{
entries
.
map
(([
key
,
value
],
index
)
=>
(
<
Row
key
=
{
index
}
gutter
=
{
8
}
align
=
"middle"
>
<
Col
span
=
{
6
}
>
<
Input
placeholder
=
{
t
(
'键名'
)}
value
=
{
key
}
onChange
=
{(
newKey
)
=>
updateKey
(
key
,
newKey
)}
/
>
<
/Col
>
<
Col
span
=
{
16
}
>
{
renderValueInput
(
key
,
value
)}
<
/Col
>
<
Col
span
=
{
2
}
>
<
Button
icon
=
{
<
IconDelete
/>
}
type
=
"danger"
theme
=
"borderless"
onClick
=
{()
=>
removeKeyValue
(
key
)}
style
=
{{
width
:
'100%'
}}
/
>
<
/Col
>
<
/Row
>
))}
<
div
className
=
"mt-2 flex justify-center"
>
<
Button
icon
=
{
<
IconPlus
/>
}
type
=
"primary"
theme
=
"outline"
onClick
=
{
addKeyValue
}
>
{
t
(
'添加键值对'
)}
<
/Button
>
<
/div
>
<
/div
>
);
};
// 添加嵌套对象
const
flattenObject
=
useCallback
((
parentKey
)
=>
{
const
newData
=
{
...
jsonData
};
let
primitive
=
''
;
const
obj
=
newData
[
parentKey
];
if
(
obj
&&
typeof
obj
===
'object'
)
{
const
firstKey
=
Object
.
keys
(
obj
)[
0
];
if
(
firstKey
!==
undefined
)
{
const
firstVal
=
obj
[
firstKey
];
if
(
typeof
firstVal
!==
'object'
)
primitive
=
firstVal
;
}
}
newData
[
parentKey
]
=
primitive
;
handleVisualChange
(
newData
);
},
[
jsonData
,
handleVisualChange
]);
const
addNestedObject
=
useCallback
((
parentKey
)
=>
{
const
newData
=
{
...
jsonData
};
if
(
typeof
newData
[
parentKey
]
!==
'object'
||
newData
[
parentKey
]
===
null
)
{
newData
[
parentKey
]
=
{};
}
const
existingKeys
=
Object
.
keys
(
newData
[
parentKey
]);
let
counter
=
1
;
let
newKey
=
`field_
${
counter
}
`
;
while
(
newData
[
parentKey
].
hasOwnProperty
(
newKey
))
{
counter
+=
1
;
newKey
=
`field_
${
counter
}
`
;
}
newData
[
parentKey
][
newKey
]
=
''
;
handleVisualChange
(
newData
);
},
[
jsonData
,
handleVisualChange
]);
// 渲染参数值输入控件(支持嵌套)
const
renderValueInput
=
(
key
,
value
)
=>
{
// 渲染值输入控件(支持嵌套)
const
renderValueInput
=
(
pairId
,
value
)
=>
{
const
valueType
=
typeof
value
;
if
(
valueType
===
'boolean'
)
{
...
...
@@ -338,7 +279,7 @@ const JSONEditor = ({
<
div
className
=
"flex items-center"
>
<
Switch
checked
=
{
value
}
onChange
=
{(
newValue
)
=>
updateValue
(
key
,
newValue
)}
onChange
=
{(
newValue
)
=>
updateValue
(
pairId
,
newValue
)}
/
>
<
Text
type
=
"tertiary"
className
=
"ml-2"
>
{
value
?
t
(
'true'
)
:
t
(
'false'
)}
...
...
@@ -351,195 +292,239 @@ const JSONEditor = ({
return
(
<
InputNumber
value
=
{
value
}
onChange
=
{(
newValue
)
=>
updateValue
(
key
,
newValue
)}
onChange
=
{(
newValue
)
=>
updateValue
(
pairId
,
newValue
)}
style
=
{{
width
:
'100%'
}}
step
=
{
key
===
'temperature'
?
0.1
:
1
}
precision
=
{
key
===
'temperature'
?
2
:
0
}
placeholder
=
{
t
(
'输入数字'
)}
/
>
);
}
if
(
valueType
===
'object'
&&
value
!==
null
)
{
// 渲染嵌套对象
const
entries
=
Object
.
entries
(
value
);
// 简化嵌套对象的处理,使用TextArea
return
(
<
Card
className
=
"!rounded-2xl"
>
{
entries
.
length
===
0
&&
(
<
Text
type
=
"tertiary"
className
=
"text-gray-500 text-xs"
>
{
t
(
'空对象,点击下方加号添加字段'
)}
<
TextArea
rows
=
{
2
}
value
=
{
JSON
.
stringify
(
value
,
null
,
2
)}
onChange
=
{(
txt
)
=>
{
try
{
const
obj
=
txt
.
trim
()
?
JSON
.
parse
(
txt
)
:
{};
updateValue
(
pairId
,
obj
);
}
catch
{
// 忽略解析错误
}
}}
placeholder
=
{
t
(
'输入JSON对象'
)}
/
>
);
}
// 字符串或其他原始类型
return
(
<
Input
placeholder
=
{
t
(
'参数值'
)}
value
=
{
String
(
value
)}
onChange
=
{(
newValue
)
=>
{
let
convertedValue
=
newValue
;
if
(
newValue
===
'true'
)
convertedValue
=
true
;
else
if
(
newValue
===
'false'
)
convertedValue
=
false
;
else
if
(
!
isNaN
(
newValue
)
&&
newValue
!==
''
&&
newValue
!==
'0'
)
{
const
num
=
Number
(
newValue
);
// 检查是否为整数
if
(
Number
.
isInteger
(
num
))
{
convertedValue
=
num
;
}
}
updateValue
(
pairId
,
convertedValue
);
}}
/
>
);
};
// 渲染键值对编辑器
const
renderKeyValueEditor
=
()
=>
{
return
(
<
div
className
=
"space-y-1"
>
{
/* 重复键警告 */
}
{
duplicateKeys
.
size
>
0
&&
(
<
Banner
type
=
"warning"
icon
=
{
<
IconAlertTriangle
/>
}
description
=
{
<
div
>
<
Text
strong
>
{
t
(
'存在重复的键名:'
)}
<
/Text
>
<
Text
>
{
Array
.
from
(
duplicateKeys
).
join
(
', '
)}
<
/Text
>
<
br
/>
<
Text
type
=
"tertiary"
size
=
"small"
>
{
t
(
'注意:JSON中重复的键只会保留最后一个同名键的值'
)}
<
/Text
>
<
/div
>
}
className
=
"mb-3"
/>
)}
{
keyValuePairs
.
length
===
0
&&
(
<
div
className
=
"text-center py-6 px-4"
>
<
Text
type
=
"tertiary"
className
=
"text-gray-500 text-sm"
>
{
t
(
'暂无数据,点击下方按钮添加键值对'
)}
<
/Text
>
)}
{
entries
.
map
(([
nestedKey
,
nestedValue
],
index
)
=>
(
<
Row
key
=
{
index
}
gutter
=
{
4
}
align
=
"middle"
className
=
"mb-1"
>
<
Col
span
=
{
8
}
>
<
Input
size
=
"small"
placeholder
=
{
t
(
'键名'
)}
value
=
{
nestedKey
}
onChange
=
{(
newKey
)
=>
{
const
newData
=
{
...
jsonData
};
const
oldValue
=
newData
[
key
][
nestedKey
];
delete
newData
[
key
][
nestedKey
];
newData
[
key
][
newKey
]
=
oldValue
;
handleVisualChange
(
newData
);
}}
/
>
<
/Col
>
<
Col
span
=
{
14
}
>
{
typeof
nestedValue
===
'object'
&&
nestedValue
!==
null
?
(
<
TextArea
size
=
"small"
rows
=
{
2
}
value
=
{
JSON
.
stringify
(
nestedValue
,
null
,
2
)}
onChange
=
{(
txt
)
=>
{
try
{
const
obj
=
txt
.
trim
()
?
JSON
.
parse
(
txt
)
:
{};
const
newData
=
{
...
jsonData
};
newData
[
key
][
nestedKey
]
=
obj
;
handleVisualChange
(
newData
);
}
catch
{
// ignore parse error
}
}}
/
>
)
:
(
<
/div
>
)}
{
keyValuePairs
.
map
((
pair
,
index
)
=>
{
const
isDuplicate
=
duplicateKeys
.
has
(
pair
.
key
);
const
isLastDuplicate
=
isDuplicate
&&
keyValuePairs
.
slice
(
index
+
1
).
every
(
p
=>
p
.
key
!==
pair
.
key
);
return
(
<
Row
key
=
{
pair
.
id
}
gutter
=
{
8
}
align
=
"middle"
>
<
Col
span
=
{
6
}
>
<
div
className
=
"relative"
>
<
Input
size
=
"small"
placeholder
=
{
t
(
'值'
)}
value
=
{
String
(
nestedValue
)}
onChange
=
{(
newValue
)
=>
{
const
newData
=
{
...
jsonData
};
let
convertedValue
=
newValue
;
if
(
newValue
===
'true'
)
convertedValue
=
true
;
else
if
(
newValue
===
'false'
)
convertedValue
=
false
;
else
if
(
!
isNaN
(
newValue
)
&&
newValue
!==
''
&&
newValue
!==
'0'
)
{
convertedValue
=
Number
(
newValue
);
}
newData
[
key
][
nestedKey
]
=
convertedValue
;
handleVisualChange
(
newData
);
}}
placeholder
=
{
t
(
'键名'
)}
value
=
{
pair
.
key
}
onChange
=
{(
newKey
)
=>
updateKey
(
pair
.
id
,
newKey
)}
status
=
{
isDuplicate
?
'warning'
:
undefined
}
/
>
)}
{
isDuplicate
&&
(
<
Tooltip
content
=
{
isLastDuplicate
?
t
(
'这是重复键中的最后一个,其值将被使用'
)
:
t
(
'重复的键名,此值将被后面的同名键覆盖'
)
}
>
<
IconAlertTriangle
className
=
"absolute right-2 top-1/2 transform -translate-y-1/2"
style
=
{{
color
:
isLastDuplicate
?
'#ff7d00'
:
'#faad14'
,
fontSize
:
'14px'
}}
/
>
<
/Tooltip
>
)}
<
/div
>
<
/Col
>
<
Col
span
=
{
16
}
>
{
renderValueInput
(
pair
.
id
,
pair
.
value
)}
<
/Col
>
<
Col
span
=
{
2
}
>
<
Button
size
=
"small"
icon
=
{
<
IconDelete
/>
}
type
=
"danger"
theme
=
"borderless"
onClick
=
{()
=>
{
const
newData
=
{
...
jsonData
};
delete
newData
[
key
][
nestedKey
];
handleVisualChange
(
newData
);
}}
onClick
=
{()
=>
removeKeyValue
(
pair
.
id
)}
style
=
{{
width
:
'100%'
}}
/
>
<
/Col
>
<
/Row
>
))}
<
div
className
=
"flex justify-center mt-1 gap-2"
>
<
Button
size
=
"small"
icon
=
{
<
IconPlus
/>
}
type
=
"tertiary"
onClick
=
{()
=>
addNestedObject
(
key
)}
>
{
t
(
'添加字段'
)}
<
/Button
>
<
Button
size
=
"small"
icon
=
{
<
IconRefresh
/>
}
type
=
"tertiary"
onClick
=
{()
=>
flattenObject
(
key
)}
>
{
t
(
'转换为值'
)}
<
/Button
>
<
/div
>
<
/Card
>
);
}
);
})}
// 字符串或其他原始类型
return
(
<
div
className
=
"flex items-center gap-1"
>
<
Input
placeholder
=
{
t
(
'参数值'
)}
value
=
{
String
(
value
)}
onChange
=
{(
newValue
)
=>
{
let
convertedValue
=
newValue
;
if
(
newValue
===
'true'
)
convertedValue
=
true
;
else
if
(
newValue
===
'false'
)
convertedValue
=
false
;
else
if
(
!
isNaN
(
newValue
)
&&
newValue
!==
''
&&
newValue
!==
'0'
)
{
convertedValue
=
Number
(
newValue
);
}
updateValue
(
key
,
convertedValue
);
}}
/
>
<
Button
icon
=
{
<
IconPlus
/>
}
type
=
"tertiary"
onClick
=
{()
=>
{
// 将当前值转换为对象
const
newData
=
{
...
jsonData
};
newData
[
key
]
=
{
'1'
:
value
};
handleVisualChange
(
newData
);
}}
title
=
{
t
(
'转换为对象'
)}
/
>
<
div
className
=
"mt-2 flex justify-center"
>
<
Button
icon
=
{
<
IconPlus
/>
}
type
=
"primary"
theme
=
"outline"
onClick
=
{
addKeyValue
}
>
{
t
(
'添加键值对'
)}
<
/Button
>
<
/div
>
<
/div
>
);
};
// 渲染区域编辑器(特殊格式)
// 渲染区域编辑器(特殊格式)
- 也需要改造以支持重复键
const
renderRegionEditor
=
()
=>
{
const
entries
=
Object
.
entries
(
jsonData
);
const
defaultEntry
=
entries
.
find
(([
key
])
=>
key
===
'default'
);
const
modelEntries
=
entries
.
filter
(([
key
])
=>
key
!==
'default'
);
const
defaultPair
=
keyValuePairs
.
find
(
pair
=>
pair
.
key
===
'default'
);
const
modelPairs
=
keyValuePairs
.
filter
(
pair
=>
pair
.
key
!==
'default'
);
return
(
<
div
className
=
"space-y-2"
>
{
/* 重复键警告 */
}
{
duplicateKeys
.
size
>
0
&&
(
<
Banner
type
=
"warning"
icon
=
{
<
IconAlertTriangle
/>
}
description
=
{
<
div
>
<
Text
strong
>
{
t
(
'存在重复的键名:'
)}
<
/Text
>
<
Text
>
{
Array
.
from
(
duplicateKeys
).
join
(
', '
)}
<
/Text
>
<
br
/>
<
Text
type
=
"tertiary"
size
=
"small"
>
{
t
(
'注意:JSON中重复的键只会保留最后一个同名键的值'
)}
<
/Text
>
<
/div
>
}
className
=
"mb-3"
/>
)}
{
/* 默认区域 */
}
<
Form
.
Slot
label
=
{
t
(
'默认区域'
)}
>
<
Input
placeholder
=
{
t
(
'默认区域,如: us-central1'
)}
value
=
{
defaultEntry
?
defaultEntry
[
1
]
:
''
}
onChange
=
{(
value
)
=>
updateValue
(
'default'
,
value
)}
value
=
{
defaultPair
?
defaultPair
.
value
:
''
}
onChange
=
{(
value
)
=>
{
if
(
defaultPair
)
{
updateValue
(
defaultPair
.
id
,
value
);
}
else
{
const
newPairs
=
[...
keyValuePairs
,
{
id
:
`
${
Date
.
now
()}
_
${
Math
.
random
()}
`
,
key
:
'default'
,
value
:
value
}];
handleVisualChange
(
newPairs
);
}
}}
/
>
<
/Form.Slot
>
{
/* 模型专用区域 */
}
<
Form
.
Slot
label
=
{
t
(
'模型专用区域'
)}
>
<
div
>
{
modelEntries
.
map
(([
modelName
,
region
],
index
)
=>
(
<
Row
key
=
{
index
}
gutter
=
{
8
}
align
=
"middle"
className
=
"mb-2"
>
<
Col
span
=
{
10
}
>
<
Input
placeholder
=
{
t
(
'模型名称'
)}
value
=
{
modelName
}
onChange
=
{(
newKey
)
=>
updateKey
(
modelName
,
newKey
)}
/
>
<
/Col
>
<
Col
span
=
{
12
}
>
<
Input
placeholder
=
{
t
(
'区域'
)}
value
=
{
region
}
onChange
=
{(
newValue
)
=>
updateValue
(
modelName
,
newValue
)}
/
>
<
/Col
>
<
Col
span
=
{
2
}
>
<
Button
icon
=
{
<
IconDelete
/>
}
type
=
"danger"
theme
=
"borderless"
onClick
=
{()
=>
removeKeyValue
(
modelName
)}
style
=
{{
width
:
'100%'
}}
/
>
<
/Col
>
<
/Row
>
))}
{
modelPairs
.
map
((
pair
)
=>
{
const
isDuplicate
=
duplicateKeys
.
has
(
pair
.
key
);
return
(
<
Row
key
=
{
pair
.
id
}
gutter
=
{
8
}
align
=
"middle"
className
=
"mb-2"
>
<
Col
span
=
{
10
}
>
<
div
className
=
"relative"
>
<
Input
placeholder
=
{
t
(
'模型名称'
)}
value
=
{
pair
.
key
}
onChange
=
{(
newKey
)
=>
updateKey
(
pair
.
id
,
newKey
)}
status
=
{
isDuplicate
?
'warning'
:
undefined
}
/
>
{
isDuplicate
&&
(
<
Tooltip
content
=
{
t
(
'重复的键名'
)}
>
<
IconAlertTriangle
className
=
"absolute right-2 top-1/2 transform -translate-y-1/2"
style
=
{{
color
:
'#faad14'
,
fontSize
:
'14px'
}}
/
>
<
/Tooltip
>
)}
<
/div
>
<
/Col
>
<
Col
span
=
{
12
}
>
<
Input
placeholder
=
{
t
(
'区域'
)}
value
=
{
pair
.
value
}
onChange
=
{(
newValue
)
=>
updateValue
(
pair
.
id
,
newValue
)}
/
>
<
/Col
>
<
Col
span
=
{
2
}
>
<
Button
icon
=
{
<
IconDelete
/>
}
type
=
"danger"
theme
=
"borderless"
onClick
=
{()
=>
removeKeyValue
(
pair
.
id
)}
style
=
{{
width
:
'100%'
}}
/
>
<
/Col
>
<
/Row
>
);
})}
<
div
className
=
"mt-2 flex justify-center"
>
<
Button
...
...
@@ -666,4 +651,4 @@ const JSONEditor = ({
);
};
export
default
JSONEditor
;
\ No newline at end of file
export
default
JSONEditor
;
\ No newline at end of file
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment