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
f0886c8a
authored
Feb 22, 2026
by
Seefs
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fix
parent
6b58648d
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
883 additions
and
444 deletions
+883
-444
web/src/components/table/channels/modals/ParamOverrideEditorModal.jsx
+883
-444
No files found.
web/src/components/table/channels/modals/ParamOverrideEditorModal.jsx
View file @
f0886c8a
...
@@ -20,10 +20,10 @@ For commercial licensing, please contact support@quantumnous.com
...
@@ -20,10 +20,10 @@ For commercial licensing, please contact support@quantumnous.com
import
React
,
{
useCallback
,
useEffect
,
useMemo
,
useState
}
from
'react'
;
import
React
,
{
useCallback
,
useEffect
,
useMemo
,
useState
}
from
'react'
;
import
{
useTranslation
}
from
'react-i18next'
;
import
{
useTranslation
}
from
'react-i18next'
;
import
{
import
{
Banner
,
Button
,
Button
,
Card
,
Card
,
Col
,
Col
,
Collapse
,
Input
,
Input
,
Modal
,
Modal
,
Row
,
Row
,
...
@@ -31,6 +31,7 @@ import {
...
@@ -31,6 +31,7 @@ import {
Space
,
Space
,
Switch
,
Switch
,
Tag
,
Tag
,
TextArea
,
Typography
,
Typography
,
}
from
'@douyinfe/semi-ui'
;
}
from
'@douyinfe/semi-ui'
;
import
{
IconDelete
,
IconPlus
}
from
'@douyinfe/semi-icons'
;
import
{
IconDelete
,
IconPlus
}
from
'@douyinfe/semi-icons'
;
...
@@ -204,6 +205,11 @@ const OPERATION_TEMPLATE = {
...
@@ -204,6 +205,11 @@ const OPERATION_TEMPLATE = {
],
],
};
};
const
TEMPLATE_LIBRARY_OPTIONS
=
[
{
label
:
'Template · Operations'
,
value
:
'operations'
},
{
label
:
'Template · Legacy Object'
,
value
:
'legacy'
},
];
let
localIdSeed
=
0
;
let
localIdSeed
=
0
;
const
nextLocalId
=
()
=>
`param_override_
${
Date
.
now
()}
_
${
localIdSeed
++
}
`
;
const
nextLocalId
=
()
=>
`param_override_
${
Date
.
now
()}
_
${
localIdSeed
++
}
`
;
...
@@ -274,6 +280,28 @@ const normalizeOperation = (operation = {}) => ({
...
@@ -274,6 +280,28 @@ const normalizeOperation = (operation = {}) => ({
const
createDefaultOperation
=
()
=>
normalizeOperation
({
mode
:
'set'
});
const
createDefaultOperation
=
()
=>
normalizeOperation
({
mode
:
'set'
});
const
getOperationSummary
=
(
operation
=
{},
index
=
0
)
=>
{
const
mode
=
operation
.
mode
||
'set'
;
if
(
mode
===
'sync_fields'
)
{
const
from
=
String
(
operation
.
from
||
''
).
trim
();
const
to
=
String
(
operation
.
to
||
''
).
trim
();
return
`
${
index
+
1
}
.
${
mode
}
·
${
from
||
to
||
'-'
}
`
;
}
const
path
=
String
(
operation
.
path
||
''
).
trim
();
const
from
=
String
(
operation
.
from
||
''
).
trim
();
const
to
=
String
(
operation
.
to
||
''
).
trim
();
return
`
${
index
+
1
}
.
${
mode
}
·
${
path
||
from
||
to
||
'-'
}
`
;
};
const
getOperationModeTagColor
=
(
mode
=
'set'
)
=>
{
if
(
mode
.
includes
(
'header'
))
return
'cyan'
;
if
(
mode
.
includes
(
'replace'
)
||
mode
.
includes
(
'trim'
))
return
'violet'
;
if
(
mode
.
includes
(
'copy'
)
||
mode
.
includes
(
'move'
))
return
'blue'
;
if
(
mode
.
includes
(
'error'
)
||
mode
.
includes
(
'prune'
))
return
'red'
;
if
(
mode
.
includes
(
'sync'
))
return
'green'
;
return
'grey'
;
};
const
parseInitialState
=
(
rawValue
)
=>
{
const
parseInitialState
=
(
rawValue
)
=>
{
const
text
=
typeof
rawValue
===
'string'
?
rawValue
:
''
;
const
text
=
typeof
rawValue
===
'string'
?
rawValue
:
''
;
const
trimmed
=
text
.
trim
();
const
trimmed
=
text
.
trim
();
...
@@ -439,6 +467,10 @@ const ParamOverrideEditorModal = ({ visible, value, onSave, onCancel }) => {
...
@@ -439,6 +467,10 @@ const ParamOverrideEditorModal = ({ visible, value, onSave, onCancel }) => {
const
[
operations
,
setOperations
]
=
useState
([
createDefaultOperation
()]);
const
[
operations
,
setOperations
]
=
useState
([
createDefaultOperation
()]);
const
[
jsonText
,
setJsonText
]
=
useState
(
''
);
const
[
jsonText
,
setJsonText
]
=
useState
(
''
);
const
[
jsonError
,
setJsonError
]
=
useState
(
''
);
const
[
jsonError
,
setJsonError
]
=
useState
(
''
);
const
[
operationSearch
,
setOperationSearch
]
=
useState
(
''
);
const
[
selectedOperationId
,
setSelectedOperationId
]
=
useState
(
''
);
const
[
expandedConditionMap
,
setExpandedConditionMap
]
=
useState
({});
const
[
templateLibraryKey
,
setTemplateLibraryKey
]
=
useState
(
'operations'
);
useEffect
(()
=>
{
useEffect
(()
=>
{
if
(
!
visible
)
return
;
if
(
!
visible
)
return
;
...
@@ -449,13 +481,73 @@ const ParamOverrideEditorModal = ({ visible, value, onSave, onCancel }) => {
...
@@ -449,13 +481,73 @@ const ParamOverrideEditorModal = ({ visible, value, onSave, onCancel }) => {
setOperations
(
nextState
.
operations
);
setOperations
(
nextState
.
operations
);
setJsonText
(
nextState
.
jsonText
);
setJsonText
(
nextState
.
jsonText
);
setJsonError
(
nextState
.
jsonError
);
setJsonError
(
nextState
.
jsonError
);
setOperationSearch
(
''
);
setSelectedOperationId
(
nextState
.
operations
[
0
]?.
id
||
''
);
setExpandedConditionMap
({});
setTemplateLibraryKey
(
nextState
.
visualMode
===
'legacy'
?
'legacy'
:
'operations'
,
);
},
[
visible
,
value
]);
},
[
visible
,
value
]);
useEffect
(()
=>
{
if
(
operations
.
length
===
0
)
{
setSelectedOperationId
(
''
);
return
;
}
if
(
!
operations
.
some
((
item
)
=>
item
.
id
===
selectedOperationId
))
{
setSelectedOperationId
(
operations
[
0
].
id
);
}
},
[
operations
,
selectedOperationId
]);
useEffect
(()
=>
{
setTemplateLibraryKey
(
visualMode
===
'legacy'
?
'legacy'
:
'operations'
);
},
[
visualMode
]);
const
operationCount
=
useMemo
(
const
operationCount
=
useMemo
(
()
=>
operations
.
filter
((
item
)
=>
!
isOperationBlank
(
item
)).
length
,
()
=>
operations
.
filter
((
item
)
=>
!
isOperationBlank
(
item
)).
length
,
[
operations
],
[
operations
],
);
);
const
filteredOperations
=
useMemo
(()
=>
{
const
keyword
=
operationSearch
.
trim
().
toLowerCase
();
if
(
!
keyword
)
return
operations
;
return
operations
.
filter
((
operation
)
=>
{
const
searchableText
=
[
operation
.
mode
,
operation
.
path
,
operation
.
from
,
operation
.
to
,
operation
.
value_text
,
]
.
filter
(
Boolean
)
.
join
(
' '
)
.
toLowerCase
();
return
searchableText
.
includes
(
keyword
);
});
},
[
operationSearch
,
operations
]);
const
selectedOperation
=
useMemo
(
()
=>
operations
.
find
((
operation
)
=>
operation
.
id
===
selectedOperationId
),
[
operations
,
selectedOperationId
],
);
const
selectedOperationIndex
=
useMemo
(
()
=>
operations
.
findIndex
((
operation
)
=>
operation
.
id
===
selectedOperationId
),
[
operations
,
selectedOperationId
],
);
const
topOperationModes
=
useMemo
(()
=>
{
const
counts
=
operations
.
reduce
((
acc
,
operation
)
=>
{
const
mode
=
operation
.
mode
||
'set'
;
acc
[
mode
]
=
(
acc
[
mode
]
||
0
)
+
1
;
return
acc
;
},
{});
return
Object
.
entries
(
counts
)
.
sort
((
a
,
b
)
=>
b
[
1
]
-
a
[
1
])
.
slice
(
0
,
4
);
},
[
operations
]);
const
buildVisualJson
=
useCallback
(()
=>
{
const
buildVisualJson
=
useCallback
(()
=>
{
if
(
visualMode
===
'legacy'
)
{
if
(
visualMode
===
'legacy'
)
{
const
trimmed
=
legacyValue
.
trim
();
const
trimmed
=
legacyValue
.
trim
();
...
@@ -545,8 +637,10 @@ const ParamOverrideEditorModal = ({ visible, value, onSave, onCancel }) => {
...
@@ -545,8 +637,10 @@ const ParamOverrideEditorModal = ({ visible, value, onSave, onCancel }) => {
if
(
editMode
===
'visual'
)
return
;
if
(
editMode
===
'visual'
)
return
;
const
trimmed
=
jsonText
.
trim
();
const
trimmed
=
jsonText
.
trim
();
if
(
!
trimmed
)
{
if
(
!
trimmed
)
{
const
fallback
=
createDefaultOperation
();
setVisualMode
(
'operations'
);
setVisualMode
(
'operations'
);
setOperations
([
createDefaultOperation
()]);
setOperations
([
fallback
]);
setSelectedOperationId
(
fallback
.
id
);
setLegacyValue
(
''
);
setLegacyValue
(
''
);
setJsonError
(
''
);
setJsonError
(
''
);
setEditMode
(
'visual'
);
setEditMode
(
'visual'
);
...
@@ -563,21 +657,24 @@ const ParamOverrideEditorModal = ({ visible, value, onSave, onCancel }) => {
...
@@ -563,21 +657,24 @@ const ParamOverrideEditorModal = ({ visible, value, onSave, onCancel }) => {
!
Array
.
isArray
(
parsed
)
&&
!
Array
.
isArray
(
parsed
)
&&
Array
.
isArray
(
parsed
.
operations
)
Array
.
isArray
(
parsed
.
operations
)
)
{
)
{
setVisualMode
(
'operations'
);
const
nextOperations
=
setOperations
(
parsed
.
operations
.
length
>
0
parsed
.
operations
.
length
>
0
?
parsed
.
operations
.
map
(
normalizeOperation
)
?
parsed
.
operations
.
map
(
normalizeOperation
)
:
[
createDefaultOperation
()],
:
[
createDefaultOperation
()];
);
setVisualMode
(
'operations'
);
setOperations
(
nextOperations
);
setSelectedOperationId
(
nextOperations
[
0
]?.
id
||
''
);
setLegacyValue
(
''
);
setLegacyValue
(
''
);
setJsonError
(
''
);
setJsonError
(
''
);
setEditMode
(
'visual'
);
setEditMode
(
'visual'
);
return
;
return
;
}
}
if
(
parsed
&&
typeof
parsed
===
'object'
&&
!
Array
.
isArray
(
parsed
))
{
if
(
parsed
&&
typeof
parsed
===
'object'
&&
!
Array
.
isArray
(
parsed
))
{
const
fallback
=
createDefaultOperation
();
setVisualMode
(
'legacy'
);
setVisualMode
(
'legacy'
);
setLegacyValue
(
JSON
.
stringify
(
parsed
,
null
,
2
));
setLegacyValue
(
JSON
.
stringify
(
parsed
,
null
,
2
));
setOperations
([
createDefaultOperation
()]);
setOperations
([
fallback
]);
setSelectedOperationId
(
fallback
.
id
);
setJsonError
(
''
);
setJsonError
(
''
);
setEditMode
(
'visual'
);
setEditMode
(
'visual'
);
return
;
return
;
...
@@ -587,27 +684,54 @@ const ParamOverrideEditorModal = ({ visible, value, onSave, onCancel }) => {
...
@@ -587,27 +684,54 @@ const ParamOverrideEditorModal = ({ visible, value, onSave, onCancel }) => {
const
setOldTemplate
=
()
=>
{
const
setOldTemplate
=
()
=>
{
const
text
=
JSON
.
stringify
(
LEGACY_TEMPLATE
,
null
,
2
);
const
text
=
JSON
.
stringify
(
LEGACY_TEMPLATE
,
null
,
2
);
const
fallback
=
createDefaultOperation
();
setVisualMode
(
'legacy'
);
setVisualMode
(
'legacy'
);
setLegacyValue
(
text
);
setLegacyValue
(
text
);
setOperations
([
fallback
]);
setSelectedOperationId
(
fallback
.
id
);
setExpandedConditionMap
({});
setJsonText
(
text
);
setJsonText
(
text
);
setJsonError
(
''
);
setJsonError
(
''
);
setEditMode
(
'visual'
);
setEditMode
(
'visual'
);
setTemplateLibraryKey
(
'legacy'
);
};
};
const
setNewTemplate
=
()
=>
{
const
setNewTemplate
=
()
=>
{
const
nextOperations
=
OPERATION_TEMPLATE
.
operations
.
map
(
normalizeOperation
);
setVisualMode
(
'operations'
);
setVisualMode
(
'operations'
);
setOperations
(
OPERATION_TEMPLATE
.
operations
.
map
(
normalizeOperation
));
setOperations
(
nextOperations
);
setSelectedOperationId
(
nextOperations
[
0
]?.
id
||
''
);
setExpandedConditionMap
({});
setJsonText
(
JSON
.
stringify
(
OPERATION_TEMPLATE
,
null
,
2
));
setJsonText
(
JSON
.
stringify
(
OPERATION_TEMPLATE
,
null
,
2
));
setJsonError
(
''
);
setJsonError
(
''
);
setEditMode
(
'visual'
);
setEditMode
(
'visual'
);
setTemplateLibraryKey
(
'operations'
);
};
};
const
clearValue
=
()
=>
{
const
clearValue
=
()
=>
{
const
fallback
=
createDefaultOperation
();
setVisualMode
(
'operations'
);
setVisualMode
(
'operations'
);
setLegacyValue
(
''
);
setLegacyValue
(
''
);
setOperations
([
createDefaultOperation
()]);
setOperations
([
fallback
]);
setSelectedOperationId
(
fallback
.
id
);
setExpandedConditionMap
({});
setJsonText
(
''
);
setJsonText
(
''
);
setJsonError
(
''
);
setJsonError
(
''
);
setTemplateLibraryKey
(
'operations'
);
};
const
applyTemplateFromLibrary
=
()
=>
{
if
(
templateLibraryKey
===
'legacy'
)
{
setOldTemplate
();
return
;
}
setNewTemplate
();
};
const
resetEditorState
=
()
=>
{
clearValue
();
setEditMode
(
'visual'
);
};
};
const
updateOperation
=
(
operationId
,
patch
)
=>
{
const
updateOperation
=
(
operationId
,
patch
)
=>
{
...
@@ -619,10 +743,13 @@ const ParamOverrideEditorModal = ({ visible, value, onSave, onCancel }) => {
...
@@ -619,10 +743,13 @@ const ParamOverrideEditorModal = ({ visible, value, onSave, onCancel }) => {
};
};
const
addOperation
=
()
=>
{
const
addOperation
=
()
=>
{
setOperations
((
prev
)
=>
[...
prev
,
createDefaultOperation
()]);
const
created
=
createDefaultOperation
();
setOperations
((
prev
)
=>
[...
prev
,
created
]);
setSelectedOperationId
(
created
.
id
);
};
};
const
duplicateOperation
=
(
operationId
)
=>
{
const
duplicateOperation
=
(
operationId
)
=>
{
let
insertedId
=
''
;
setOperations
((
prev
)
=>
{
setOperations
((
prev
)
=>
{
const
index
=
prev
.
findIndex
((
item
)
=>
item
.
id
===
operationId
);
const
index
=
prev
.
findIndex
((
item
)
=>
item
.
id
===
operationId
);
if
(
index
<
0
)
return
prev
;
if
(
index
<
0
)
return
prev
;
...
@@ -643,10 +770,14 @@ const ParamOverrideEditorModal = ({ visible, value, onSave, onCancel }) => {
...
@@ -643,10 +770,14 @@ const ParamOverrideEditorModal = ({ visible, value, onSave, onCancel }) => {
pass_missing_key
:
condition
.
pass_missing_key
,
pass_missing_key
:
condition
.
pass_missing_key
,
})),
})),
});
});
insertedId
=
cloned
.
id
;
const
next
=
[...
prev
];
const
next
=
[...
prev
];
next
.
splice
(
index
+
1
,
0
,
cloned
);
next
.
splice
(
index
+
1
,
0
,
cloned
);
return
next
;
return
next
;
});
});
if
(
insertedId
)
{
setSelectedOperationId
(
insertedId
);
}
};
};
const
removeOperation
=
(
operationId
)
=>
{
const
removeOperation
=
(
operationId
)
=>
{
...
@@ -654,19 +785,32 @@ const ParamOverrideEditorModal = ({ visible, value, onSave, onCancel }) => {
...
@@ -654,19 +785,32 @@ const ParamOverrideEditorModal = ({ visible, value, onSave, onCancel }) => {
if
(
prev
.
length
<=
1
)
return
[
createDefaultOperation
()];
if
(
prev
.
length
<=
1
)
return
[
createDefaultOperation
()];
return
prev
.
filter
((
item
)
=>
item
.
id
!==
operationId
);
return
prev
.
filter
((
item
)
=>
item
.
id
!==
operationId
);
});
});
setExpandedConditionMap
((
prev
)
=>
{
if
(
!
Object
.
prototype
.
hasOwnProperty
.
call
(
prev
,
operationId
))
{
return
prev
;
}
const
next
=
{
...
prev
};
delete
next
[
operationId
];
return
next
;
});
};
};
const
addCondition
=
(
operationId
)
=>
{
const
addCondition
=
(
operationId
)
=>
{
const
createdCondition
=
createDefaultCondition
();
setOperations
((
prev
)
=>
setOperations
((
prev
)
=>
prev
.
map
((
operation
)
=>
prev
.
map
((
operation
)
=>
operation
.
id
===
operationId
operation
.
id
===
operationId
?
{
?
{
...
operation
,
...
operation
,
conditions
:
[...(
operation
.
conditions
||
[]),
create
DefaultCondition
()
],
conditions
:
[...(
operation
.
conditions
||
[]),
create
dCondition
],
}
}
:
operation
,
:
operation
,
),
),
);
);
setExpandedConditionMap
((
prev
)
=>
({
...
prev
,
[
operationId
]:
[...(
prev
[
operationId
]
||
[]),
createdCondition
.
id
],
}));
};
};
const
updateCondition
=
(
operationId
,
conditionId
,
patch
)
=>
{
const
updateCondition
=
(
operationId
,
conditionId
,
patch
)
=>
{
...
@@ -697,8 +841,50 @@ const ParamOverrideEditorModal = ({ visible, value, onSave, onCancel }) => {
...
@@ -697,8 +841,50 @@ const ParamOverrideEditorModal = ({ visible, value, onSave, onCancel }) => {
};
};
}),
}),
);
);
setExpandedConditionMap
((
prev
)
=>
({
...
prev
,
[
operationId
]:
(
prev
[
operationId
]
||
[]).
filter
(
(
id
)
=>
id
!==
conditionId
,
),
}));
};
};
const
selectedConditionKeys
=
useMemo
(
()
=>
expandedConditionMap
[
selectedOperationId
]
||
[],
[
expandedConditionMap
,
selectedOperationId
],
);
const
handleConditionCollapseChange
=
useCallback
(
(
operationId
,
activeKeys
)
=>
{
const
keys
=
(
Array
.
isArray
(
activeKeys
)
?
activeKeys
:
[
activeKeys
]
).
filter
(
Boolean
);
setExpandedConditionMap
((
prev
)
=>
({
...
prev
,
[
operationId
]:
keys
,
}));
},
[],
);
const
expandAllSelectedConditions
=
useCallback
(()
=>
{
if
(
!
selectedOperationId
||
!
selectedOperation
)
return
;
setExpandedConditionMap
((
prev
)
=>
({
...
prev
,
[
selectedOperationId
]:
(
selectedOperation
.
conditions
||
[]).
map
(
(
condition
)
=>
condition
.
id
,
),
}));
},
[
selectedOperation
,
selectedOperationId
]);
const
collapseAllSelectedConditions
=
useCallback
(()
=>
{
if
(
!
selectedOperationId
)
return
;
setExpandedConditionMap
((
prev
)
=>
({
...
prev
,
[
selectedOperationId
]:
[],
}));
},
[
selectedOperationId
]);
const
handleJsonChange
=
(
nextValue
)
=>
{
const
handleJsonChange
=
(
nextValue
)
=>
{
setJsonText
(
nextValue
);
setJsonText
(
nextValue
);
const
trimmed
=
String
(
nextValue
||
''
).
trim
();
const
trimmed
=
String
(
nextValue
||
''
).
trim
();
...
@@ -761,21 +947,13 @@ const ParamOverrideEditorModal = ({ visible, value, onSave, onCancel }) => {
...
@@ -761,21 +947,13 @@ const ParamOverrideEditorModal = ({ visible, value, onSave, onCancel }) => {
<
Modal
<
Modal
title=
{
t
(
'参数覆盖'
)
}
title=
{
t
(
'参数覆盖'
)
}
visible=
{
visible
}
visible=
{
visible
}
width=
{
98
0
}
width=
{
112
0
}
onCancel=
{
onCancel
}
onCancel=
{
onCancel
}
onOk=
{
handleSave
}
onOk=
{
handleSave
}
okText=
{
t
(
'保存'
)
}
okText=
{
t
(
'保存'
)
}
cancelText=
{
t
(
'取消'
)
}
cancelText=
{
t
(
'取消'
)
}
>
>
<
Space
vertical
align=
'start'
spacing=
{
12
}
style=
{
{
width
:
'100%'
}
}
>
<
Space
vertical
align=
'start'
spacing=
{
12
}
style=
{
{
width
:
'100%'
}
}
>
<
Banner
fullMode=
{
false
}
type=
'info'
description=
{
t
(
'支持旧格式直接覆盖,也支持新格式 operations 条件编辑;可在可视化和 JSON 之间双向切换。'
,
)
}
/>
<
Space
wrap
>
<
Space
wrap
>
<
Button
<
Button
type=
{
editMode
===
'visual'
?
'primary'
:
'tertiary'
}
type=
{
editMode
===
'visual'
?
'primary'
:
'tertiary'
}
...
@@ -789,32 +967,24 @@ const ParamOverrideEditorModal = ({ visible, value, onSave, onCancel }) => {
...
@@ -789,32 +967,24 @@ const ParamOverrideEditorModal = ({ visible, value, onSave, onCancel }) => {
>
>
{
t
(
'JSON 模式'
)
}
{
t
(
'JSON 模式'
)
}
</
Button
>
</
Button
>
<
Button
onClick=
{
setOldTemplate
}
>
{
t
(
'旧格式模板'
)
}
</
Button
>
<
Select
<
Button
onClick=
{
setNewTemplate
}
>
{
t
(
'新格式模板'
)
}
</
Button
>
value=
{
templateLibraryKey
}
<
Button
onClick=
{
clearValue
}
>
{
t
(
'不更改'
)
}
</
Button
>
optionList=
{
TEMPLATE_LIBRARY_OPTIONS
}
onChange=
{
(
nextValue
)
=>
setTemplateLibraryKey
(
nextValue
||
'operations'
)
}
style=
{
{
width
:
240
}
}
/>
<
Button
onClick=
{
applyTemplateFromLibrary
}
>
{
t
(
'应用模板'
)
}
</
Button
>
<
Button
onClick=
{
resetEditorState
}
>
{
t
(
'重置'
)
}
</
Button
>
</
Space
>
</
Space
>
{
editMode
===
'visual'
?
(
{
editMode
===
'visual'
?
(
<
div
style=
{
{
width
:
'100%'
}
}
>
<
div
style=
{
{
width
:
'100%'
}
}
>
<
Space
wrap
style=
{
{
marginBottom
:
12
}
}
>
<
Button
type=
{
visualMode
===
'operations'
?
'primary'
:
'tertiary'
}
onClick=
{
()
=>
setVisualMode
(
'operations'
)
}
>
{
t
(
'新格式模板'
)
}
</
Button
>
<
Button
type=
{
visualMode
===
'legacy'
?
'primary'
:
'tertiary'
}
onClick=
{
()
=>
setVisualMode
(
'legacy'
)
}
>
{
t
(
'旧格式模板'
)
}
</
Button
>
</
Space
>
{
visualMode
===
'legacy'
?
(
{
visualMode
===
'legacy'
?
(
<
div
>
<
div
>
<
Text
className=
'mb-2 block'
>
{
t
(
'旧格式(直接覆盖):'
)
}
</
Text
>
<
Text
className=
'mb-2 block'
>
{
t
(
'旧格式(直接覆盖):'
)
}
</
Text
>
<
Input
.
TextArea
<
TextArea
value=
{
legacyValue
}
value=
{
legacyValue
}
autosize=
{
{
minRows
:
10
,
maxRows
:
20
}
}
autosize=
{
{
minRows
:
10
,
maxRows
:
20
}
}
placeholder=
{
JSON
.
stringify
(
LEGACY_TEMPLATE
,
null
,
2
)
}
placeholder=
{
JSON
.
stringify
(
LEGACY_TEMPLATE
,
null
,
2
)
}
...
@@ -830,462 +1000,731 @@ const ParamOverrideEditorModal = ({ visible, value, onSave, onCancel }) => {
...
@@ -830,462 +1000,731 @@ const ParamOverrideEditorModal = ({ visible, value, onSave, onCancel }) => {
<
div
className=
'flex items-center justify-between mb-3'
>
<
div
className=
'flex items-center justify-between mb-3'
>
<
Space
>
<
Space
>
<
Text
>
{
t
(
'新格式(支持条件判断与json自定义):'
)
}
</
Text
>
<
Text
>
{
t
(
'新格式(支持条件判断与json自定义):'
)
}
</
Text
>
<
Tag
color=
'cyan'
>
<
Tag
color=
'cyan'
>
{
`${t('规则')}: ${operationCount}`
}
</
Tag
>
{
`${t('规则')}: ${operationCount}`
}
</
Tag
>
</
Space
>
</
Space
>
<
Button
icon=
{
<
IconPlus
/>
}
onClick=
{
addOperation
}
>
<
Button
icon=
{
<
IconPlus
/>
}
onClick=
{
addOperation
}
>
{
t
(
'新增规则'
)
}
{
t
(
'新增规则'
)
}
</
Button
>
</
Button
>
</
div
>
</
div
>
<
Space
vertical
spacing=
{
8
}
style=
{
{
width
:
'100%'
}
}
>
<
Row
gutter=
{
12
}
>
{
operations
.
map
((
operation
,
index
)
=>
{
<
Col
xs=
{
24
}
md=
{
8
}
>
const
mode
=
operation
.
mode
||
'set'
;
<
Card
const
meta
=
MODE_META
[
mode
]
||
MODE_META
.
set
;
className=
'!rounded-2xl !border-0 h-full'
const
conditions
=
operation
.
conditions
||
[];
bodyStyle=
{
{
const
syncFromTarget
=
padding
:
12
,
mode
===
'sync_fields'
background
:
'var(--semi-color-fill-0)'
,
?
parseSyncTargetSpec
(
operation
.
from
)
display
:
'flex'
,
:
null
;
flexDirection
:
'column'
,
const
syncToTarget
=
gap
:
10
,
mode
===
'sync_fields'
minHeight
:
520
,
?
parseSyncTargetSpec
(
operation
.
to
)
}
}
:
null
;
>
return
(
<
div
className=
'flex items-center justify-between'
>
<
Card
key=
{
operation
.
id
}
className=
'!rounded-xl border'
>
<
Text
strong
>
{
t
(
'规则导航'
)
}
</
Text
>
<
div
className=
'flex items-center justify-between mb-2'
>
<
Tag
color=
'grey'
>
{
`${operationCount}/${operations.length}`
}
</
Tag
>
<
Space
>
</
div
>
<
Tag
>
{
`#${index + 1}`
}
</
Tag
>
<
Text
>
{
mode
}
</
Text
>
{
topOperationModes
.
length
>
0
?
(
</
Space
>
<
Space
wrap
spacing=
{
6
}
>
<
Space
>
{
topOperationModes
.
map
(([
mode
,
count
])
=>
(
<
Button
<
Tag
key=
{
`mode_stat_${mode}`
}
size=
'small'
size=
'small'
type=
'tertiary'
color=
{
getOperationModeTagColor
(
mode
)
}
onClick=
{
()
=>
duplicateOperation
(
operation
.
id
)
}
>
>
{
t
(
'复制'
)
}
{
`${mode} · ${count}`
}
</
Button
>
</
Tag
>
<
Button
))
}
theme=
'borderless'
</
Space
>
type=
'danger'
)
:
null
}
icon=
{
<
IconDelete
/>
}
onClick=
{
()
=>
removeOperation
(
operation
.
id
)
}
<
Input
/>
value=
{
operationSearch
}
</
Space
>
placeholder=
{
t
(
'搜索规则(mode/path/from/to)'
)
}
</
div
>
onChange=
{
(
nextValue
)
=>
setOperationSearch
(
nextValue
||
''
)
<
Row
gutter=
{
12
}
>
}
<
Col
xs=
{
24
}
md=
{
8
}
>
showClear
<
Text
type=
'tertiary'
size=
'small'
>
/>
mode
</
Text
>
<
div
<
Select
className=
'overflow-auto'
value=
{
mode
}
style=
{
{
flex
:
1
,
minHeight
:
320
,
paddingRight
:
2
}
}
optionList=
{
OPERATION_MODE_OPTIONS
}
>
onChange=
{
(
nextMode
)
=>
{
filteredOperations
.
length
===
0
?
(
updateOperation
(
operation
.
id
,
{
mode
:
nextMode
})
<
Text
type=
'tertiary'
size=
'small'
>
}
{
t
(
'没有匹配的规则'
)
}
style=
{
{
width
:
'100%'
}
}
</
Text
>
/>
)
:
(
</
Col
>
<
div
{
meta
.
path
||
meta
.
pathOptional
?
(
style=
{
{
<
Col
xs=
{
24
}
md=
{
16
}
>
display
:
'flex'
,
<
Text
type=
'tertiary'
size=
'small'
>
flexDirection
:
'column'
,
{
meta
.
pathOptional
?
'path (optional)'
:
'path'
}
gap
:
8
,
</
Text
>
width
:
'100%'
,
<
Input
}
}
value=
{
operation
.
path
}
>
placeholder=
{
{
filteredOperations
.
map
((
operation
)
=>
{
mode
.
includes
(
'header'
)
const
index
=
operations
.
findIndex
(
?
'X-Debug-Mode'
(
item
)
=>
item
.
id
===
operation
.
id
,
:
mode
===
'prune_objects'
);
?
'messages (optional)'
const
isActive
=
:
'temperature'
operation
.
id
===
selectedOperationId
;
}
return
(
onChange=
{
(
nextValue
)
=>
<
div
updateOperation
(
operation
.
id
,
{
key=
{
operation
.
id
}
path
:
nextValue
,
role=
'button'
})
tabIndex=
{
0
}
}
onClick=
{
()
=>
/>
setSelectedOperationId
(
operation
.
id
)
<
Space
wrap
style=
{
{
marginTop
:
6
}
}
>
}
{
OPERATION_PATH_SUGGESTIONS
.
map
((
pathItem
)
=>
(
onKeyDown=
{
(
event
)
=>
{
<
Tag
if
(
key=
{
`${operation.id}_${pathItem}`
}
event
.
key
===
'Enter'
||
size=
'small'
event
.
key
===
' '
color=
'grey'
)
{
className=
'cursor-pointer'
event
.
preventDefault
();
onClick=
{
()
=>
setSelectedOperationId
(
operation
.
id
);
updateOperation
(
operation
.
id
,
{
path
:
pathItem
,
})
}
}
>
}
}
{
pathItem
}
className=
'w-full rounded-xl px-3 py-3 cursor-pointer transition-colors'
</
Tag
>
style=
{
{
))
}
background
:
isActive
</
Space
>
?
'var(--semi-color-primary-light-default)'
</
Col
>
:
'var(--semi-color-bg-2)'
,
)
:
null
}
border
:
isActive
</
Row
>
?
'1px solid var(--semi-color-primary)'
<
Text
type=
'tertiary'
size=
'small'
className=
'mt-1 block'
>
:
'1px solid var(--semi-color-border)'
,
{
MODE_DESCRIPTIONS
[
mode
]
||
''
}
}
}
</
Text
>
>
<
div
className=
'flex items-start justify-between gap-2'
>
{
meta
.
value
?
(
<
div
>
<
div
className=
'mt-2'
>
<
Text
strong
>
{
`#${index + 1}`
}
</
Text
>
<
Text
type=
'tertiary'
size=
'small'
>
<
Text
value (JSON or plain text)
type=
'tertiary'
</
Text
>
size=
'small'
<
Input
.
TextArea
className=
'block mt-1'
value=
{
operation
.
value_text
}
>
autosize=
{
{
minRows
:
1
,
maxRows
:
4
}
}
{
getOperationSummary
(
operation
,
index
)
}
placeholder=
'0.7'
</
Text
>
onChange=
{
(
nextValue
)
=>
</
div
>
updateOperation
(
operation
.
id
,
{
<
Tag
size=
'small'
color=
'grey'
>
value_text
:
nextValue
,
{
(
operation
.
conditions
||
[]).
length
}
})
</
Tag
>
}
</
div
>
/>
<
Space
spacing=
{
6
}
style=
{
{
marginTop
:
8
}
}
>
</
div
>
<
Tag
)
:
null
}
size=
'small'
color=
{
getOperationModeTagColor
(
{
meta
.
keepOrigin
?
(
operation
.
mode
||
'set'
,
<
div
className=
'mt-2'
>
)
}
<
Switch
>
checked=
{
operation
.
keep_origin
}
{
operation
.
mode
||
'set'
}
checkedText=
{
t
(
'开'
)
}
</
Tag
>
uncheckedText=
{
t
(
'关'
)
}
<
Text
type=
'tertiary'
size=
'small'
>
onChange=
{
(
nextValue
)
=>
{
t
(
'条件'
)
}
updateOperation
(
operation
.
id
,
{
</
Text
>
keep_origin
:
nextValue
,
</
Space
>
})
</
div
>
}
);
/>
})
}
<
Text
type=
'tertiary'
size=
'small'
className=
'ml-2'
>
keep_origin
</
Text
>
</
div
>
</
div
>
)
:
null
}
)
}
</
div
>
</
Card
>
</
Col
>
<
Col
xs=
{
24
}
md=
{
16
}
>
{
selectedOperation
?
(
(()
=>
{
const
mode
=
selectedOperation
.
mode
||
'set'
;
const
meta
=
MODE_META
[
mode
]
||
MODE_META
.
set
;
const
conditions
=
selectedOperation
.
conditions
||
[];
const
syncFromTarget
=
mode
===
'sync_fields'
?
parseSyncTargetSpec
(
selectedOperation
.
from
)
:
null
;
const
syncToTarget
=
mode
===
'sync_fields'
?
parseSyncTargetSpec
(
selectedOperation
.
to
)
:
null
;
return
(
<
Card
className=
'!rounded-2xl !border-0'
bodyStyle=
{
{
padding
:
14
,
background
:
'var(--semi-color-fill-0)'
,
}
}
>
<
div
className=
'flex items-center justify-between mb-3'
>
<
Space
>
<
Tag
color=
'blue'
>
{
`#${selectedOperationIndex + 1}`
}
</
Tag
>
<
Text
strong
>
{
getOperationSummary
(
selectedOperation
,
selectedOperationIndex
,
)
}
</
Text
>
</
Space
>
<
Space
>
<
Button
size=
'small'
type=
'tertiary'
onClick=
{
()
=>
duplicateOperation
(
selectedOperation
.
id
)
}
>
{
t
(
'复制'
)
}
</
Button
>
<
Button
size=
'small'
type=
'danger'
theme=
'borderless'
icon=
{
<
IconDelete
/>
}
onClick=
{
()
=>
removeOperation
(
selectedOperation
.
id
)
}
/>
</
Space
>
</
div
>
{
mode
===
'sync_fields'
?
(
<
Row
gutter=
{
12
}
>
<
div
className=
'mt-2'
>
<
Col
xs=
{
24
}
md=
{
8
}
>
<
Text
type=
'tertiary'
size=
'small'
>
sync endpoints
</
Text
>
<
Row
gutter=
{
12
}
style=
{
{
marginTop
:
6
}
}
>
<
Col
xs=
{
24
}
md=
{
12
}
>
<
Text
type=
'tertiary'
size=
'small'
>
<
Text
type=
'tertiary'
size=
'small'
>
from endpoint
mode
</
Text
>
</
Text
>
<
div
className=
'flex gap-2'
>
<
Select
<
Select
value=
{
mode
}
value=
{
syncFromTarget
?.
type
||
'json'
}
optionList=
{
OPERATION_MODE_OPTIONS
}
optionList=
{
SYNC_TARGET_TYPE_OPTIONS
}
onChange=
{
(
nextMode
)
=>
style=
{
{
width
:
120
}
}
updateOperation
(
selectedOperation
.
id
,
{
onChange=
{
(
nextType
)
=>
mode
:
nextMode
,
updateOperation
(
operation
.
id
,
{
})
from
:
buildSyncTargetSpec
(
}
nextType
,
style=
{
{
width
:
'100%'
}
}
syncFromTarget
?.
key
||
''
,
/>
),
})
}
/>
<
Input
value=
{
syncFromTarget
?.
key
||
''
}
placeholder=
'session_id'
onChange=
{
(
nextKey
)
=>
updateOperation
(
operation
.
id
,
{
from
:
buildSyncTargetSpec
(
syncFromTarget
?.
type
||
'json'
,
nextKey
,
),
})
}
/>
</
div
>
</
Col
>
</
Col
>
<
Col
xs=
{
24
}
md=
{
12
}
>
{
meta
.
path
||
meta
.
pathOptional
?
(
<
Text
type=
'tertiary'
size=
'small'
>
<
Col
xs=
{
24
}
md=
{
16
}
>
to endpoint
<
Text
type=
'tertiary'
size=
'small'
>
</
Text
>
{
meta
.
pathOptional
<
div
className=
'flex gap-2'
>
?
'path (optional)'
<
Select
:
'path'
}
value=
{
syncToTarget
?.
type
||
'json'
}
</
Text
>
optionList=
{
SYNC_TARGET_TYPE_OPTIONS
}
style=
{
{
width
:
120
}
}
onChange=
{
(
nextType
)
=>
updateOperation
(
operation
.
id
,
{
to
:
buildSyncTargetSpec
(
nextType
,
syncToTarget
?.
key
||
''
,
),
})
}
/>
<
Input
<
Input
value=
{
syncToTarget
?.
key
||
''
}
value=
{
selectedOperation
.
path
}
placeholder=
'prompt_cache_key'
placeholder=
{
onChange=
{
(
nextKey
)
=>
mode
.
includes
(
'header'
)
updateOperation
(
operation
.
id
,
{
?
'X-Debug-Mode'
to
:
buildSyncTargetSpec
(
:
mode
===
'prune_objects'
syncToTarget
?.
type
||
'json'
,
?
'messages (optional)'
nextKey
,
:
'temperature'
),
}
onChange=
{
(
nextValue
)
=>
updateOperation
(
selectedOperation
.
id
,
{
path
:
nextValue
,
})
})
}
}
/>
/>
</
div
>
<
Space
wrap
style=
{
{
marginTop
:
6
}
}
>
</
Col
>
{
OPERATION_PATH_SUGGESTIONS
.
map
(
(
pathItem
)
=>
(
<
Tag
key=
{
`${selectedOperation.id}_${pathItem}`
}
size=
'small'
color=
'grey'
className=
'cursor-pointer'
onClick=
{
()
=>
updateOperation
(
selectedOperation
.
id
,
{
path
:
pathItem
,
},
)
}
>
{
pathItem
}
</
Tag
>
),
)
}
</
Space
>
</
Col
>
)
:
null
}
</
Row
>
</
Row
>
<
Space
wrap
style=
{
{
marginTop
:
8
}
}
>
<
Tag
<
Text
size=
'small'
type=
'tertiary'
color=
'cyan'
size=
'small'
className=
'cursor-pointer'
className=
'mt-1 block'
onClick=
{
()
=>
>
updateOperation
(
operation
.
id
,
{
{
MODE_DESCRIPTIONS
[
mode
]
||
''
}
from
:
'header:session_id'
,
</
Text
>
to
:
'json:prompt_cache_key'
,
})
{
meta
.
value
?
(
}
<
div
className=
'mt-2'
>
>
{
'header:session_id -> json:prompt_cache_key'
}
</
Tag
>
<
Tag
size=
'small'
color=
'cyan'
className=
'cursor-pointer'
onClick=
{
()
=>
updateOperation
(
operation
.
id
,
{
from
:
'json:prompt_cache_key'
,
to
:
'header:session_id'
,
})
}
>
{
'json:prompt_cache_key -> header:session_id'
}
</
Tag
>
</
Space
>
</
div
>
)
:
meta
.
from
||
meta
.
to
===
false
||
meta
.
to
?
(
<
Row
gutter=
{
12
}
style=
{
{
marginTop
:
8
}
}
>
{
meta
.
from
||
meta
.
to
===
false
?
(
<
Col
xs=
{
24
}
md=
{
12
}
>
<
Text
type=
'tertiary'
size=
'small'
>
<
Text
type=
'tertiary'
size=
'small'
>
from
value (JSON or plain text)
</
Text
>
</
Text
>
<
Input
<
TextArea
value=
{
operation
.
from
}
value=
{
selectedOperation
.
value_text
}
placeholder=
{
autosize=
{
{
minRows
:
1
,
maxRows
:
4
}
}
mode
.
includes
(
'header'
)
placeholder=
'0.7'
?
'Authorization'
:
'model'
}
onChange=
{
(
nextValue
)
=>
onChange=
{
(
nextValue
)
=>
updateOperation
(
o
peration
.
id
,
{
updateOperation
(
selectedO
peration
.
id
,
{
from
:
nextValue
,
value_text
:
nextValue
,
})
})
}
}
/>
/>
</
Col
>
</
div
>
)
:
null
}
)
:
null
}
{
meta
.
to
||
meta
.
to
===
false
?
(
<
Col
xs=
{
24
}
md=
{
12
}
>
{
meta
.
keepOrigin
?
(
<
Text
type=
'tertiary'
size=
'small'
>
<
div
className=
'mt-2'
>
to
<
Switch
</
Text
>
checked=
{
Boolean
(
<
Input
selectedOperation
.
keep_origin
,
value=
{
operation
.
to
}
)
}
placeholder=
{
checkedText=
{
t
(
'开'
)
}
mode
.
includes
(
'header'
)
uncheckedText=
{
t
(
'关'
)
}
?
'X-Upstream-Auth'
:
'original_model'
}
onChange=
{
(
nextValue
)
=>
onChange=
{
(
nextValue
)
=>
updateOperation
(
operation
.
id
,
{
to
:
nextValue
})
updateOperation
(
selectedOperation
.
id
,
{
keep_origin
:
nextValue
,
})
}
}
/>
/>
</
Col
>
<
Text
type=
'tertiary'
size=
'small'
className=
'ml-2'
>
keep_origin
</
Text
>
</
div
>
)
:
null
}
)
:
null
}
</
Row
>
)
:
null
}
<
div
className=
'mt-3 border rounded-lg p-2'
>
<
div
className=
'flex items-center justify-between mb-2'
>
<
Space
>
<
Text
>
{
t
(
'条件'
)
}
</
Text
>
<
Select
value=
{
operation
.
logic
||
'OR'
}
optionList=
{
[
{
label
:
'OR'
,
value
:
'OR'
},
{
label
:
'AND'
,
value
:
'AND'
},
]
}
style=
{
{
width
:
96
}
}
onChange=
{
(
nextValue
)
=>
updateOperation
(
operation
.
id
,
{
logic
:
nextValue
,
})
}
/>
</
Space
>
<
Button
icon=
{
<
IconPlus
/>
}
size=
'small'
onClick=
{
()
=>
addCondition
(
operation
.
id
)
}
>
{
t
(
'新增条件'
)
}
</
Button
>
</
div
>
{
conditions
.
length
===
0
?
(
{
mode
===
'sync_fields'
?
(
<
Text
type=
'tertiary'
size=
'small'
>
<
div
className=
'mt-2'
>
{
t
(
'没有条件时,默认总是执行该操作。'
)
}
<
Text
type=
'tertiary'
size=
'small'
>
</
Text
>
sync endpoints
)
:
(
</
Text
>
<
Space
vertical
spacing=
{
8
}
style=
{
{
width
:
'100%'
}
}
>
<
Row
gutter=
{
12
}
style=
{
{
marginTop
:
6
}
}
>
{
conditions
.
map
((
condition
,
conditionIndex
)
=>
(
<
Col
xs=
{
24
}
md=
{
12
}
>
<
Card
<
Text
type=
'tertiary'
size=
'small'
>
key=
{
condition
.
id
}
from endpoint
bodyStyle=
{
{
padding
:
10
}
}
</
Text
>
className=
'!rounded-lg'
<
div
className=
'flex gap-2'
>
>
<
Select
<
div
className=
'flex items-center justify-between mb-2'
>
value=
{
syncFromTarget
?.
type
||
'json'
}
<
Tag
size=
'small'
>
{
`C${conditionIndex + 1}`
}
</
Tag
>
optionList=
{
SYNC_TARGET_TYPE_OPTIONS
}
<
Button
style=
{
{
width
:
120
}
}
theme=
'borderless'
onChange=
{
(
nextType
)
=>
type=
'danger'
updateOperation
(
icon=
{
<
IconDelete
/>
}
selectedOperation
.
id
,
size=
'small'
{
onClick=
{
()
=>
from
:
buildSyncTargetSpec
(
removeCondition
(
operation
.
id
,
condition
.
id
)
nextType
,
}
syncFromTarget
?.
key
||
''
,
/>
),
</
div
>
},
<
Row
gutter=
{
12
}
>
)
<
Col
xs=
{
24
}
md=
{
10
}
>
}
<
Text
type=
'tertiary'
size=
'small'
>
/>
path
</
Text
>
<
Input
<
Input
value=
{
condition
.
path
}
value=
{
syncFromTarget
?.
key
||
''
}
placeholder=
'model'
placeholder=
'session_id'
onChange=
{
(
nextValue
)
=>
onChange=
{
(
nextKey
)
=>
updateCondition
(
updateOperation
(
operation
.
id
,
selectedOperation
.
id
,
condition
.
id
,
{
{
path
:
nextValue
},
from
:
buildSyncTargetSpec
(
syncFromTarget
?.
type
||
'json'
,
nextKey
,
),
},
)
)
}
}
/>
/>
<
Space
wrap
style=
{
{
marginTop
:
6
}
}
>
</
div
>
{
CONDITION_PATH_SUGGESTIONS
.
map
(
</
Col
>
(
pathItem
)
=>
(
<
Col
xs=
{
24
}
md=
{
12
}
>
<
Tag
<
Text
type=
'tertiary'
size=
'small'
>
key=
{
`${condition.id}_${pathItem}`
}
to endpoint
size=
'small'
</
Text
>
color=
'grey'
<
div
className=
'flex gap-2'
>
className=
'cursor-pointer'
onClick=
{
()
=>
updateCondition
(
operation
.
id
,
condition
.
id
,
{
path
:
pathItem
},
)
}
>
{
pathItem
}
</
Tag
>
),
)
}
</
Space
>
</
Col
>
<
Col
xs=
{
24
}
md=
{
8
}
>
<
Text
type=
'tertiary'
size=
'small'
>
mode
</
Text
>
<
Select
<
Select
value=
{
condition
.
mode
}
value=
{
syncToTarget
?.
type
||
'json'
}
optionList=
{
CONDITION_MODE_OPTIONS
}
optionList=
{
SYNC_TARGET_TYPE_OPTIONS
}
onChange=
{
(
nextValue
)
=>
style=
{
{
width
:
120
}
}
updateCondition
(
onChange=
{
(
nextType
)
=>
operation
.
id
,
updateOperation
(
condition
.
id
,
selectedOperation
.
id
,
{
mode
:
nextValue
},
{
to
:
buildSyncTargetSpec
(
nextType
,
syncToTarget
?.
key
||
''
,
),
},
)
)
}
}
style=
{
{
width
:
'100%'
}
}
/>
/>
</
Col
>
<
Col
xs=
{
24
}
md=
{
6
}
>
<
Text
type=
'tertiary'
size=
'small'
>
value
</
Text
>
<
Input
<
Input
value=
{
condition
.
value_text
}
value=
{
syncToTarget
?.
key
||
''
}
placeholder=
'gpt'
placeholder=
'prompt_cache_key'
onChange=
{
(
nextValue
)
=>
onChange=
{
(
nextKey
)
=>
updateCondition
(
updateOperation
(
operation
.
id
,
selectedOperation
.
id
,
condition
.
id
,
{
{
value_text
:
nextValue
},
to
:
buildSyncTargetSpec
(
syncToTarget
?.
type
||
'json'
,
nextKey
,
),
},
)
)
}
}
/>
/>
</
Col
>
</
div
>
</
Row
>
</
Col
>
<
Space
style=
{
{
marginTop
:
8
}
}
>
</
Row
>
<
Switch
<
Space
wrap
style=
{
{
marginTop
:
8
}
}
>
checked=
{
condition
.
invert
}
<
Tag
checkedText=
{
t
(
'开'
)
}
size=
'small'
uncheckedText=
{
t
(
'关'
)
}
color=
'cyan'
className=
'cursor-pointer'
onClick=
{
()
=>
updateOperation
(
selectedOperation
.
id
,
{
from
:
'header:session_id'
,
to
:
'json:prompt_cache_key'
,
})
}
>
{
'header:session_id -> json:prompt_cache_key'
}
</
Tag
>
<
Tag
size=
'small'
color=
'cyan'
className=
'cursor-pointer'
onClick=
{
()
=>
updateOperation
(
selectedOperation
.
id
,
{
from
:
'json:prompt_cache_key'
,
to
:
'header:session_id'
,
})
}
>
{
'json:prompt_cache_key -> header:session_id'
}
</
Tag
>
</
Space
>
</
div
>
)
:
meta
.
from
||
meta
.
to
===
false
||
meta
.
to
?
(
<
Row
gutter=
{
12
}
style=
{
{
marginTop
:
8
}
}
>
{
meta
.
from
||
meta
.
to
===
false
?
(
<
Col
xs=
{
24
}
md=
{
12
}
>
<
Text
type=
'tertiary'
size=
'small'
>
from
</
Text
>
<
Input
value=
{
selectedOperation
.
from
}
placeholder=
{
mode
.
includes
(
'header'
)
?
'Authorization'
:
'model'
}
onChange=
{
(
nextValue
)
=>
onChange=
{
(
nextValue
)
=>
updateCondition
(
updateOperation
(
selectedOperation
.
id
,
{
operation
.
id
,
from
:
nextValue
,
condition
.
id
,
})
{
invert
:
nextValue
},
)
}
}
/>
/>
</
Col
>
)
:
null
}
{
meta
.
to
||
meta
.
to
===
false
?
(
<
Col
xs=
{
24
}
md=
{
12
}
>
<
Text
type=
'tertiary'
size=
'small'
>
<
Text
type=
'tertiary'
size=
'small'
>
invert
to
</
Text
>
</
Text
>
<
Switch
<
Input
checked=
{
condition
.
pass_missing_key
}
value=
{
selectedOperation
.
to
}
checkedText=
{
t
(
'开'
)
}
placeholder=
{
uncheckedText=
{
t
(
'关'
)
}
mode
.
includes
(
'header'
)
?
'X-Upstream-Auth'
:
'original_model'
}
onChange=
{
(
nextValue
)
=>
onChange=
{
(
nextValue
)
=>
updateCondition
(
updateOperation
(
selectedOperation
.
id
,
{
operation
.
id
,
to
:
nextValue
,
condition
.
id
,
})
{
pass_missing_key
:
nextValue
},
)
}
}
/>
/>
<
Text
type=
'tertiary'
size=
'small'
>
</
Col
>
pass_missing_key
)
:
null
}
</
Text
>
</
Row
>
</
Space
>
)
:
null
}
</
Card
>
))
}
<
div
</
Space
>
className=
'mt-3 rounded-xl p-2'
)
}
style=
{
{
</
div
>
background
:
'rgba(127, 127, 127, 0.08)'
,
}
}
>
<
div
className=
'flex items-center justify-between mb-2'
>
<
Space
>
<
Text
>
{
t
(
'条件'
)
}
</
Text
>
<
Select
value=
{
selectedOperation
.
logic
||
'OR'
}
optionList=
{
[
{
label
:
'OR'
,
value
:
'OR'
},
{
label
:
'AND'
,
value
:
'AND'
},
]
}
style=
{
{
width
:
96
}
}
onChange=
{
(
nextValue
)
=>
updateOperation
(
selectedOperation
.
id
,
{
logic
:
nextValue
,
})
}
/>
</
Space
>
<
Space
spacing=
{
6
}
>
<
Button
size=
'small'
type=
'tertiary'
onClick=
{
expandAllSelectedConditions
}
>
{
t
(
'全部展开'
)
}
</
Button
>
<
Button
size=
'small'
type=
'tertiary'
onClick=
{
collapseAllSelectedConditions
}
>
{
t
(
'全部收起'
)
}
</
Button
>
<
Button
icon=
{
<
IconPlus
/>
}
size=
'small'
onClick=
{
()
=>
addCondition
(
selectedOperation
.
id
)
}
>
{
t
(
'新增条件'
)
}
</
Button
>
</
Space
>
</
div
>
{
conditions
.
length
===
0
?
(
<
Text
type=
'tertiary'
size=
'small'
>
{
t
(
'没有条件时,默认总是执行该操作。'
)
}
</
Text
>
)
:
(
<
Collapse
keepDOM
activeKey=
{
selectedConditionKeys
}
onChange=
{
(
activeKeys
)
=>
handleConditionCollapseChange
(
selectedOperation
.
id
,
activeKeys
,
)
}
>
{
conditions
.
map
(
(
condition
,
conditionIndex
)
=>
(
<
Collapse
.
Panel
key=
{
condition
.
id
}
itemKey=
{
condition
.
id
}
header=
{
<
Space
spacing=
{
8
}
>
<
Tag
size=
'small'
>
{
`C${conditionIndex + 1}`
}
</
Tag
>
<
Text
type=
'tertiary'
size=
'small'
>
{
condition
.
path
||
t
(
'未设置 path'
)
}
</
Text
>
</
Space
>
}
>
<
div
>
<
div
className=
'flex justify-end mb-2'
>
<
Button
theme=
'borderless'
type=
'danger'
icon=
{
<
IconDelete
/>
}
size=
'small'
onClick=
{
()
=>
removeCondition
(
selectedOperation
.
id
,
condition
.
id
,
)
}
/>
</
div
>
<
Row
gutter=
{
12
}
>
<
Col
xs=
{
24
}
md=
{
10
}
>
<
Text
type=
'tertiary'
size=
'small'
>
path
</
Text
>
<
Input
value=
{
condition
.
path
}
placeholder=
'model'
onChange=
{
(
nextValue
)
=>
updateCondition
(
selectedOperation
.
id
,
condition
.
id
,
{
path
:
nextValue
},
)
}
/>
<
Space
wrap
style=
{
{
marginTop
:
6
}
}
>
{
CONDITION_PATH_SUGGESTIONS
.
map
(
(
pathItem
)
=>
(
<
Tag
key=
{
`${condition.id}_${pathItem}`
}
size=
'small'
color=
'grey'
className=
'cursor-pointer'
onClick=
{
()
=>
updateCondition
(
selectedOperation
.
id
,
condition
.
id
,
{
path
:
pathItem
},
)
}
>
{
pathItem
}
</
Tag
>
),
)
}
</
Space
>
</
Col
>
<
Col
xs=
{
24
}
md=
{
8
}
>
<
Text
type=
'tertiary'
size=
'small'
>
mode
</
Text
>
<
Select
value=
{
condition
.
mode
}
optionList=
{
CONDITION_MODE_OPTIONS
}
onChange=
{
(
nextValue
)
=>
updateCondition
(
selectedOperation
.
id
,
condition
.
id
,
{
mode
:
nextValue
},
)
}
style=
{
{
width
:
'100%'
}
}
/>
</
Col
>
<
Col
xs=
{
24
}
md=
{
6
}
>
<
Text
type=
'tertiary'
size=
'small'
>
value
</
Text
>
<
Input
value=
{
condition
.
value_text
}
placeholder=
'gpt'
onChange=
{
(
nextValue
)
=>
updateCondition
(
selectedOperation
.
id
,
condition
.
id
,
{
value_text
:
nextValue
},
)
}
/>
</
Col
>
</
Row
>
<
Space
style=
{
{
marginTop
:
8
}
}
>
<
Switch
checked=
{
Boolean
(
condition
.
invert
,
)
}
checkedText=
{
t
(
'开'
)
}
uncheckedText=
{
t
(
'关'
)
}
onChange=
{
(
nextValue
)
=>
updateCondition
(
selectedOperation
.
id
,
condition
.
id
,
{
invert
:
nextValue
},
)
}
/>
<
Text
type=
'tertiary'
size=
'small'
>
invert
</
Text
>
<
Switch
checked=
{
Boolean
(
condition
.
pass_missing_key
,
)
}
checkedText=
{
t
(
'开'
)
}
uncheckedText=
{
t
(
'关'
)
}
onChange=
{
(
nextValue
)
=>
updateCondition
(
selectedOperation
.
id
,
condition
.
id
,
{
pass_missing_key
:
nextValue
,
},
)
}
/>
<
Text
type=
'tertiary'
size=
'small'
>
pass_missing_key
</
Text
>
</
Space
>
</
div
>
</
Collapse
.
Panel
>
),
)
}
</
Collapse
>
)
}
</
div
>
</
Card
>
);
})()
)
:
(
<
Card
className=
'!rounded-2xl !border-0'
bodyStyle=
{
{
padding
:
14
,
background
:
'var(--semi-color-fill-0)'
,
}
}
>
<
Text
type=
'tertiary'
>
{
t
(
'请选择一条规则进行编辑。'
)
}
</
Text
>
</
Card
>
</
Card
>
);
)
}
})
}
</
Space
>
<
Card
<
Card
className=
'!rounded-xl border mt-3'
>
className=
'!rounded-2xl !border-0 mt-3'
<
div
className=
'flex items-center justify-between mb-2'
>
bodyStyle=
{
{
<
Text
>
{
t
(
'实时 JSON 预览'
)
}
</
Text
>
padding
:
12
,
<
Tag
color=
'grey'
>
{
t
(
'预览'
)
}
</
Tag
>
background
:
'var(--semi-color-fill-0)'
,
</
div
>
}
}
<
pre
className=
'mb-0 text-xs leading-5 whitespace-pre-wrap break-all max-h-64 overflow-auto'
>
>
{
visualPreview
||
'
{}
'
}
<
div
className=
'flex items-center justify-between mb-2'
>
</
pre
>
<
Text
>
{
t
(
'实时 JSON 预览'
)
}
</
Text
>
</
Card
>
<
Tag
color=
'grey'
>
{
t
(
'预览'
)
}
</
Tag
>
</
div
>
<
pre
className=
'mb-0 text-xs leading-5 whitespace-pre-wrap break-all max-h-64 overflow-auto'
>
{
visualPreview
||
'
{}
'
}
</
pre
>
</
Card
>
</
Col
>
</
Row
>
</
div
>
</
div
>
)
}
)
}
</
div
>
</
div
>
...
@@ -1295,7 +1734,7 @@ const ParamOverrideEditorModal = ({ visible, value, onSave, onCancel }) => {
...
@@ -1295,7 +1734,7 @@ const ParamOverrideEditorModal = ({ visible, value, onSave, onCancel }) => {
<
Button
onClick=
{
formatJson
}
>
{
t
(
'格式化'
)
}
</
Button
>
<
Button
onClick=
{
formatJson
}
>
{
t
(
'格式化'
)
}
</
Button
>
<
Tag
color=
'grey'
>
{
t
(
'普通编辑'
)
}
</
Tag
>
<
Tag
color=
'grey'
>
{
t
(
'普通编辑'
)
}
</
Tag
>
</
Space
>
</
Space
>
<
Input
.
TextArea
<
TextArea
value=
{
jsonText
}
value=
{
jsonText
}
autosize=
{
{
minRows
:
18
,
maxRows
:
28
}
}
autosize=
{
{
minRows
:
18
,
maxRows
:
28
}
}
onChange=
{
(
nextValue
)
=>
handleJsonChange
(
nextValue
??
''
)
}
onChange=
{
(
nextValue
)
=>
handleJsonChange
(
nextValue
??
''
)
}
...
...
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