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
325de987
authored
Nov 19, 2024
by
CalciumIon
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: 渠道标签分组
parent
55f9a48f
Show whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
767 additions
and
291 deletions
+767
-291
controller/channel.go
+97
-1
model/ability.go
+20
-0
model/channel.go
+54
-0
router/api-router.go
+3
-0
web/src/components/ChannelsTable.js
+276
-123
web/src/components/TextInput.js
+22
-0
web/src/helpers/render.js
+2
-0
web/src/pages/Channel/EditChannel.js
+200
-167
web/src/pages/Channel/EditTagModal.js
+93
-0
No files found.
controller/channel.go
View file @
325de987
...
@@ -57,10 +57,24 @@ func GetAllChannels(c *gin.Context) {
...
@@ -57,10 +57,24 @@ func GetAllChannels(c *gin.Context) {
})
})
return
return
}
}
tags
:=
make
(
map
[
string
]
bool
)
channelData
:=
make
([]
*
model
.
Channel
,
0
,
len
(
channels
))
for
_
,
channel
:=
range
channels
{
channelTag
:=
channel
.
GetTag
()
if
channelTag
!=
""
&&
!
tags
[
channelTag
]
{
tags
[
channelTag
]
=
true
tagChannels
,
err
:=
model
.
GetChannelsByTag
(
channelTag
)
if
err
==
nil
{
channelData
=
append
(
channelData
,
tagChannels
...
)
}
}
else
{
channelData
=
append
(
channelData
,
channel
)
}
}
c
.
JSON
(
http
.
StatusOK
,
gin
.
H
{
c
.
JSON
(
http
.
StatusOK
,
gin
.
H
{
"success"
:
true
,
"success"
:
true
,
"message"
:
""
,
"message"
:
""
,
"data"
:
channel
s
,
"data"
:
channel
Data
,
})
})
return
return
}
}
...
@@ -279,6 +293,88 @@ func DeleteDisabledChannel(c *gin.Context) {
...
@@ -279,6 +293,88 @@ func DeleteDisabledChannel(c *gin.Context) {
return
return
}
}
type
ChannelTag
struct
{
Tag
string
`json:"tag"`
NewTag
*
string
`json:"newTag"`
Priority
*
int64
`json:"priority"`
Weight
*
uint
`json:"weight"`
}
func
DisableTagChannels
(
c
*
gin
.
Context
)
{
channelTag
:=
ChannelTag
{}
err
:=
c
.
ShouldBindJSON
(
&
channelTag
)
if
err
!=
nil
||
channelTag
.
Tag
==
""
{
c
.
JSON
(
http
.
StatusOK
,
gin
.
H
{
"success"
:
false
,
"message"
:
"参数错误"
,
})
return
}
err
=
model
.
DisableChannelByTag
(
channelTag
.
Tag
)
if
err
!=
nil
{
c
.
JSON
(
http
.
StatusOK
,
gin
.
H
{
"success"
:
false
,
"message"
:
err
.
Error
(),
})
return
}
c
.
JSON
(
http
.
StatusOK
,
gin
.
H
{
"success"
:
true
,
"message"
:
""
,
})
return
}
func
EnableTagChannels
(
c
*
gin
.
Context
)
{
channelTag
:=
ChannelTag
{}
err
:=
c
.
ShouldBindJSON
(
&
channelTag
)
if
err
!=
nil
||
channelTag
.
Tag
==
""
{
c
.
JSON
(
http
.
StatusOK
,
gin
.
H
{
"success"
:
false
,
"message"
:
"参数错误"
,
})
return
}
err
=
model
.
EnableChannelByTag
(
channelTag
.
Tag
)
if
err
!=
nil
{
c
.
JSON
(
http
.
StatusOK
,
gin
.
H
{
"success"
:
false
,
"message"
:
err
.
Error
(),
})
return
}
c
.
JSON
(
http
.
StatusOK
,
gin
.
H
{
"success"
:
true
,
"message"
:
""
,
})
return
}
func
EditTagChannels
(
c
*
gin
.
Context
)
{
channelTag
:=
ChannelTag
{}
err
:=
c
.
ShouldBindJSON
(
&
channelTag
)
if
err
!=
nil
||
channelTag
.
Tag
==
""
{
c
.
JSON
(
http
.
StatusOK
,
gin
.
H
{
"success"
:
false
,
"message"
:
"参数错误"
,
})
return
}
err
=
model
.
EditChannelByTag
(
channelTag
.
Tag
,
channelTag
.
NewTag
,
channelTag
.
Priority
,
channelTag
.
Weight
)
if
err
!=
nil
{
c
.
JSON
(
http
.
StatusOK
,
gin
.
H
{
"success"
:
false
,
"message"
:
err
.
Error
(),
})
return
}
c
.
JSON
(
http
.
StatusOK
,
gin
.
H
{
"success"
:
true
,
"message"
:
""
,
})
return
}
type
ChannelBatch
struct
{
type
ChannelBatch
struct
{
Ids
[]
int
`json:"ids"`
Ids
[]
int
`json:"ids"`
}
}
...
...
model/ability.go
View file @
325de987
...
@@ -16,6 +16,7 @@ type Ability struct {
...
@@ -16,6 +16,7 @@ type Ability struct {
Enabled
bool
`json:"enabled"`
Enabled
bool
`json:"enabled"`
Priority
*
int64
`json:"priority" gorm:"bigint;default:0;index"`
Priority
*
int64
`json:"priority" gorm:"bigint;default:0;index"`
Weight
uint
`json:"weight" gorm:"default:0;index"`
Weight
uint
`json:"weight" gorm:"default:0;index"`
Tag
*
string
`json:"tag" gorm:"index"`
}
}
func
GetGroupModels
(
group
string
)
[]
string
{
func
GetGroupModels
(
group
string
)
[]
string
{
...
@@ -149,6 +150,7 @@ func (channel *Channel) AddAbilities() error {
...
@@ -149,6 +150,7 @@ func (channel *Channel) AddAbilities() error {
Enabled
:
channel
.
Status
==
common
.
ChannelStatusEnabled
,
Enabled
:
channel
.
Status
==
common
.
ChannelStatusEnabled
,
Priority
:
channel
.
Priority
,
Priority
:
channel
.
Priority
,
Weight
:
uint
(
channel
.
GetWeight
()),
Weight
:
uint
(
channel
.
GetWeight
()),
Tag
:
channel
.
Tag
,
}
}
abilities
=
append
(
abilities
,
ability
)
abilities
=
append
(
abilities
,
ability
)
}
}
...
@@ -190,6 +192,24 @@ func UpdateAbilityStatus(channelId int, status bool) error {
...
@@ -190,6 +192,24 @@ func UpdateAbilityStatus(channelId int, status bool) error {
return
DB
.
Model
(
&
Ability
{})
.
Where
(
"channel_id = ?"
,
channelId
)
.
Select
(
"enabled"
)
.
Update
(
"enabled"
,
status
)
.
Error
return
DB
.
Model
(
&
Ability
{})
.
Where
(
"channel_id = ?"
,
channelId
)
.
Select
(
"enabled"
)
.
Update
(
"enabled"
,
status
)
.
Error
}
}
func
UpdateAbilityStatusByTag
(
tag
string
,
status
bool
)
error
{
return
DB
.
Model
(
&
Ability
{})
.
Where
(
"tag = ?"
,
tag
)
.
Select
(
"enabled"
)
.
Update
(
"enabled"
,
status
)
.
Error
}
func
UpdateAbilityByTag
(
tag
string
,
newTag
*
string
,
priority
*
int64
,
weight
*
uint
)
error
{
ability
:=
Ability
{}
if
newTag
!=
nil
{
ability
.
Tag
=
newTag
}
if
priority
!=
nil
{
ability
.
Priority
=
priority
}
if
weight
!=
nil
{
ability
.
Weight
=
*
weight
}
return
DB
.
Model
(
&
Ability
{})
.
Where
(
"tag = ?"
,
tag
)
.
Updates
(
ability
)
.
Error
}
func
FixAbility
()
(
int
,
error
)
{
func
FixAbility
()
(
int
,
error
)
{
var
channelIds
[]
int
var
channelIds
[]
int
count
:=
0
count
:=
0
...
...
model/channel.go
View file @
325de987
...
@@ -32,6 +32,7 @@ type Channel struct {
...
@@ -32,6 +32,7 @@ type Channel struct {
Priority
*
int64
`json:"priority" gorm:"bigint;default:0"`
Priority
*
int64
`json:"priority" gorm:"bigint;default:0"`
AutoBan
*
int
`json:"auto_ban" gorm:"default:1"`
AutoBan
*
int
`json:"auto_ban" gorm:"default:1"`
OtherInfo
string
`json:"other_info"`
OtherInfo
string
`json:"other_info"`
Tag
*
string
`json:"tag" gorm:"index"`
}
}
func
(
channel
*
Channel
)
GetModels
()
[]
string
{
func
(
channel
*
Channel
)
GetModels
()
[]
string
{
...
@@ -61,6 +62,17 @@ func (channel *Channel) SetOtherInfo(otherInfo map[string]interface{}) {
...
@@ -61,6 +62,17 @@ func (channel *Channel) SetOtherInfo(otherInfo map[string]interface{}) {
channel
.
OtherInfo
=
string
(
otherInfoBytes
)
channel
.
OtherInfo
=
string
(
otherInfoBytes
)
}
}
func
(
channel
*
Channel
)
GetTag
()
string
{
if
channel
.
Tag
==
nil
{
return
""
}
return
*
channel
.
Tag
}
func
(
channel
*
Channel
)
SetTag
(
tag
string
)
{
channel
.
Tag
=
&
tag
}
func
(
channel
*
Channel
)
GetAutoBan
()
bool
{
func
(
channel
*
Channel
)
GetAutoBan
()
bool
{
if
channel
.
AutoBan
==
nil
{
if
channel
.
AutoBan
==
nil
{
return
false
return
false
...
@@ -87,6 +99,12 @@ func GetAllChannels(startIdx int, num int, selectAll bool, idSort bool) ([]*Chan
...
@@ -87,6 +99,12 @@ func GetAllChannels(startIdx int, num int, selectAll bool, idSort bool) ([]*Chan
return
channels
,
err
return
channels
,
err
}
}
func
GetChannelsByTag
(
tag
string
)
([]
*
Channel
,
error
)
{
var
channels
[]
*
Channel
err
:=
DB
.
Where
(
"tag = ?"
,
tag
)
.
Find
(
&
channels
)
.
Error
return
channels
,
err
}
func
SearchChannels
(
keyword
string
,
group
string
,
model
string
)
([]
*
Channel
,
error
)
{
func
SearchChannels
(
keyword
string
,
group
string
,
model
string
)
([]
*
Channel
,
error
)
{
var
channels
[]
*
Channel
var
channels
[]
*
Channel
keyCol
:=
"`key`"
keyCol
:=
"`key`"
...
@@ -288,6 +306,42 @@ func UpdateChannelStatusById(id int, status int, reason string) {
...
@@ -288,6 +306,42 @@ func UpdateChannelStatusById(id int, status int, reason string) {
}
}
func
EnableChannelByTag
(
tag
string
)
error
{
err
:=
DB
.
Model
(
&
Channel
{})
.
Where
(
"tag = ?"
,
tag
)
.
Update
(
"status"
,
common
.
ChannelStatusEnabled
)
.
Error
if
err
!=
nil
{
return
err
}
err
=
UpdateAbilityStatusByTag
(
tag
,
true
)
return
err
}
func
DisableChannelByTag
(
tag
string
)
error
{
err
:=
DB
.
Model
(
&
Channel
{})
.
Where
(
"tag = ?"
,
tag
)
.
Update
(
"status"
,
common
.
ChannelStatusManuallyDisabled
)
.
Error
if
err
!=
nil
{
return
err
}
err
=
UpdateAbilityStatusByTag
(
tag
,
false
)
return
err
}
func
EditChannelByTag
(
tag
string
,
newTag
*
string
,
priority
*
int64
,
weight
*
uint
)
error
{
updateData
:=
Channel
{}
if
newTag
!=
nil
{
updateData
.
Tag
=
newTag
}
if
priority
!=
nil
{
updateData
.
Priority
=
priority
}
if
weight
!=
nil
{
updateData
.
Weight
=
weight
}
err
:=
DB
.
Model
(
&
Channel
{})
.
Where
(
"tag = ?"
,
tag
)
.
Updates
(
updateData
)
.
Error
if
err
!=
nil
{
return
err
}
return
UpdateAbilityByTag
(
tag
,
newTag
,
priority
,
weight
)
}
func
UpdateChannelUsedQuota
(
id
int
,
quota
int
)
{
func
UpdateChannelUsedQuota
(
id
int
,
quota
int
)
{
if
common
.
BatchUpdateEnabled
{
if
common
.
BatchUpdateEnabled
{
addNewRecord
(
BatchUpdateTypeChannelUsedQuota
,
id
,
quota
)
addNewRecord
(
BatchUpdateTypeChannelUsedQuota
,
id
,
quota
)
...
...
router/api-router.go
View file @
325de987
...
@@ -91,6 +91,9 @@ func SetApiRouter(router *gin.Engine) {
...
@@ -91,6 +91,9 @@ func SetApiRouter(router *gin.Engine) {
channelRoute
.
POST
(
"/"
,
controller
.
AddChannel
)
channelRoute
.
POST
(
"/"
,
controller
.
AddChannel
)
channelRoute
.
PUT
(
"/"
,
controller
.
UpdateChannel
)
channelRoute
.
PUT
(
"/"
,
controller
.
UpdateChannel
)
channelRoute
.
DELETE
(
"/disabled"
,
controller
.
DeleteDisabledChannel
)
channelRoute
.
DELETE
(
"/disabled"
,
controller
.
DeleteDisabledChannel
)
channelRoute
.
POST
(
"/tag/disabled"
,
controller
.
DisableTagChannels
)
channelRoute
.
POST
(
"/tag/enabled"
,
controller
.
EnableTagChannels
)
channelRoute
.
PUT
(
"/tag"
,
controller
.
EditTagChannels
)
channelRoute
.
DELETE
(
"/:id"
,
controller
.
DeleteChannel
)
channelRoute
.
DELETE
(
"/:id"
,
controller
.
DeleteChannel
)
channelRoute
.
POST
(
"/batch"
,
controller
.
DeleteChannelBatch
)
channelRoute
.
POST
(
"/batch"
,
controller
.
DeleteChannelBatch
)
channelRoute
.
POST
(
"/fix"
,
controller
.
FixChannelsAbilities
)
channelRoute
.
POST
(
"/fix"
,
controller
.
FixChannelsAbilities
)
...
...
web/src/components/ChannelsTable.js
View file @
325de987
...
@@ -7,14 +7,14 @@ import {
...
@@ -7,14 +7,14 @@ import {
showInfo
,
showInfo
,
showSuccess
,
showSuccess
,
showWarning
,
showWarning
,
timestamp2string
,
timestamp2string
}
from
'../helpers'
;
}
from
'../helpers'
;
import
{
CHANNEL_OPTIONS
,
ITEMS_PER_PAGE
}
from
'../constants'
;
import
{
CHANNEL_OPTIONS
,
ITEMS_PER_PAGE
}
from
'../constants'
;
import
{
import
{
renderGroup
,
renderGroup
,
renderNumberWithPoint
,
renderNumberWithPoint
,
renderQuota
,
renderQuota
}
from
'../helpers/render'
;
}
from
'../helpers/render'
;
import
{
import
{
Button
,
Divider
,
Button
,
Divider
,
...
@@ -28,11 +28,12 @@ import {
...
@@ -28,11 +28,12 @@ import {
Table
,
Table
,
Tag
,
Tag
,
Tooltip
,
Tooltip
,
Typography
,
Typography
}
from
'@douyinfe/semi-ui'
;
}
from
'@douyinfe/semi-ui'
;
import
EditChannel
from
'../pages/Channel/EditChannel'
;
import
EditChannel
from
'../pages/Channel/EditChannel'
;
import
{
IconTreeTriangleDown
}
from
'@douyinfe/semi-icons'
;
import
{
IconTreeTriangleDown
}
from
'@douyinfe/semi-icons'
;
import
{
loadChannelModels
}
from
'./utils.js'
;
import
{
loadChannelModels
}
from
'./utils.js'
;
import
EditTagModal
from
'../pages/Channel/EditTagModal.js'
;
function
renderTimestamp
(
timestamp
)
{
function
renderTimestamp
(
timestamp
)
{
return
<>
{
timestamp2string
(
timestamp
)}
<
/>
;
return
<>
{
timestamp2string
(
timestamp
)}
<
/>
;
...
@@ -49,7 +50,7 @@ function renderType(type) {
...
@@ -49,7 +50,7 @@ function renderType(type) {
type2label
[
0
]
=
{
value
:
0
,
text
:
'未知类型'
,
color
:
'grey'
};
type2label
[
0
]
=
{
value
:
0
,
text
:
'未知类型'
,
color
:
'grey'
};
}
}
return
(
return
(
<
Tag
size
=
'large'
color
=
{
type2label
[
type
]?.
color
}
>
<
Tag
size
=
"large"
color
=
{
type2label
[
type
]?.
color
}
>
{
type2label
[
type
]?.
text
}
{
type2label
[
type
]?.
text
}
<
/Tag
>
<
/Tag
>
);
);
...
@@ -64,11 +65,11 @@ const ChannelsTable = () => {
...
@@ -64,11 +65,11 @@ const ChannelsTable = () => {
// },
// },
{
{
title
:
'ID'
,
title
:
'ID'
,
dataIndex
:
'id'
,
dataIndex
:
'id'
},
},
{
{
title
:
'名称'
,
title
:
'名称'
,
dataIndex
:
'name'
,
dataIndex
:
'name'
},
},
{
{
title
:
'分组'
,
title
:
'分组'
,
...
@@ -77,20 +78,20 @@ const ChannelsTable = () => {
...
@@ -77,20 +78,20 @@ const ChannelsTable = () => {
return
(
return
(
<
div
>
<
div
>
<
Space
spacing
=
{
2
}
>
<
Space
spacing
=
{
2
}
>
{
text
.
split
(
','
).
map
((
item
,
index
)
=>
{
{
text
?
.
split
(
','
).
map
((
item
,
index
)
=>
{
return
renderGroup
(
item
);
return
renderGroup
(
item
);
})}
})}
<
/Space
>
<
/Space
>
<
/div
>
<
/div
>
);
);
}
,
}
},
},
{
{
title
:
'类型'
,
title
:
'类型'
,
dataIndex
:
'type'
,
dataIndex
:
'type'
,
render
:
(
text
,
record
,
index
)
=>
{
render
:
(
text
,
record
,
index
)
=>
{
return
<
div
>
{
renderType
(
text
)}
<
/div>
;
return
<
div
>
{
renderType
(
text
)}
<
/div>
;
}
,
}
},
},
{
{
title
:
'状态'
,
title
:
'状态'
,
...
@@ -98,7 +99,7 @@ const ChannelsTable = () => {
...
@@ -98,7 +99,7 @@ const ChannelsTable = () => {
render
:
(
text
,
record
,
index
)
=>
{
render
:
(
text
,
record
,
index
)
=>
{
if
(
text
===
3
)
{
if
(
text
===
3
)
{
if
(
record
.
other_info
===
''
)
{
if
(
record
.
other_info
===
''
)
{
record
.
other_info
=
'{}'
record
.
other_info
=
'{}'
;
}
}
let
otherInfo
=
JSON
.
parse
(
record
.
other_info
);
let
otherInfo
=
JSON
.
parse
(
record
.
other_info
);
let
reason
=
otherInfo
[
'status_reason'
];
let
reason
=
otherInfo
[
'status_reason'
];
...
@@ -113,32 +114,33 @@ const ChannelsTable = () => {
...
@@ -113,32 +114,33 @@ const ChannelsTable = () => {
}
else
{
}
else
{
return
renderStatus
(
text
);
return
renderStatus
(
text
);
}
}
}
,
}
},
},
{
{
title
:
'响应时间'
,
title
:
'响应时间'
,
dataIndex
:
'response_time'
,
dataIndex
:
'response_time'
,
render
:
(
text
,
record
,
index
)
=>
{
render
:
(
text
,
record
,
index
)
=>
{
return
<
div
>
{
renderResponseTime
(
text
)}
<
/div>
;
return
<
div
>
{
renderResponseTime
(
text
)}
<
/div>
;
}
,
}
},
},
{
{
title
:
'已用/剩余'
,
title
:
'已用/剩余'
,
dataIndex
:
'expired_time'
,
dataIndex
:
'expired_time'
,
render
:
(
text
,
record
,
index
)
=>
{
render
:
(
text
,
record
,
index
)
=>
{
if
(
record
.
children
===
undefined
)
{
return
(
return
(
<
div
>
<
div
>
<
Space
spacing
=
{
1
}
>
<
Space
spacing
=
{
1
}
>
<
Tooltip
content
=
{
'已用额度'
}
>
<
Tooltip
content
=
{
'已用额度'
}
>
<
Tag
color
=
'white'
type
=
'ghost'
size
=
'large'
>
<
Tag
color
=
"white"
type
=
"ghost"
size
=
"large"
>
{
renderQuota
(
record
.
used_quota
)}
{
renderQuota
(
record
.
used_quota
)}
<
/Tag
>
<
/Tag
>
<
/Tooltip
>
<
/Tooltip
>
<
Tooltip
content
=
{
'剩余额度'
+
record
.
balance
+
',点击更新'
}
>
<
Tooltip
content
=
{
'剩余额度'
+
record
.
balance
+
',点击更新'
}
>
<
Tag
<
Tag
color
=
'white'
color
=
"white"
type
=
'ghost'
type
=
"ghost"
size
=
'large'
size
=
"large"
onClick
=
{()
=>
{
onClick
=
{()
=>
{
updateChannelBalance
(
record
);
updateChannelBalance
(
record
);
}}
}}
...
@@ -149,17 +151,25 @@ const ChannelsTable = () => {
...
@@ -149,17 +151,25 @@ const ChannelsTable = () => {
<
/Space
>
<
/Space
>
<
/div
>
<
/div
>
);
);
},
}
else
{
return
<
Tooltip
content
=
{
'已用额度'
}
>
<
Tag
color
=
"white"
type
=
"ghost"
size
=
"large"
>
{
renderQuota
(
record
.
used_quota
)}
<
/Tag
>
<
/Tooltip>
;
}
}
},
},
{
{
title
:
'优先级'
,
title
:
'优先级'
,
dataIndex
:
'priority'
,
dataIndex
:
'priority'
,
render
:
(
text
,
record
,
index
)
=>
{
render
:
(
text
,
record
,
index
)
=>
{
if
(
record
.
children
===
undefined
)
{
return
(
return
(
<
div
>
<
div
>
<
InputNumber
<
InputNumber
style
=
{{
width
:
70
}}
style
=
{{
width
:
70
}}
name
=
'priority'
name
=
"priority"
onBlur
=
{(
e
)
=>
{
onBlur
=
{(
e
)
=>
{
manageChannel
(
record
.
id
,
'priority'
,
record
,
e
.
target
.
value
);
manageChannel
(
record
.
id
,
'priority'
,
record
,
e
.
target
.
value
);
}}
}}
...
@@ -170,17 +180,23 @@ const ChannelsTable = () => {
...
@@ -170,17 +180,23 @@ const ChannelsTable = () => {
/
>
/
>
<
/div
>
<
/div
>
);
);
},
}
else
{
return
<>
<
Button
theme
=
"outline"
type
=
"primary"
>
修改
<
/Button
>
<
/>
;
}
}
},
},
{
{
title
:
'权重'
,
title
:
'权重'
,
dataIndex
:
'weight'
,
dataIndex
:
'weight'
,
render
:
(
text
,
record
,
index
)
=>
{
render
:
(
text
,
record
,
index
)
=>
{
if
(
record
.
children
===
undefined
)
{
return
(
return
(
<
div
>
<
div
>
<
InputNumber
<
InputNumber
style
=
{{
width
:
70
}}
style
=
{{
width
:
70
}}
name
=
'weight'
name
=
"weight"
onBlur
=
{(
e
)
=>
{
onBlur
=
{(
e
)
=>
{
manageChannel
(
record
.
id
,
'weight'
,
record
,
e
.
target
.
value
);
manageChannel
(
record
.
id
,
'weight'
,
record
,
e
.
target
.
value
);
}}
}}
...
@@ -191,19 +207,31 @@ const ChannelsTable = () => {
...
@@ -191,19 +207,31 @@ const ChannelsTable = () => {
/
>
/
>
<
/div
>
<
/div
>
);
);
},
}
else
{
return
(
<
Button
theme
=
"outline"
type
=
"primary"
>
修改
<
/Button
>
);
}
}
},
},
{
{
title
:
''
,
title
:
''
,
dataIndex
:
'operate'
,
dataIndex
:
'operate'
,
render
:
(
text
,
record
,
index
)
=>
(
render
:
(
text
,
record
,
index
)
=>
{
if
(
record
.
children
===
undefined
)
{
return
(
<
div
>
<
div
>
<
SplitButtonGroup
<
SplitButtonGroup
style
=
{{
marginRight
:
1
}}
style
=
{{
marginRight
:
1
}}
aria
-
label
=
'测试操作项目组'
aria
-
label
=
"测试单个渠道操作项目组"
>
>
<
Button
<
Button
theme
=
'light'
theme
=
"light"
onClick
=
{()
=>
{
onClick
=
{()
=>
{
testChannel
(
record
,
''
);
testChannel
(
record
,
''
);
}}
}}
...
@@ -211,21 +239,21 @@ const ChannelsTable = () => {
...
@@ -211,21 +239,21 @@ const ChannelsTable = () => {
测试
测试
<
/Button
>
<
/Button
>
<
Dropdown
<
Dropdown
trigger
=
'click'
trigger
=
"click"
position
=
'bottomRight'
position
=
"bottomRight"
menu
=
{
record
.
test_models
}
menu
=
{
record
.
test_models
}
>
>
<
Button
<
Button
style
=
{{
padding
:
'8px 4px'
}}
style
=
{{
padding
:
'8px 4px'
}}
type
=
'primary'
type
=
"primary"
icon
=
{
<
IconTreeTriangleDown
/>
}
icon
=
{
<
IconTreeTriangleDown
/>
}
><
/Button
>
><
/Button
>
<
/Dropdown
>
<
/Dropdown
>
<
/SplitButtonGroup
>
<
/SplitButtonGroup
>
{
/*<Button theme='light' type='primary' style={{marginRight: 1}} onClick={()=>testChannel(record)}>测试</Button>*/
}
{
/*<Button theme='light' type='primary' style={{marginRight: 1}} onClick={()=>testChannel(record)}>测试</Button>*/
}
<
Popconfirm
<
Popconfirm
title
=
'确定是否要删除此渠道?'
title
=
"确定是否要删除此渠道?"
content
=
'此修改将不可逆'
content
=
"此修改将不可逆"
okType
=
{
'danger'
}
okType
=
{
'danger'
}
position
=
{
'left'
}
position
=
{
'left'
}
onConfirm
=
{()
=>
{
onConfirm
=
{()
=>
{
...
@@ -234,14 +262,14 @@ const ChannelsTable = () => {
...
@@ -234,14 +262,14 @@ const ChannelsTable = () => {
});
});
}}
}}
>
>
<
Button
theme
=
'light'
type
=
'danger'
style
=
{{
marginRight
:
1
}}
>
<
Button
theme
=
"light"
type
=
"danger"
style
=
{{
marginRight
:
1
}}
>
删除
删除
<
/Button
>
<
/Button
>
<
/Popconfirm
>
<
/Popconfirm
>
{
record
.
status
===
1
?
(
{
record
.
status
===
1
?
(
<
Button
<
Button
theme
=
'light'
theme
=
"light"
type
=
'warning'
type
=
"warning"
style
=
{{
marginRight
:
1
}}
style
=
{{
marginRight
:
1
}}
onClick
=
{
async
()
=>
{
onClick
=
{
async
()
=>
{
manageChannel
(
record
.
id
,
'disable'
,
record
);
manageChannel
(
record
.
id
,
'disable'
,
record
);
...
@@ -251,8 +279,8 @@ const ChannelsTable = () => {
...
@@ -251,8 +279,8 @@ const ChannelsTable = () => {
<
/Button
>
<
/Button
>
)
:
(
)
:
(
<
Button
<
Button
theme
=
'light'
theme
=
"light"
type
=
'secondary'
type
=
"secondary"
style
=
{{
marginRight
:
1
}}
style
=
{{
marginRight
:
1
}}
onClick
=
{
async
()
=>
{
onClick
=
{
async
()
=>
{
manageChannel
(
record
.
id
,
'enable'
,
record
);
manageChannel
(
record
.
id
,
'enable'
,
record
);
...
@@ -262,8 +290,8 @@ const ChannelsTable = () => {
...
@@ -262,8 +290,8 @@ const ChannelsTable = () => {
<
/Button
>
<
/Button
>
)}
)}
<
Button
<
Button
theme
=
'light'
theme
=
"light"
type
=
'tertiary'
type
=
"tertiary"
style
=
{{
marginRight
:
1
}}
style
=
{{
marginRight
:
1
}}
onClick
=
{()
=>
{
onClick
=
{()
=>
{
setEditingChannel
(
record
);
setEditingChannel
(
record
);
...
@@ -273,21 +301,59 @@ const ChannelsTable = () => {
...
@@ -273,21 +301,59 @@ const ChannelsTable = () => {
编辑
编辑
<
/Button
>
<
/Button
>
<
Popconfirm
<
Popconfirm
title
=
'确定是否要复制此渠道?'
title
=
"确定是否要复制此渠道?"
content
=
'复制渠道的所有信息'
content
=
"复制渠道的所有信息"
okType
=
{
'danger'
}
okType
=
{
'danger'
}
position
=
{
'left'
}
position
=
{
'left'
}
onConfirm
=
{
async
()
=>
{
onConfirm
=
{
async
()
=>
{
copySelectedChannel
(
record
.
id
);
copySelectedChannel
(
record
.
id
);
}}
}}
>
>
<
Button
theme
=
'light'
type
=
'primary'
style
=
{{
marginRight
:
1
}}
>
<
Button
theme
=
"light"
type
=
"primary"
style
=
{{
marginRight
:
1
}}
>
复制
复制
<
/Button
>
<
/Button
>
<
/Popconfirm
>
<
/Popconfirm
>
<
/div
>
<
/div
>
),
);
},
}
else
{
return
(
<>
<
Button
theme
=
"light"
type
=
"secondary"
style
=
{{
marginRight
:
1
}}
onClick
=
{
async
()
=>
{
manageTag
(
record
.
key
,
'enable'
);
}}
>
启用全部
<
/Button
>
<
Button
theme
=
"light"
type
=
"warning"
style
=
{{
marginRight
:
1
}}
onClick
=
{
async
()
=>
{
manageTag
(
record
.
key
,
'disable'
);
}}
>
禁用全部
<
/Button
>
<
Button
theme
=
"light"
type
=
"tertiary"
style
=
{{
marginRight
:
1
}}
onClick
=
{()
=>
{
setShowEditTag
(
true
);
setEditingTag
(
record
.
key
);
}}
>
编辑
<
/Button
>
<
/
>
);
}
}
}
];
];
const
[
channels
,
setChannels
]
=
useState
([]);
const
[
channels
,
setChannels
]
=
useState
([]);
...
@@ -301,15 +367,17 @@ const ChannelsTable = () => {
...
@@ -301,15 +367,17 @@ const ChannelsTable = () => {
const
[
updatingBalance
,
setUpdatingBalance
]
=
useState
(
false
);
const
[
updatingBalance
,
setUpdatingBalance
]
=
useState
(
false
);
const
[
pageSize
,
setPageSize
]
=
useState
(
ITEMS_PER_PAGE
);
const
[
pageSize
,
setPageSize
]
=
useState
(
ITEMS_PER_PAGE
);
const
[
showPrompt
,
setShowPrompt
]
=
useState
(
const
[
showPrompt
,
setShowPrompt
]
=
useState
(
shouldShowPrompt
(
'channel-test'
)
,
shouldShowPrompt
(
'channel-test'
)
);
);
const
[
channelCount
,
setChannelCount
]
=
useState
(
pageSize
);
const
[
channelCount
,
setChannelCount
]
=
useState
(
pageSize
);
const
[
groupOptions
,
setGroupOptions
]
=
useState
([]);
const
[
groupOptions
,
setGroupOptions
]
=
useState
([]);
const
[
showEdit
,
setShowEdit
]
=
useState
(
false
);
const
[
showEdit
,
setShowEdit
]
=
useState
(
false
);
const
[
enableBatchDelete
,
setEnableBatchDelete
]
=
useState
(
false
);
const
[
enableBatchDelete
,
setEnableBatchDelete
]
=
useState
(
false
);
const
[
editingChannel
,
setEditingChannel
]
=
useState
({
const
[
editingChannel
,
setEditingChannel
]
=
useState
({
id
:
undefined
,
id
:
undefined
});
});
const
[
showEditTag
,
setShowEditTag
]
=
useState
(
false
);
const
[
editingTag
,
setEditingTag
]
=
useState
(
''
);
const
[
selectedChannels
,
setSelectedChannels
]
=
useState
([]);
const
[
selectedChannels
,
setSelectedChannels
]
=
useState
([]);
const
removeRecord
=
(
id
)
=>
{
const
removeRecord
=
(
id
)
=>
{
...
@@ -325,14 +393,12 @@ const ChannelsTable = () => {
...
@@ -325,14 +393,12 @@ const ChannelsTable = () => {
};
};
const
setChannelFormat
=
(
channels
)
=>
{
const
setChannelFormat
=
(
channels
)
=>
{
let
channelDates
=
[];
let
channelTags
=
{};
for
(
let
i
=
0
;
i
<
channels
.
length
;
i
++
)
{
for
(
let
i
=
0
;
i
<
channels
.
length
;
i
++
)
{
// if (channels[i].type === 8) {
// showWarning(
// '检测到您使用了“自定义渠道”类型,请更换为“OpenAI”渠道类型!',
// );
// showWarning('下个版本将不再支持“自定义渠道”类型!');
// }
channels
[
i
].
key
=
''
+
channels
[
i
].
id
;
channels
[
i
].
key
=
''
+
channels
[
i
].
id
;
if
(
channels
[
i
].
tag
===
''
||
channels
[
i
].
tag
===
null
)
{
let
test_models
=
[];
let
test_models
=
[];
channels
[
i
].
models
.
split
(
','
).
forEach
((
item
,
index
)
=>
{
channels
[
i
].
models
.
split
(
','
).
forEach
((
item
,
index
)
=>
{
test_models
.
push
({
test_models
.
push
({
...
@@ -340,24 +406,69 @@ const ChannelsTable = () => {
...
@@ -340,24 +406,69 @@ const ChannelsTable = () => {
name
:
item
,
name
:
item
,
onClick
:
()
=>
{
onClick
:
()
=>
{
testChannel
(
channels
[
i
],
item
);
testChannel
(
channels
[
i
],
item
);
},
}
});
});
});
});
channels
[
i
].
test_models
=
test_models
;
channels
[
i
].
test_models
=
test_models
;
channelDates
.
push
(
channels
[
i
]);
}
else
{
let
tag
=
channels
[
i
].
tag
;
// find from channelTags
let
tagIndex
=
channelTags
[
tag
];
let
tagChannelDates
=
undefined
;
if
(
tagIndex
===
undefined
)
{
// not found, create a new tag
channelTags
[
tag
]
=
1
;
tagChannelDates
=
{
key
:
tag
,
id
:
tag
,
tag
:
tag
,
name
:
'标签:'
+
tag
,
group
:
''
,
used_quota
:
0
,
response_time
:
0
};
tagChannelDates
.
children
=
[];
channelDates
.
push
(
tagChannelDates
);
}
else
{
// found, add to the tag
tagChannelDates
=
channelDates
.
find
((
item
)
=>
item
.
key
===
tag
);
}
if
(
tagChannelDates
.
group
===
''
)
{
tagChannelDates
.
group
=
channels
[
i
].
group
;
}
else
{
let
channelGroupsStr
=
channels
[
i
].
group
;
channelGroupsStr
.
split
(
','
).
forEach
((
item
,
index
)
=>
{
if
(
tagChannelDates
.
group
.
indexOf
(
item
)
===
-
1
)
{
tagChannelDates
.
group
+=
item
+
','
;
}
});
}
tagChannelDates
.
children
.
push
(
channels
[
i
]);
if
(
channels
[
i
].
status
===
1
)
{
tagChannelDates
.
status
=
1
;
}
tagChannelDates
.
used_quota
+=
channels
[
i
].
used_quota
;
tagChannelDates
.
response_time
+=
channels
[
i
].
response_time
;
tagChannelDates
.
response_time
=
tagChannelDates
.
response_time
/
2
;
}
}
}
// data.key = '' + data.id
// data.key = '' + data.id
setChannels
(
channels
);
setChannels
(
channel
Date
s
);
if
(
channels
.
length
>=
pageSize
)
{
if
(
channel
Date
s
.
length
>=
pageSize
)
{
setChannelCount
(
channels
.
length
+
pageSize
);
setChannelCount
(
channel
Date
s
.
length
+
pageSize
);
}
else
{
}
else
{
setChannelCount
(
channels
.
length
);
setChannelCount
(
channel
Date
s
.
length
);
}
}
};
};
const
loadChannels
=
async
(
startIdx
,
pageSize
,
idSort
)
=>
{
const
loadChannels
=
async
(
startIdx
,
pageSize
,
idSort
)
=>
{
setLoading
(
true
);
setLoading
(
true
);
const
res
=
await
API
.
get
(
const
res
=
await
API
.
get
(
`/api/channel/?p=
${
startIdx
}
&page_size=
${
pageSize
}
&id_sort=
${
idSort
}
`
,
`/api/channel/?p=
${
startIdx
}
&page_size=
${
pageSize
}
&id_sort=
${
idSort
}
`
);
);
if
(
res
===
undefined
)
{
if
(
res
===
undefined
)
{
return
;
return
;
...
@@ -379,7 +490,7 @@ const ChannelsTable = () => {
...
@@ -379,7 +490,7 @@ const ChannelsTable = () => {
const
copySelectedChannel
=
async
(
id
)
=>
{
const
copySelectedChannel
=
async
(
id
)
=>
{
const
channelToCopy
=
channels
.
find
(
const
channelToCopy
=
channels
.
find
(
(
channel
)
=>
String
(
channel
.
id
)
===
String
(
id
)
,
(
channel
)
=>
String
(
channel
.
id
)
===
String
(
id
)
);
);
console
.
log
(
channelToCopy
);
console
.
log
(
channelToCopy
);
channelToCopy
.
name
+=
'_复制'
;
channelToCopy
.
name
+=
'_复制'
;
...
@@ -472,29 +583,63 @@ const ChannelsTable = () => {
...
@@ -472,29 +583,63 @@ const ChannelsTable = () => {
}
}
};
};
const
manageTag
=
async
(
tag
,
action
)
=>
{
console
.
log
(
tag
,
action
);
let
res
;
switch
(
action
)
{
case
'enable'
:
res
=
await
API
.
post
(
'/api/channel/tag/enabled'
,
{
tag
:
tag
});
break
;
case
'disable'
:
res
=
await
API
.
post
(
'/api/channel/tag/disabled'
,
{
tag
:
tag
});
break
;
}
const
{
success
,
message
}
=
res
.
data
;
if
(
success
)
{
showSuccess
(
'操作成功完成!'
);
let
newChannels
=
[...
channels
];
for
(
let
i
=
0
;
i
<
newChannels
.
length
;
i
++
)
{
if
(
newChannels
[
i
].
tag
===
tag
)
{
let
status
=
action
===
'enable'
?
1
:
2
;
newChannels
[
i
]?.
children
?.
forEach
((
channel
)
=>
{
channel
.
status
=
status
;
});
newChannels
[
i
].
status
=
status
;
}
}
setChannels
(
newChannels
);
}
else
{
showError
(
message
);
}
};
const
renderStatus
=
(
status
)
=>
{
const
renderStatus
=
(
status
)
=>
{
switch
(
status
)
{
switch
(
status
)
{
case
1
:
case
1
:
return
(
return
(
<
Tag
size
=
'large'
color
=
'green'
>
<
Tag
size
=
"large"
color
=
"green"
>
已启用
已启用
<
/Tag
>
<
/Tag
>
);
);
case
2
:
case
2
:
return
(
return
(
<
Tag
size
=
'large'
color
=
'yellow'
>
<
Tag
size
=
"large"
color
=
"yellow"
>
已禁用
已禁用
<
/Tag
>
<
/Tag
>
);
);
case
3
:
case
3
:
return
(
return
(
<
Tag
size
=
'large'
color
=
'yellow'
>
<
Tag
size
=
"large"
color
=
"yellow"
>
自动禁用
自动禁用
<
/Tag
>
<
/Tag
>
);
);
default
:
default
:
return
(
return
(
<
Tag
size
=
'large'
color
=
'grey'
>
<
Tag
size
=
"large"
color
=
"grey"
>
未知状态
未知状态
<
/Tag
>
<
/Tag
>
);
);
...
@@ -506,31 +651,31 @@ const ChannelsTable = () => {
...
@@ -506,31 +651,31 @@ const ChannelsTable = () => {
time
=
time
.
toFixed
(
2
)
+
' 秒'
;
time
=
time
.
toFixed
(
2
)
+
' 秒'
;
if
(
responseTime
===
0
)
{
if
(
responseTime
===
0
)
{
return
(
return
(
<
Tag
size
=
'large'
color
=
'grey'
>
<
Tag
size
=
"large"
color
=
"grey"
>
未测试
未测试
<
/Tag
>
<
/Tag
>
);
);
}
else
if
(
responseTime
<=
1000
)
{
}
else
if
(
responseTime
<=
1000
)
{
return
(
return
(
<
Tag
size
=
'large'
color
=
'green'
>
<
Tag
size
=
"large"
color
=
"green"
>
{
time
}
{
time
}
<
/Tag
>
<
/Tag
>
);
);
}
else
if
(
responseTime
<=
3000
)
{
}
else
if
(
responseTime
<=
3000
)
{
return
(
return
(
<
Tag
size
=
'large'
color
=
'lime'
>
<
Tag
size
=
"large"
color
=
"lime"
>
{
time
}
{
time
}
<
/Tag
>
<
/Tag
>
);
);
}
else
if
(
responseTime
<=
5000
)
{
}
else
if
(
responseTime
<=
5000
)
{
return
(
return
(
<
Tag
size
=
'large'
color
=
'yellow'
>
<
Tag
size
=
"large"
color
=
"yellow"
>
{
time
}
{
time
}
<
/Tag
>
<
/Tag
>
);
);
}
else
{
}
else
{
return
(
return
(
<
Tag
size
=
'large'
color
=
'red'
>
<
Tag
size
=
"large"
color
=
"red"
>
{
time
}
{
time
}
<
/Tag
>
<
/Tag
>
);
);
...
@@ -546,7 +691,7 @@ const ChannelsTable = () => {
...
@@ -546,7 +691,7 @@ const ChannelsTable = () => {
}
}
setSearching
(
true
);
setSearching
(
true
);
const
res
=
await
API
.
get
(
const
res
=
await
API
.
get
(
`/api/channel/search?keyword=
${
searchKeyword
}
&group=
${
searchGroup
}
&model=
${
searchModel
}
`
,
`/api/channel/search?keyword=
${
searchKeyword
}
&group=
${
searchGroup
}
&model=
${
searchModel
}
`
);
);
const
{
success
,
message
,
data
}
=
res
.
data
;
const
{
success
,
message
,
data
}
=
res
.
data
;
if
(
success
)
{
if
(
success
)
{
...
@@ -649,14 +794,15 @@ const ChannelsTable = () => {
...
@@ -649,14 +794,15 @@ const ChannelsTable = () => {
let
pageData
=
channels
.
slice
(
let
pageData
=
channels
.
slice
(
(
activePage
-
1
)
*
pageSize
,
(
activePage
-
1
)
*
pageSize
,
activePage
*
pageSize
,
activePage
*
pageSize
);
);
const
handlePageChange
=
(
page
)
=>
{
const
handlePageChange
=
(
page
)
=>
{
setActivePage
(
page
);
setActivePage
(
page
);
if
(
page
===
Math
.
ceil
(
channels
.
length
/
pageSize
)
+
1
)
{
if
(
page
===
Math
.
ceil
(
channels
.
length
/
pageSize
)
+
1
)
{
// In this case we have to load more data and then append them.
// In this case we have to load more data and then append them.
loadChannels
(
page
-
1
,
pageSize
,
idSort
).
then
((
r
)
=>
{});
loadChannels
(
page
-
1
,
pageSize
,
idSort
).
then
((
r
)
=>
{
});
}
}
};
};
...
@@ -682,8 +828,8 @@ const ChannelsTable = () => {
...
@@ -682,8 +828,8 @@ const ChannelsTable = () => {
setGroupOptions
(
setGroupOptions
(
res
.
data
.
data
.
map
((
group
)
=>
({
res
.
data
.
data
.
map
((
group
)
=>
({
label
:
group
,
label
:
group
,
value
:
group
,
value
:
group
}))
,
}))
);
);
}
catch
(
error
)
{
}
catch
(
error
)
{
showError
(
error
.
message
);
showError
(
error
.
message
);
...
@@ -698,8 +844,8 @@ const ChannelsTable = () => {
...
@@ -698,8 +844,8 @@ const ChannelsTable = () => {
if
(
record
.
status
!==
1
)
{
if
(
record
.
status
!==
1
)
{
return
{
return
{
style
:
{
style
:
{
background
:
'var(--semi-color-disabled-border)'
,
background
:
'var(--semi-color-disabled-border)'
}
,
}
};
};
}
else
{
}
else
{
return
{};
return
{};
...
@@ -708,6 +854,12 @@ const ChannelsTable = () => {
...
@@ -708,6 +854,12 @@ const ChannelsTable = () => {
return
(
return
(
<>
<>
<
EditTagModal
visible
=
{
showEditTag
}
tag
=
{
editingTag
}
handleClose
=
{()
=>
setShowEditTag
(
false
)}
refresh
=
{
refresh
}
/
>
<
EditChannel
<
EditChannel
refresh
=
{
refresh
}
refresh
=
{
refresh
}
visible
=
{
showEdit
}
visible
=
{
showEdit
}
...
@@ -718,14 +870,14 @@ const ChannelsTable = () => {
...
@@ -718,14 +870,14 @@ const ChannelsTable = () => {
onSubmit
=
{()
=>
{
onSubmit
=
{()
=>
{
searchChannels
(
searchKeyword
,
searchGroup
,
searchModel
);
searchChannels
(
searchKeyword
,
searchGroup
,
searchModel
);
}}
}}
labelPosition
=
'left'
labelPosition
=
"left"
>
>
<
div
style
=
{{
display
:
'flex'
}}
>
<
div
style
=
{{
display
:
'flex'
}}
>
<
Space
>
<
Space
>
<
Form
.
Input
<
Form
.
Input
field
=
'search_keyword'
field
=
"search_keyword"
label
=
'搜索渠道关键词'
label
=
"搜索渠道关键词"
placeholder
=
'ID,名称和密钥 ...'
placeholder
=
"ID,名称和密钥 ..."
value
=
{
searchKeyword
}
value
=
{
searchKeyword
}
loading
=
{
searching
}
loading
=
{
searching
}
onChange
=
{(
v
)
=>
{
onChange
=
{(
v
)
=>
{
...
@@ -733,9 +885,9 @@ const ChannelsTable = () => {
...
@@ -733,9 +885,9 @@ const ChannelsTable = () => {
}}
}}
/
>
/
>
<
Form
.
Input
<
Form
.
Input
field
=
'search_model'
field
=
"search_model"
label
=
'模型'
label
=
"模型"
placeholder
=
'模型关键字'
placeholder
=
"模型关键字"
value
=
{
searchModel
}
value
=
{
searchModel
}
loading
=
{
searching
}
loading
=
{
searching
}
onChange
=
{(
v
)
=>
{
onChange
=
{(
v
)
=>
{
...
@@ -743,9 +895,9 @@ const ChannelsTable = () => {
...
@@ -743,9 +895,9 @@ const ChannelsTable = () => {
}}
}}
/
>
/
>
<
Form
.
Select
<
Form
.
Select
field
=
'group'
field
=
"group"
label
=
'分组'
label
=
"分组"
optionList
=
{[{
label
:
'选择分组'
,
value
:
null
},
...
groupOptions
]}
optionList
=
{[{
label
:
'选择分组'
,
value
:
null
},
...
groupOptions
]}
initValue
=
{
null
}
initValue
=
{
null
}
onChange
=
{(
v
)
=>
{
onChange
=
{(
v
)
=>
{
setSearchGroup
(
v
);
setSearchGroup
(
v
);
...
@@ -753,35 +905,35 @@ const ChannelsTable = () => {
...
@@ -753,35 +905,35 @@ const ChannelsTable = () => {
}}
}}
/
>
/
>
<
Button
<
Button
label
=
'查询'
label
=
"查询"
type
=
'primary'
type
=
"primary"
htmlType
=
'submit'
htmlType
=
"submit"
className
=
'btn-margin-right'
className
=
"btn-margin-right"
style
=
{{
marginRight
:
8
}}
style
=
{{
marginRight
:
8
}}
>
>
查询
查询
<
/Button
>
<
/Button
>
<
/Space
>
<
/Space
>
<
/div
>
<
/div
>
<
/Form
>
<
/Form
>
<
Divider
style
=
{{
marginBottom
:
15
}}
/
>
<
Divider
style
=
{{
marginBottom
:
15
}}
/
>
<
div
<
div
style
=
{{
style
=
{{
display
:
isMobile
()
?
''
:
'flex'
,
display
:
isMobile
()
?
''
:
'flex'
,
marginTop
:
isMobile
()
?
0
:
-
45
,
marginTop
:
isMobile
()
?
0
:
-
45
,
zIndex
:
999
,
zIndex
:
999
,
pointerEvents
:
'none'
,
pointerEvents
:
'none'
}}
}}
>
>
<
Space
<
Space
style
=
{{
pointerEvents
:
'auto'
,
marginTop
:
isMobile
()
?
0
:
45
}}
style
=
{{
pointerEvents
:
'auto'
,
marginTop
:
isMobile
()
?
0
:
45
}}
>
>
<
Typography
.
Text
strong
>
使用
ID
排序
<
/Typography.Text
>
<
Typography
.
Text
strong
>
使用
ID
排序
<
/Typography.Text
>
<
Switch
<
Switch
checked
=
{
idSort
}
checked
=
{
idSort
}
label
=
'使用ID排序'
label
=
"使用ID排序"
uncheckedText
=
'关'
uncheckedText
=
"关"
aria
-
label
=
'是否用ID排序'
aria
-
label
=
"是否用ID排序"
onChange
=
{(
v
)
=>
{
onChange
=
{(
v
)
=>
{
localStorage
.
setItem
(
'id-sort'
,
v
+
''
);
localStorage
.
setItem
(
'id-sort'
,
v
+
''
);
setIdSort
(
v
);
setIdSort
(
v
);
...
@@ -793,12 +945,12 @@ const ChannelsTable = () => {
...
@@ -793,12 +945,12 @@ const ChannelsTable = () => {
}}
}}
><
/Switch
>
><
/Switch
>
<
Button
<
Button
theme
=
'light'
theme
=
"light"
type
=
'primary'
type
=
"primary"
style
=
{{
marginRight
:
8
}}
style
=
{{
marginRight
:
8
}}
onClick
=
{()
=>
{
onClick
=
{()
=>
{
setEditingChannel
({
setEditingChannel
({
id
:
undefined
,
id
:
undefined
});
});
setShowEdit
(
true
);
setShowEdit
(
true
);
}}
}}
...
@@ -806,59 +958,59 @@ const ChannelsTable = () => {
...
@@ -806,59 +958,59 @@ const ChannelsTable = () => {
添加渠道
添加渠道
<
/Button
>
<
/Button
>
<
Popconfirm
<
Popconfirm
title
=
'确定?'
title
=
"确定?"
okType
=
{
'warning'
}
okType
=
{
'warning'
}
onConfirm
=
{
testAllChannels
}
onConfirm
=
{
testAllChannels
}
position
=
{
isMobile
()
?
'top'
:
'top'
}
position
=
{
isMobile
()
?
'top'
:
'top'
}
>
>
<
Button
theme
=
'light'
type
=
'warning'
style
=
{{
marginRight
:
8
}}
>
<
Button
theme
=
"light"
type
=
"warning"
style
=
{{
marginRight
:
8
}}
>
测试所有通道
测试所有通道
<
/Button
>
<
/Button
>
<
/Popconfirm
>
<
/Popconfirm
>
<
Popconfirm
<
Popconfirm
title
=
'确定?'
title
=
"确定?"
okType
=
{
'secondary'
}
okType
=
{
'secondary'
}
onConfirm
=
{
updateAllChannelsBalance
}
onConfirm
=
{
updateAllChannelsBalance
}
>
>
<
Button
theme
=
'light'
type
=
'secondary'
style
=
{{
marginRight
:
8
}}
>
<
Button
theme
=
"light"
type
=
"secondary"
style
=
{{
marginRight
:
8
}}
>
更新所有已启用通道余额
更新所有已启用通道余额
<
/Button
>
<
/Button
>
<
/Popconfirm
>
<
/Popconfirm
>
<
Popconfirm
<
Popconfirm
title
=
'确定是否要删除禁用通道?'
title
=
"确定是否要删除禁用通道?"
content
=
'此修改将不可逆'
content
=
"此修改将不可逆"
okType
=
{
'danger'
}
okType
=
{
'danger'
}
onConfirm
=
{
deleteAllDisabledChannels
}
onConfirm
=
{
deleteAllDisabledChannels
}
>
>
<
Button
theme
=
'light'
type
=
'danger'
style
=
{{
marginRight
:
8
}}
>
<
Button
theme
=
"light"
type
=
"danger"
style
=
{{
marginRight
:
8
}}
>
删除禁用通道
删除禁用通道
<
/Button
>
<
/Button
>
<
/Popconfirm
>
<
/Popconfirm
>
<
Button
<
Button
theme
=
'light'
theme
=
"light"
type
=
'primary'
type
=
"primary"
style
=
{{
marginRight
:
8
}}
style
=
{{
marginRight
:
8
}}
onClick
=
{
refresh
}
onClick
=
{
refresh
}
>
>
刷新
刷新
<
/Button
>
<
/Button
>
<
/Space
>
<
/Space
>
<
/div
>
<
/div
>
<
div
style
=
{{
marginTop
:
20
}}
>
<
div
style
=
{{
marginTop
:
20
}}
>
<
Space
>
<
Space
>
<
Typography
.
Text
strong
>
开启批量删除
<
/Typography.Text
>
<
Typography
.
Text
strong
>
开启批量删除
<
/Typography.Text
>
<
Switch
<
Switch
label
=
'开启批量删除'
label
=
"开启批量删除"
uncheckedText
=
'关'
uncheckedText
=
"关"
aria
-
label
=
'是否开启批量删除'
aria
-
label
=
"是否开启批量删除"
onChange
=
{(
v
)
=>
{
onChange
=
{(
v
)
=>
{
setEnableBatchDelete
(
v
);
setEnableBatchDelete
(
v
);
}}
}}
><
/Switch
>
><
/Switch
>
<
Popconfirm
<
Popconfirm
title
=
'确定是否要删除所选通道?'
title
=
"确定是否要删除所选通道?"
content
=
'此修改将不可逆'
content
=
"此修改将不可逆"
okType
=
{
'danger'
}
okType
=
{
'danger'
}
onConfirm
=
{
batchDeleteChannels
}
onConfirm
=
{
batchDeleteChannels
}
disabled
=
{
!
enableBatchDelete
}
disabled
=
{
!
enableBatchDelete
}
...
@@ -866,30 +1018,31 @@ const ChannelsTable = () => {
...
@@ -866,30 +1018,31 @@ const ChannelsTable = () => {
>
>
<
Button
<
Button
disabled
=
{
!
enableBatchDelete
}
disabled
=
{
!
enableBatchDelete
}
theme
=
'light'
theme
=
"light"
type
=
'danger'
type
=
"danger"
style
=
{{
marginRight
:
8
}}
style
=
{{
marginRight
:
8
}}
>
>
删除所选通道
删除所选通道
<
/Button
>
<
/Button
>
<
/Popconfirm
>
<
/Popconfirm
>
<
Popconfirm
<
Popconfirm
title
=
'确定是否要修复数据库一致性?'
title
=
"确定是否要修复数据库一致性?"
content
=
'进行该操作时,可能导致渠道访问错误,请仅在数据库出现问题时使用'
content
=
"进行该操作时,可能导致渠道访问错误,请仅在数据库出现问题时使用"
okType
=
{
'warning'
}
okType
=
{
'warning'
}
onConfirm
=
{
fixChannelsAbilities
}
onConfirm
=
{
fixChannelsAbilities
}
position
=
{
'top'
}
position
=
{
'top'
}
>
>
<
Button
theme
=
'light'
type
=
'secondary'
style
=
{{
marginRight
:
8
}}
>
<
Button
theme
=
"light"
type
=
"secondary"
style
=
{{
marginRight
:
8
}}
>
修复数据库一致性
修复数据库一致性
<
/Button
>
<
/Button
>
<
/Popconfirm
>
<
/Popconfirm
>
<
/Space
>
<
/Space
>
<
/div
>
<
/div
>
<
Table
<
Table
className
=
{
'channel-table'
}
className
=
{
'channel-table'
}
style
=
{{
marginTop
:
15
}}
style
=
{{
marginTop
:
15
}}
columns
=
{
columns
}
columns
=
{
columns
}
dataSource
=
{
pageData
}
dataSource
=
{
pageData
}
pagination
=
{{
pagination
=
{{
...
@@ -902,7 +1055,7 @@ const ChannelsTable = () => {
...
@@ -902,7 +1055,7 @@ const ChannelsTable = () => {
onPageSizeChange
:
(
size
)
=>
{
onPageSizeChange
:
(
size
)
=>
{
handlePageSizeChange
(
size
).
then
();
handlePageSizeChange
(
size
).
then
();
},
},
onPageChange
:
handlePageChange
,
onPageChange
:
handlePageChange
}}
}}
loading
=
{
loading
}
loading
=
{
loading
}
onRow
=
{
handleRow
}
onRow
=
{
handleRow
}
...
@@ -912,7 +1065,7 @@ const ChannelsTable = () => {
...
@@ -912,7 +1065,7 @@ const ChannelsTable = () => {
onChange
:
(
selectedRowKeys
,
selectedRows
)
=>
{
onChange
:
(
selectedRowKeys
,
selectedRows
)
=>
{
// console.log(`selectedRowKeys: ${selectedRowKeys}`, 'selectedRows: ', selectedRows);
// console.log(`selectedRowKeys: ${selectedRowKeys}`, 'selectedRows: ', selectedRows);
setSelectedChannels
(
selectedRows
);
setSelectedChannels
(
selectedRows
);
},
}
}
}
:
null
:
null
}
}
...
...
web/src/components/TextInput.js
0 → 100644
View file @
325de987
import
{
Input
,
Typography
}
from
'@douyinfe/semi-ui'
;
import
React
from
'react'
;
const
TextInput
=
({
label
,
name
,
value
,
onChange
,
placeholder
,
type
=
'text'
})
=>
{
return
(
<>
<
div
style
=
{{
marginTop
:
10
}}
>
<
Typography
.
Text
strong
>
{
label
}
<
/Typography.Text
>
<
/div
>
<
Input
name
=
{
name
}
placeholder
=
{
placeholder
}
onChange
=
{(
value
)
=>
onChange
(
value
)}
value
=
{
value
}
autoComplete
=
"new-password"
/>
<
/
>
);
}
export
default
TextInput
;
\ No newline at end of file
web/src/helpers/render.js
View file @
325de987
...
@@ -67,6 +67,8 @@ export function renderQuotaNumberWithDigit(num, digits = 2) {
...
@@ -67,6 +67,8 @@ export function renderQuotaNumberWithDigit(num, digits = 2) {
}
}
export
function
renderNumberWithPoint
(
num
)
{
export
function
renderNumberWithPoint
(
num
)
{
if
(
num
===
undefined
)
return
''
;
num
=
num
.
toFixed
(
2
);
num
=
num
.
toFixed
(
2
);
if
(
num
>=
100000
)
{
if
(
num
>=
100000
)
{
// Convert number to string to manipulate it
// Convert number to string to manipulate it
...
...
web/src/pages/Channel/EditChannel.js
View file @
325de987
...
@@ -6,7 +6,7 @@ import {
...
@@ -6,7 +6,7 @@ import {
showError
,
showError
,
showInfo
,
showInfo
,
showSuccess
,
showSuccess
,
verifyJSON
,
verifyJSON
}
from
'../../helpers'
;
}
from
'../../helpers'
;
import
{
CHANNEL_OPTIONS
}
from
'../../constants'
;
import
{
CHANNEL_OPTIONS
}
from
'../../constants'
;
import
Title
from
'@douyinfe/semi-ui/lib/es/typography/title'
;
import
Title
from
'@douyinfe/semi-ui/lib/es/typography/title'
;
...
@@ -21,7 +21,7 @@ import {
...
@@ -21,7 +21,7 @@ import {
Select
,
Select
,
TextArea
,
TextArea
,
Checkbox
,
Checkbox
,
Banner
,
Banner
}
from
'@douyinfe/semi-ui'
;
}
from
'@douyinfe/semi-ui'
;
import
{
Divider
}
from
'semantic-ui-react'
;
import
{
Divider
}
from
'semantic-ui-react'
;
import
{
getChannelModels
,
loadChannelModels
}
from
'../../components/utils.js'
;
import
{
getChannelModels
,
loadChannelModels
}
from
'../../components/utils.js'
;
...
@@ -30,19 +30,19 @@ import axios from 'axios';
...
@@ -30,19 +30,19 @@ import axios from 'axios';
const
MODEL_MAPPING_EXAMPLE
=
{
const
MODEL_MAPPING_EXAMPLE
=
{
'gpt-3.5-turbo-0301'
:
'gpt-3.5-turbo'
,
'gpt-3.5-turbo-0301'
:
'gpt-3.5-turbo'
,
'gpt-4-0314'
:
'gpt-4'
,
'gpt-4-0314'
:
'gpt-4'
,
'gpt-4-32k-0314'
:
'gpt-4-32k'
,
'gpt-4-32k-0314'
:
'gpt-4-32k'
};
};
const
STATUS_CODE_MAPPING_EXAMPLE
=
{
const
STATUS_CODE_MAPPING_EXAMPLE
=
{
400
:
'500'
,
400
:
'500'
};
};
const
REGION_EXAMPLE
=
{
const
REGION_EXAMPLE
=
{
"default"
:
"us-central1"
,
'default'
:
'us-central1'
,
"claude-3-5-sonnet-20240620"
:
"europe-west1"
'claude-3-5-sonnet-20240620'
:
'europe-west1'
}
}
;
const
fetchButtonTips
=
"1. 新建渠道时,请求通过当前浏览器发出;2. 编辑已有渠道,请求通过后端服务器发出"
const
fetchButtonTips
=
'1. 新建渠道时,请求通过当前浏览器发出;2. 编辑已有渠道,请求通过后端服务器发出'
;
function
type2secretPrompt
(
type
)
{
function
type2secretPrompt
(
type
)
{
// inputs.type === 15 ? '按照如下格式输入:APIKey|SecretKey' : (inputs.type === 18 ? '按照如下格式输入:APPID|APISecret|APIKey' : '请输入渠道对应的鉴权密钥')
// inputs.type === 15 ? '按照如下格式输入:APIKey|SecretKey' : (inputs.type === 18 ? '按照如下格式输入:APPID|APISecret|APIKey' : '请输入渠道对应的鉴权密钥')
...
@@ -84,6 +84,9 @@ const EditChannel = (props) => {
...
@@ -84,6 +84,9 @@ const EditChannel = (props) => {
auto_ban
:
1
,
auto_ban
:
1
,
test_model
:
''
,
test_model
:
''
,
groups
:
[
'default'
],
groups
:
[
'default'
],
priority
:
0
,
weight
:
0
,
tag
:
''
};
};
const
[
batch
,
setBatch
]
=
useState
(
false
);
const
[
batch
,
setBatch
]
=
useState
(
false
);
const
[
autoBan
,
setAutoBan
]
=
useState
(
true
);
const
[
autoBan
,
setAutoBan
]
=
useState
(
true
);
...
@@ -108,7 +111,7 @@ const EditChannel = (props) => {
...
@@ -108,7 +111,7 @@ const EditChannel = (props) => {
'mj_blend'
,
'mj_blend'
,
'mj_upscale'
,
'mj_upscale'
,
'mj_describe'
,
'mj_describe'
,
'mj_uploads'
,
'mj_uploads'
];
];
break
;
break
;
case
5
:
case
5
:
...
@@ -128,13 +131,13 @@ const EditChannel = (props) => {
...
@@ -128,13 +131,13 @@ const EditChannel = (props) => {
'mj_high_variation'
,
'mj_high_variation'
,
'mj_low_variation'
,
'mj_low_variation'
,
'mj_pan'
,
'mj_pan'
,
'mj_uploads'
,
'mj_uploads'
];
];
break
;
break
;
case
36
:
case
36
:
localModels
=
[
localModels
=
[
'suno_music'
,
'suno_music'
,
'suno_lyrics'
,
'suno_lyrics'
];
];
break
;
break
;
default
:
default
:
...
@@ -171,7 +174,7 @@ const EditChannel = (props) => {
...
@@ -171,7 +174,7 @@ const EditChannel = (props) => {
data
.
model_mapping
=
JSON
.
stringify
(
data
.
model_mapping
=
JSON
.
stringify
(
JSON
.
parse
(
data
.
model_mapping
),
JSON
.
parse
(
data
.
model_mapping
),
null
,
null
,
2
,
2
);
);
}
}
setInputs
(
data
);
setInputs
(
data
);
...
@@ -190,61 +193,60 @@ const EditChannel = (props) => {
...
@@ -190,61 +193,60 @@ const EditChannel = (props) => {
const
fetchUpstreamModelList
=
async
(
name
)
=>
{
const
fetchUpstreamModelList
=
async
(
name
)
=>
{
if
(
inputs
[
"type"
]
!==
1
)
{
if
(
inputs
[
'type'
]
!==
1
)
{
showError
(
"仅支持 OpenAI 接口格式"
)
showError
(
'仅支持 OpenAI 接口格式'
);
return
;
return
;
}
}
setLoading
(
true
)
setLoading
(
true
)
;
const
models
=
inputs
[
"models"
]
||
[]
const
models
=
inputs
[
'models'
]
||
[];
let
err
=
false
;
let
err
=
false
;
if
(
isEdit
)
{
if
(
isEdit
)
{
const
res
=
await
API
.
get
(
"/api/channel/fetch_models/"
+
channelId
)
const
res
=
await
API
.
get
(
'/api/channel/fetch_models/'
+
channelId
);
if
(
res
.
data
&&
res
.
data
?.
success
)
{
if
(
res
.
data
&&
res
.
data
?.
success
)
{
models
.
push
(...
res
.
data
.
data
)
models
.
push
(...
res
.
data
.
data
)
;
}
else
{
}
else
{
err
=
true
err
=
true
;
}
}
}
else
{
}
else
{
if
(
!
inputs
?.[
"key"
])
{
if
(
!
inputs
?.[
'key'
])
{
showError
(
"请填写密钥"
)
showError
(
'请填写密钥'
);
err
=
true
err
=
true
;
}
else
{
}
else
{
try
{
try
{
const
host
=
new
URL
((
inputs
[
"base_url"
]
||
"https://api.openai.com"
))
const
host
=
new
URL
((
inputs
[
'base_url'
]
||
'https://api.openai.com'
));
const
url
=
`https://
${
host
.
hostname
}
/v1/models`
;
const
url
=
`https://
${
host
.
hostname
}
/v1/models`
;
const
key
=
inputs
[
"key"
];
const
key
=
inputs
[
'key'
];
const
res
=
await
axios
.
get
(
url
,
{
const
res
=
await
axios
.
get
(
url
,
{
headers
:
{
headers
:
{
'Authorization'
:
`Bearer
${
key
}
`
'Authorization'
:
`Bearer
${
key
}
`
}
}
})
})
;
if
(
res
.
data
&&
res
.
data
?.
success
)
{
if
(
res
.
data
&&
res
.
data
?.
success
)
{
models
.
push
(...
res
.
data
.
data
.
map
((
model
)
=>
model
.
id
))
models
.
push
(...
res
.
data
.
data
.
map
((
model
)
=>
model
.
id
))
;
}
else
{
}
else
{
err
=
true
err
=
true
;
}
}
}
catch
(
error
)
{
}
catch
(
error
)
{
err
=
true
err
=
true
;
}
}
}
}
}
}
if
(
!
err
)
{
if
(
!
err
)
{
handleInputChange
(
name
,
Array
.
from
(
new
Set
(
models
)));
handleInputChange
(
name
,
Array
.
from
(
new
Set
(
models
)));
showSuccess
(
"获取模型列表成功"
);
showSuccess
(
'获取模型列表成功'
);
}
else
{
}
else
{
showError
(
'获取模型列表失败'
);
showError
(
'获取模型列表失败'
);
}
}
setLoading
(
false
);
setLoading
(
false
);
}
}
;
const
fetchModels
=
async
()
=>
{
const
fetchModels
=
async
()
=>
{
try
{
try
{
let
res
=
await
API
.
get
(
`/api/channel/models`
);
let
res
=
await
API
.
get
(
`/api/channel/models`
);
let
localModelOptions
=
res
.
data
.
data
.
map
((
model
)
=>
({
let
localModelOptions
=
res
.
data
.
data
.
map
((
model
)
=>
({
label
:
model
.
id
,
label
:
model
.
id
,
value
:
model
.
id
,
value
:
model
.
id
}));
}));
setOriginModelOptions
(
localModelOptions
);
setOriginModelOptions
(
localModelOptions
);
setFullModels
(
res
.
data
.
data
.
map
((
model
)
=>
model
.
id
));
setFullModels
(
res
.
data
.
data
.
map
((
model
)
=>
model
.
id
));
...
@@ -253,7 +255,7 @@ const EditChannel = (props) => {
...
@@ -253,7 +255,7 @@ const EditChannel = (props) => {
.
filter
((
model
)
=>
{
.
filter
((
model
)
=>
{
return
model
.
id
.
startsWith
(
'gpt-3'
)
||
model
.
id
.
startsWith
(
'text-'
);
return
model
.
id
.
startsWith
(
'gpt-3'
)
||
model
.
id
.
startsWith
(
'text-'
);
})
})
.
map
((
model
)
=>
model
.
id
)
,
.
map
((
model
)
=>
model
.
id
)
);
);
}
catch
(
error
)
{
}
catch
(
error
)
{
showError
(
error
.
message
);
showError
(
error
.
message
);
...
@@ -269,8 +271,8 @@ const EditChannel = (props) => {
...
@@ -269,8 +271,8 @@ const EditChannel = (props) => {
setGroupOptions
(
setGroupOptions
(
res
.
data
.
data
.
map
((
group
)
=>
({
res
.
data
.
data
.
map
((
group
)
=>
({
label
:
group
,
label
:
group
,
value
:
group
,
value
:
group
}))
,
}))
);
);
}
catch
(
error
)
{
}
catch
(
error
)
{
showError
(
error
.
message
);
showError
(
error
.
message
);
...
@@ -283,7 +285,7 @@ const EditChannel = (props) => {
...
@@ -283,7 +285,7 @@ const EditChannel = (props) => {
if
(
!
localModelOptions
.
find
((
option
)
=>
option
.
key
===
model
))
{
if
(
!
localModelOptions
.
find
((
option
)
=>
option
.
key
===
model
))
{
localModelOptions
.
push
({
localModelOptions
.
push
({
label
:
model
,
label
:
model
,
value
:
model
,
value
:
model
});
});
}
}
});
});
...
@@ -294,7 +296,8 @@ const EditChannel = (props) => {
...
@@ -294,7 +296,8 @@ const EditChannel = (props) => {
fetchModels
().
then
();
fetchModels
().
then
();
fetchGroups
().
then
();
fetchGroups
().
then
();
if
(
isEdit
)
{
if
(
isEdit
)
{
loadChannel
().
then
(()
=>
{});
loadChannel
().
then
(()
=>
{
});
}
else
{
}
else
{
setInputs
(
originInputs
);
setInputs
(
originInputs
);
let
localModels
=
getChannelModels
(
inputs
.
type
);
let
localModels
=
getChannelModels
(
inputs
.
type
);
...
@@ -320,7 +323,7 @@ const EditChannel = (props) => {
...
@@ -320,7 +323,7 @@ const EditChannel = (props) => {
if
(
localInputs
.
base_url
&&
localInputs
.
base_url
.
endsWith
(
'/'
))
{
if
(
localInputs
.
base_url
&&
localInputs
.
base_url
.
endsWith
(
'/'
))
{
localInputs
.
base_url
=
localInputs
.
base_url
.
slice
(
localInputs
.
base_url
=
localInputs
.
base_url
.
slice
(
0
,
0
,
localInputs
.
base_url
.
length
-
1
,
localInputs
.
base_url
.
length
-
1
);
);
}
}
if
(
localInputs
.
type
===
3
&&
localInputs
.
other
===
''
)
{
if
(
localInputs
.
type
===
3
&&
localInputs
.
other
===
''
)
{
...
@@ -341,7 +344,7 @@ const EditChannel = (props) => {
...
@@ -341,7 +344,7 @@ const EditChannel = (props) => {
if
(
isEdit
)
{
if
(
isEdit
)
{
res
=
await
API
.
put
(
`/api/channel/`
,
{
res
=
await
API
.
put
(
`/api/channel/`
,
{
...
localInputs
,
...
localInputs
,
id
:
parseInt
(
channelId
)
,
id
:
parseInt
(
channelId
)
});
});
}
else
{
}
else
{
res
=
await
API
.
post
(
`/api/channel/`
,
localInputs
);
res
=
await
API
.
post
(
`/api/channel/`
,
localInputs
);
...
@@ -378,7 +381,7 @@ const EditChannel = (props) => {
...
@@ -378,7 +381,7 @@ const EditChannel = (props) => {
// 添加到下拉选项
// 添加到下拉选项
key
:
model
,
key
:
model
,
text
:
model
,
text
:
model
,
value
:
model
,
value
:
model
});
});
}
else
if
(
model
)
{
}
else
if
(
model
)
{
showError
(
'某些模型已存在!'
);
showError
(
'某些模型已存在!'
);
...
@@ -409,11 +412,11 @@ const EditChannel = (props) => {
...
@@ -409,11 +412,11 @@ const EditChannel = (props) => {
footer
=
{
footer
=
{
<
div
style
=
{{
display
:
'flex'
,
justifyContent
:
'flex-end'
}}
>
<
div
style
=
{{
display
:
'flex'
,
justifyContent
:
'flex-end'
}}
>
<
Space
>
<
Space
>
<
Button
theme
=
'solid'
size
=
{
'large'
}
onClick
=
{
submit
}
>
<
Button
theme
=
"solid"
size
=
{
'large'
}
onClick
=
{
submit
}
>
提交
提交
<
/Button
>
<
/Button
>
<
Button
<
Button
theme
=
'solid'
theme
=
"solid"
size
=
{
'large'
}
size
=
{
'large'
}
type
=
{
'tertiary'
}
type
=
{
'tertiary'
}
onClick
=
{
handleCancel
}
onClick
=
{
handleCancel
}
...
@@ -432,7 +435,7 @@ const EditChannel = (props) => {
...
@@ -432,7 +435,7 @@ const EditChannel = (props) => {
<
Typography
.
Text
strong
>
类型:
<
/Typography.Text
>
<
Typography
.
Text
strong
>
类型:
<
/Typography.Text
>
<
/div
>
<
/div
>
<
Select
<
Select
name
=
'type'
name
=
"type"
required
required
optionList
=
{
CHANNEL_OPTIONS
}
optionList
=
{
CHANNEL_OPTIONS
}
value
=
{
inputs
.
type
}
value
=
{
inputs
.
type
}
...
@@ -450,8 +453,8 @@ const EditChannel = (props) => {
...
@@ -450,8 +453,8 @@ const EditChannel = (props) => {
,因为
One
API
会把请求体中的
model
,因为
One
API
会把请求体中的
model
参数替换为你的部署名称(模型名称中的点会被剔除),
参数替换为你的部署名称(模型名称中的点会被剔除),
<
a
<
a
target
=
'_blank'
target
=
"_blank"
href
=
'https://github.com/songquanpeng/one-api/issues/133?notification_referrer_id=NT_kwDOAmJSYrM2NjIwMzI3NDgyOjM5OTk4MDUw#issuecomment-1571602271'
href
=
"https://github.com/songquanpeng/one-api/issues/133?notification_referrer_id=NT_kwDOAmJSYrM2NjIwMzI3NDgyOjM5OTk4MDUw#issuecomment-1571602271"
>
>
图片演示
图片演示
<
/a
>
<
/a
>
...
@@ -466,8 +469,8 @@ const EditChannel = (props) => {
...
@@ -466,8 +469,8 @@ const EditChannel = (props) => {
<
/Typography.Text
>
<
/Typography.Text
>
<
/div
>
<
/div
>
<
Input
<
Input
label
=
'AZURE_OPENAI_ENDPOINT'
label
=
"AZURE_OPENAI_ENDPOINT"
name
=
'azure_base_url'
name
=
"azure_base_url"
placeholder
=
{
placeholder
=
{
'请输入 AZURE_OPENAI_ENDPOINT,例如:https://docs-test-001.openai.azure.com'
'请输入 AZURE_OPENAI_ENDPOINT,例如:https://docs-test-001.openai.azure.com'
}
}
...
@@ -475,14 +478,14 @@ const EditChannel = (props) => {
...
@@ -475,14 +478,14 @@ const EditChannel = (props) => {
handleInputChange
(
'base_url'
,
value
);
handleInputChange
(
'base_url'
,
value
);
}}
}}
value
=
{
inputs
.
base_url
}
value
=
{
inputs
.
base_url
}
autoComplete
=
'new-password'
autoComplete
=
"new-password"
/>
/>
<
div
style
=
{{
marginTop
:
10
}}
>
<
div
style
=
{{
marginTop
:
10
}}
>
<
Typography
.
Text
strong
>
默认
API
版本:
<
/Typography.Text
>
<
Typography
.
Text
strong
>
默认
API
版本:
<
/Typography.Text
>
<
/div
>
<
/div
>
<
Input
<
Input
label
=
'默认 API 版本'
label
=
"默认 API 版本"
name
=
'azure_other'
name
=
"azure_other"
placeholder
=
{
placeholder
=
{
'请输入默认 API 版本,例如:2023-06-01-preview,该配置可以被实际的请求查询参数所覆盖'
'请输入默认 API 版本,例如:2023-06-01-preview,该配置可以被实际的请求查询参数所覆盖'
}
}
...
@@ -490,7 +493,7 @@ const EditChannel = (props) => {
...
@@ -490,7 +493,7 @@ const EditChannel = (props) => {
handleInputChange
(
'other'
,
value
);
handleInputChange
(
'other'
,
value
);
}}
}}
value
=
{
inputs
.
other
}
value
=
{
inputs
.
other
}
autoComplete
=
'new-password'
autoComplete
=
"new-password"
/>
/>
<
/
>
<
/
>
)}
)}
...
@@ -512,7 +515,7 @@ const EditChannel = (props) => {
...
@@ -512,7 +515,7 @@ const EditChannel = (props) => {
<
/Typography.Text
>
<
/Typography.Text
>
<
/div
>
<
/div
>
<
Input
<
Input
name
=
'base_url'
name
=
"base_url"
placeholder
=
{
placeholder
=
{
'请输入完整的URL,例如:https://api.openai.com/v1/chat/completions'
'请输入完整的URL,例如:https://api.openai.com/v1/chat/completions'
}
}
...
@@ -520,19 +523,54 @@ const EditChannel = (props) => {
...
@@ -520,19 +523,54 @@ const EditChannel = (props) => {
handleInputChange
(
'base_url'
,
value
);
handleInputChange
(
'base_url'
,
value
);
}}
}}
value
=
{
inputs
.
base_url
}
value
=
{
inputs
.
base_url
}
autoComplete
=
'new-password'
autoComplete
=
"new-password"
/>
<
/
>
)}
{
inputs
.
type
!==
3
&&
inputs
.
type
!==
8
&&
inputs
.
type
!==
22
&&
inputs
.
type
!==
36
&&
(
<>
<
div
style
=
{{
marginTop
:
10
}}
>
<
Typography
.
Text
strong
>
代理:
<
/Typography.Text
>
<
/div
>
<
Input
label
=
"代理"
name
=
"base_url"
placeholder
=
{
'此项可选,用于通过代理站来进行 API 调用'
}
onChange
=
{(
value
)
=>
{
handleInputChange
(
'base_url'
,
value
);
}}
value
=
{
inputs
.
base_url
}
autoComplete
=
"new-password"
/>
<
/
>
)}
{
inputs
.
type
===
22
&&
(
<>
<
div
style
=
{{
marginTop
:
10
}}
>
<
Typography
.
Text
strong
>
私有部署地址:
<
/Typography.Text
>
<
/div
>
<
Input
name
=
"base_url"
placeholder
=
{
'请输入私有部署地址,格式为:https://fastgpt.run/api/openapi'
}
onChange
=
{(
value
)
=>
{
handleInputChange
(
'base_url'
,
value
);
}}
value
=
{
inputs
.
base_url
}
autoComplete
=
"new-password"
/>
/>
<
/
>
<
/
>
)}
)}
{
inputs
.
type
===
36
&&
(
{
inputs
.
type
===
36
&&
(
<>
<>
<
div
style
=
{{
marginTop
:
10
}}
>
<
div
style
=
{{
marginTop
:
10
}}
>
<
Typography
.
Text
strong
>
<
Typography
.
Text
strong
>
注意非
Chat
API
,请务必填写正确的
API
地址,否则可能导致无法使用
注意非
Chat
API
,请务必填写正确的
API
地址,否则可能导致无法使用
<
/Typography.Text
>
<
/Typography.Text
>
<
/div
>
<
/div
>
<
Input
<
Input
name
=
'base_url'
name
=
"base_url"
placeholder
=
{
placeholder
=
{
'请输入到 /suno 前的路径,通常就是域名,例如:https://api.example.com '
'请输入到 /suno 前的路径,通常就是域名,例如:https://api.example.com '
}
}
...
@@ -540,29 +578,29 @@ const EditChannel = (props) => {
...
@@ -540,29 +578,29 @@ const EditChannel = (props) => {
handleInputChange
(
'base_url'
,
value
);
handleInputChange
(
'base_url'
,
value
);
}}
}}
value
=
{
inputs
.
base_url
}
value
=
{
inputs
.
base_url
}
autoComplete
=
'new-password'
autoComplete
=
"new-password"
/>
/>
<
/
>
<
/
>
)}
)}
<
div
style
=
{{
marginTop
:
10
}}
>
<
div
style
=
{{
marginTop
:
10
}}
>
<
Typography
.
Text
strong
>
名称:
<
/Typography.Text
>
<
Typography
.
Text
strong
>
名称:
<
/Typography.Text
>
<
/div
>
<
/div
>
<
Input
<
Input
required
required
name
=
'name'
name
=
"name"
placeholder
=
{
'请为渠道命名'
}
placeholder
=
{
'请为渠道命名'
}
onChange
=
{(
value
)
=>
{
onChange
=
{(
value
)
=>
{
handleInputChange
(
'name'
,
value
);
handleInputChange
(
'name'
,
value
);
}}
}}
value
=
{
inputs
.
name
}
value
=
{
inputs
.
name
}
autoComplete
=
'new-password'
autoComplete
=
"new-password"
/>
/>
<
div
style
=
{{
marginTop
:
10
}}
>
<
div
style
=
{{
marginTop
:
10
}}
>
<
Typography
.
Text
strong
>
分组:
<
/Typography.Text
>
<
Typography
.
Text
strong
>
分组:
<
/Typography.Text
>
<
/div
>
<
/div
>
<
Select
<
Select
placeholder
=
{
'请选择可以使用该渠道的分组'
}
placeholder
=
{
'请选择可以使用该渠道的分组'
}
name
=
'groups'
name
=
"groups"
required
required
multiple
multiple
selection
selection
...
@@ -572,7 +610,7 @@ const EditChannel = (props) => {
...
@@ -572,7 +610,7 @@ const EditChannel = (props) => {
handleInputChange
(
'groups'
,
value
);
handleInputChange
(
'groups'
,
value
);
}}
}}
value
=
{
inputs
.
groups
}
value
=
{
inputs
.
groups
}
autoComplete
=
'new-password'
autoComplete
=
"new-password"
optionList
=
{
groupOptions
}
optionList
=
{
groupOptions
}
/
>
/
>
{
inputs
.
type
===
18
&&
(
{
inputs
.
type
===
18
&&
(
...
@@ -581,7 +619,7 @@ const EditChannel = (props) => {
...
@@ -581,7 +619,7 @@ const EditChannel = (props) => {
<
Typography
.
Text
strong
>
模型版本:
<
/Typography.Text
>
<
Typography
.
Text
strong
>
模型版本:
<
/Typography.Text
>
<
/div
>
<
/div
>
<
Input
<
Input
name
=
'other'
name
=
"other"
placeholder
=
{
placeholder
=
{
'请输入星火大模型版本,注意是接口地址中的版本号,例如:v2.1'
'请输入星火大模型版本,注意是接口地址中的版本号,例如:v2.1'
}
}
...
@@ -589,7 +627,7 @@ const EditChannel = (props) => {
...
@@ -589,7 +627,7 @@ const EditChannel = (props) => {
handleInputChange
(
'other'
,
value
);
handleInputChange
(
'other'
,
value
);
}}
}}
value
=
{
inputs
.
other
}
value
=
{
inputs
.
other
}
autoComplete
=
'new-password'
autoComplete
=
"new-password"
/>
/>
<
/
>
<
/
>
)}
)}
...
@@ -599,7 +637,7 @@ const EditChannel = (props) => {
...
@@ -599,7 +637,7 @@ const EditChannel = (props) => {
<
Typography
.
Text
strong
>
部署地区:
<
/Typography.Text
>
<
Typography
.
Text
strong
>
部署地区:
<
/Typography.Text
>
<
/div
>
<
/div
>
<
TextArea
<
TextArea
name
=
'other'
name
=
"other"
placeholder
=
{
placeholder
=
{
'请输入部署地区,例如:us-central1\n支持使用模型映射格式\n'
+
'请输入部署地区,例如:us-central1\n支持使用模型映射格式\n'
+
'{\n'
+
'{\n'
+
...
@@ -612,18 +650,18 @@ const EditChannel = (props) => {
...
@@ -612,18 +650,18 @@ const EditChannel = (props) => {
handleInputChange
(
'other'
,
value
);
handleInputChange
(
'other'
,
value
);
}}
}}
value
=
{
inputs
.
other
}
value
=
{
inputs
.
other
}
autoComplete
=
'new-password'
autoComplete
=
"new-password"
/>
/>
<
Typography
.
Text
<
Typography
.
Text
style
=
{{
style
=
{{
color
:
'rgba(var(--semi-blue-5), 1)'
,
color
:
'rgba(var(--semi-blue-5), 1)'
,
userSelect
:
'none'
,
userSelect
:
'none'
,
cursor
:
'pointer'
,
cursor
:
'pointer'
}}
}}
onClick
=
{()
=>
{
onClick
=
{()
=>
{
handleInputChange
(
handleInputChange
(
'other'
,
'other'
,
JSON
.
stringify
(
REGION_EXAMPLE
,
null
,
2
)
,
JSON
.
stringify
(
REGION_EXAMPLE
,
null
,
2
)
);
);
}}
}}
>
>
...
@@ -637,14 +675,14 @@ const EditChannel = (props) => {
...
@@ -637,14 +675,14 @@ const EditChannel = (props) => {
<
Typography
.
Text
strong
>
知识库
ID
:
<
/Typography.Text
>
<
Typography
.
Text
strong
>
知识库
ID
:
<
/Typography.Text
>
<
/div
>
<
/div
>
<
Input
<
Input
label
=
'知识库 ID'
label
=
"知识库 ID"
name
=
'other'
name
=
"other"
placeholder
=
{
'请输入知识库 ID,例如:123456'
}
placeholder
=
{
'请输入知识库 ID,例如:123456'
}
onChange
=
{(
value
)
=>
{
onChange
=
{(
value
)
=>
{
handleInputChange
(
'other'
,
value
);
handleInputChange
(
'other'
,
value
);
}}
}}
value
=
{
inputs
.
other
}
value
=
{
inputs
.
other
}
autoComplete
=
'new-password'
autoComplete
=
"new-password"
/>
/>
<
/
>
<
/
>
)}
)}
...
@@ -654,7 +692,7 @@ const EditChannel = (props) => {
...
@@ -654,7 +692,7 @@ const EditChannel = (props) => {
<
Typography
.
Text
strong
>
Account
ID
:
<
/Typography.Text
>
<
Typography
.
Text
strong
>
Account
ID
:
<
/Typography.Text
>
<
/div
>
<
/div
>
<
Input
<
Input
name
=
'other'
name
=
"other"
placeholder
=
{
placeholder
=
{
'请输入Account ID,例如:d6b5da8hk1awo8nap34ube6gh'
'请输入Account ID,例如:d6b5da8hk1awo8nap34ube6gh'
}
}
...
@@ -662,7 +700,7 @@ const EditChannel = (props) => {
...
@@ -662,7 +700,7 @@ const EditChannel = (props) => {
handleInputChange
(
'other'
,
value
);
handleInputChange
(
'other'
,
value
);
}}
}}
value
=
{
inputs
.
other
}
value
=
{
inputs
.
other
}
autoComplete
=
'new-password'
autoComplete
=
"new-password"
/>
/>
<
/
>
<
/
>
)}
)}
...
@@ -671,7 +709,7 @@ const EditChannel = (props) => {
...
@@ -671,7 +709,7 @@ const EditChannel = (props) => {
<
/div
>
<
/div
>
<
Select
<
Select
placeholder
=
{
'请选择该渠道所支持的模型'
}
placeholder
=
{
'请选择该渠道所支持的模型'
}
name
=
'models'
name
=
"models"
required
required
multiple
multiple
selection
selection
...
@@ -679,13 +717,13 @@ const EditChannel = (props) => {
...
@@ -679,13 +717,13 @@ const EditChannel = (props) => {
handleInputChange
(
'models'
,
value
);
handleInputChange
(
'models'
,
value
);
}}
}}
value
=
{
inputs
.
models
}
value
=
{
inputs
.
models
}
autoComplete
=
'new-password'
autoComplete
=
"new-password"
optionList
=
{
modelOptions
}
optionList
=
{
modelOptions
}
/
>
/
>
<
div
style
=
{{
lineHeight
:
'40px'
,
marginBottom
:
'12px'
}}
>
<
div
style
=
{{
lineHeight
:
'40px'
,
marginBottom
:
'12px'
}}
>
<
Space
>
<
Space
>
<
Button
<
Button
type
=
'primary'
type
=
"primary"
onClick
=
{()
=>
{
onClick
=
{()
=>
{
handleInputChange
(
'models'
,
basicModels
);
handleInputChange
(
'models'
,
basicModels
);
}}
}}
...
@@ -693,7 +731,7 @@ const EditChannel = (props) => {
...
@@ -693,7 +731,7 @@ const EditChannel = (props) => {
填入相关模型
填入相关模型
<
/Button
>
<
/Button
>
<
Button
<
Button
type
=
'secondary'
type
=
"secondary"
onClick
=
{()
=>
{
onClick
=
{()
=>
{
handleInputChange
(
'models'
,
fullModels
);
handleInputChange
(
'models'
,
fullModels
);
}}
}}
...
@@ -702,7 +740,7 @@ const EditChannel = (props) => {
...
@@ -702,7 +740,7 @@ const EditChannel = (props) => {
<
/Button
>
<
/Button
>
<
Tooltip
content
=
{
fetchButtonTips
}
>
<
Tooltip
content
=
{
fetchButtonTips
}
>
<
Button
<
Button
type
=
'tertiary'
type
=
"tertiary"
onClick
=
{()
=>
{
onClick
=
{()
=>
{
fetchUpstreamModelList
(
'models'
);
fetchUpstreamModelList
(
'models'
);
}}
}}
...
@@ -711,7 +749,7 @@ const EditChannel = (props) => {
...
@@ -711,7 +749,7 @@ const EditChannel = (props) => {
<
/Button
>
<
/Button
>
<
/Tooltip
>
<
/Tooltip
>
<
Button
<
Button
type
=
'warning'
type
=
"warning"
onClick
=
{()
=>
{
onClick
=
{()
=>
{
handleInputChange
(
'models'
,
[]);
handleInputChange
(
'models'
,
[]);
}}
}}
...
@@ -721,11 +759,11 @@ const EditChannel = (props) => {
...
@@ -721,11 +759,11 @@ const EditChannel = (props) => {
<
/Space
>
<
/Space
>
<
Input
<
Input
addonAfter
=
{
addonAfter
=
{
<
Button
type
=
'primary'
onClick
=
{
addCustomModels
}
>
<
Button
type
=
"primary"
onClick
=
{
addCustomModels
}
>
填入
填入
<
/Button
>
<
/Button
>
}
}
placeholder
=
'输入自定义模型名称'
placeholder
=
"输入自定义模型名称"
value
=
{
customModel
}
value
=
{
customModel
}
onChange
=
{(
value
)
=>
{
onChange
=
{(
value
)
=>
{
setCustomModel
(
value
.
trim
());
setCustomModel
(
value
.
trim
());
...
@@ -737,24 +775,24 @@ const EditChannel = (props) => {
...
@@ -737,24 +775,24 @@ const EditChannel = (props) => {
<
/div
>
<
/div
>
<
TextArea
<
TextArea
placeholder
=
{
`此项可选,用于修改请求体中的模型名称,为一个 JSON 字符串,键为请求中模型名称,值为要替换的模型名称,例如:\n
${
JSON
.
stringify
(
MODEL_MAPPING_EXAMPLE
,
null
,
2
)}
`
}
placeholder
=
{
`此项可选,用于修改请求体中的模型名称,为一个 JSON 字符串,键为请求中模型名称,值为要替换的模型名称,例如:\n
${
JSON
.
stringify
(
MODEL_MAPPING_EXAMPLE
,
null
,
2
)}
`
}
name
=
'model_mapping'
name
=
"model_mapping"
onChange
=
{(
value
)
=>
{
onChange
=
{(
value
)
=>
{
handleInputChange
(
'model_mapping'
,
value
);
handleInputChange
(
'model_mapping'
,
value
);
}}
}}
autosize
autosize
value
=
{
inputs
.
model_mapping
}
value
=
{
inputs
.
model_mapping
}
autoComplete
=
'new-password'
autoComplete
=
"new-password"
/>
/>
<
Typography
.
Text
<
Typography
.
Text
style
=
{{
style
=
{{
color
:
'rgba(var(--semi-blue-5), 1)'
,
color
:
'rgba(var(--semi-blue-5), 1)'
,
userSelect
:
'none'
,
userSelect
:
'none'
,
cursor
:
'pointer'
,
cursor
:
'pointer'
}}
}}
onClick
=
{()
=>
{
onClick
=
{()
=>
{
handleInputChange
(
handleInputChange
(
'model_mapping'
,
'model_mapping'
,
JSON
.
stringify
(
MODEL_MAPPING_EXAMPLE
,
null
,
2
)
,
JSON
.
stringify
(
MODEL_MAPPING_EXAMPLE
,
null
,
2
)
);
);
}}
}}
>
>
...
@@ -765,8 +803,8 @@ const EditChannel = (props) => {
...
@@ -765,8 +803,8 @@ const EditChannel = (props) => {
<
/div
>
<
/div
>
{
batch
?
(
{
batch
?
(
<
TextArea
<
TextArea
label
=
'密钥'
label
=
"密钥"
name
=
'key'
name
=
"key"
required
required
placeholder
=
{
'请输入密钥,一行一个'
}
placeholder
=
{
'请输入密钥,一行一个'
}
onChange
=
{(
value
)
=>
{
onChange
=
{(
value
)
=>
{
...
@@ -774,14 +812,14 @@ const EditChannel = (props) => {
...
@@ -774,14 +812,14 @@ const EditChannel = (props) => {
}}
}}
value
=
{
inputs
.
key
}
value
=
{
inputs
.
key
}
style
=
{{
minHeight
:
150
,
fontFamily
:
'JetBrains Mono, Consolas'
}}
style
=
{{
minHeight
:
150
,
fontFamily
:
'JetBrains Mono, Consolas'
}}
autoComplete
=
'new-password'
autoComplete
=
"new-password"
/>
/>
)
:
(
)
:
(
<>
<>
{
inputs
.
type
===
41
?
(
{
inputs
.
type
===
41
?
(
<
TextArea
<
TextArea
label
=
'鉴权json'
label
=
"鉴权json"
name
=
'key'
name
=
"key"
required
required
placeholder
=
{
'{\n'
+
placeholder
=
{
'{\n'
+
' "type": "service_account",\n'
+
' "type": "service_account",\n'
+
...
@@ -801,33 +839,46 @@ const EditChannel = (props) => {
...
@@ -801,33 +839,46 @@ const EditChannel = (props) => {
}}
}}
autosize
=
{{
minRows
:
10
}}
autosize
=
{{
minRows
:
10
}}
value
=
{
inputs
.
key
}
value
=
{
inputs
.
key
}
autoComplete
=
'new-password'
autoComplete
=
"new-password"
/>
/>
)
:
(
)
:
(
<
Input
<
Input
label
=
'密钥'
label
=
"密钥"
name
=
'key'
name
=
"key"
required
required
placeholder
=
{
type2secretPrompt
(
inputs
.
type
)}
placeholder
=
{
type2secretPrompt
(
inputs
.
type
)}
onChange
=
{(
value
)
=>
{
onChange
=
{(
value
)
=>
{
handleInputChange
(
'key'
,
value
);
handleInputChange
(
'key'
,
value
);
}}
}}
value
=
{
inputs
.
key
}
value
=
{
inputs
.
key
}
autoComplete
=
'new-password'
autoComplete
=
"new-password"
/>
/>
)
)
}
}
<
/
>
<
/
>
)}
)}
{
!
isEdit
&&
(
<
div
style
=
{{
marginTop
:
10
,
display
:
'flex'
}}
>
<
Space
>
<
Checkbox
checked
=
{
batch
}
label
=
"批量创建"
name
=
"batch"
onChange
=
{()
=>
setBatch
(
!
batch
)}
/
>
<
Typography
.
Text
strong
>
批量创建
<
/Typography.Text
>
<
/Space
>
<
/div
>
)}
{
inputs
.
type
===
1
&&
(
{
inputs
.
type
===
1
&&
(
<>
<>
<
div
style
=
{{
marginTop
:
10
}}
>
<
div
style
=
{{
marginTop
:
10
}}
>
<
Typography
.
Text
strong
>
组织:
<
/Typography.Text
>
<
Typography
.
Text
strong
>
组织:
<
/Typography.Text
>
<
/div
>
<
/div
>
<
Input
<
Input
label
=
'组织,可选,不填则为默认组织'
label
=
"组织,可选,不填则为默认组织"
name
=
'openai_organization'
name
=
"openai_organization"
placeholder
=
'请输入组织org-xxx'
placeholder
=
"请输入组织org-xxx"
onChange
=
{(
value
)
=>
{
onChange
=
{(
value
)
=>
{
handleInputChange
(
'openai_organization'
,
value
);
handleInputChange
(
'openai_organization'
,
value
);
}}
}}
...
@@ -839,8 +890,8 @@ const EditChannel = (props) => {
...
@@ -839,8 +890,8 @@ const EditChannel = (props) => {
<
Typography
.
Text
strong
>
默认测试模型:
<
/Typography.Text
>
<
Typography
.
Text
strong
>
默认测试模型:
<
/Typography.Text
>
<
/div
>
<
/div
>
<
Input
<
Input
name
=
'test_model'
name
=
"test_model"
placeholder
=
'不填则为模型列表第一个'
placeholder
=
"不填则为模型列表第一个"
onChange
=
{(
value
)
=>
{
onChange
=
{(
value
)
=>
{
handleInputChange
(
'test_model'
,
value
);
handleInputChange
(
'test_model'
,
value
);
}}
}}
...
@@ -849,7 +900,7 @@ const EditChannel = (props) => {
...
@@ -849,7 +900,7 @@ const EditChannel = (props) => {
<
div
style
=
{{
marginTop
:
10
,
display
:
'flex'
}}
>
<
div
style
=
{{
marginTop
:
10
,
display
:
'flex'
}}
>
<
Space
>
<
Space
>
<
Checkbox
<
Checkbox
name
=
'auto_ban'
name
=
"auto_ban"
checked
=
{
autoBan
}
checked
=
{
autoBan
}
onChange
=
{()
=>
{
onChange
=
{()
=>
{
setAutoBan
(
!
autoBan
);
setAutoBan
(
!
autoBan
);
...
@@ -861,55 +912,6 @@ const EditChannel = (props) => {
...
@@ -861,55 +912,6 @@ const EditChannel = (props) => {
<
/Typography.Text
>
<
/Typography.Text
>
<
/Space
>
<
/Space
>
<
/div
>
<
/div
>
{
!
isEdit
&&
(
<
div
style
=
{{
marginTop
:
10
,
display
:
'flex'
}}
>
<
Space
>
<
Checkbox
checked
=
{
batch
}
label
=
'批量创建'
name
=
'batch'
onChange
=
{()
=>
setBatch
(
!
batch
)}
/
>
<
Typography
.
Text
strong
>
批量创建
<
/Typography.Text
>
<
/Space
>
<
/div
>
)}
{
inputs
.
type
!==
3
&&
inputs
.
type
!==
8
&&
inputs
.
type
!==
22
&&
inputs
.
type
!==
36
&&
(
<>
<
div
style
=
{{
marginTop
:
10
}}
>
<
Typography
.
Text
strong
>
代理:
<
/Typography.Text
>
<
/div
>
<
Input
label
=
'代理'
name
=
'base_url'
placeholder
=
{
'此项可选,用于通过代理站来进行 API 调用'
}
onChange
=
{(
value
)
=>
{
handleInputChange
(
'base_url'
,
value
);
}}
value
=
{
inputs
.
base_url
}
autoComplete
=
'new-password'
/>
<
/
>
)}
{
inputs
.
type
===
22
&&
(
<>
<
div
style
=
{{
marginTop
:
10
}}
>
<
Typography
.
Text
strong
>
私有部署地址:
<
/Typography.Text
>
<
/div
>
<
Input
name
=
'base_url'
placeholder
=
{
'请输入私有部署地址,格式为:https://fastgpt.run/api/openapi'
}
onChange
=
{(
value
)
=>
{
handleInputChange
(
'base_url'
,
value
);
}}
value
=
{
inputs
.
base_url
}
autoComplete
=
'new-password'
/>
<
/
>
)}
<
div
style
=
{{
marginTop
:
10
}}
>
<
div
style
=
{{
marginTop
:
10
}}
>
<
Typography
.
Text
strong
>
<
Typography
.
Text
strong
>
状态码复写(仅影响本地判断,不修改返回到上游的状态码):
状态码复写(仅影响本地判断,不修改返回到上游的状态码):
...
@@ -917,43 +919,74 @@ const EditChannel = (props) => {
...
@@ -917,43 +919,74 @@ const EditChannel = (props) => {
<
/div
>
<
/div
>
<
TextArea
<
TextArea
placeholder
=
{
`此项可选,用于复写返回的状态码,比如将claude渠道的400错误复写为500(用于重试),请勿滥用该功能,例如:\n
${
JSON
.
stringify
(
STATUS_CODE_MAPPING_EXAMPLE
,
null
,
2
)}
`
}
placeholder
=
{
`此项可选,用于复写返回的状态码,比如将claude渠道的400错误复写为500(用于重试),请勿滥用该功能,例如:\n
${
JSON
.
stringify
(
STATUS_CODE_MAPPING_EXAMPLE
,
null
,
2
)}
`
}
name
=
'status_code_mapping'
name
=
"status_code_mapping"
onChange
=
{(
value
)
=>
{
onChange
=
{(
value
)
=>
{
handleInputChange
(
'status_code_mapping'
,
value
);
handleInputChange
(
'status_code_mapping'
,
value
);
}}
}}
autosize
autosize
value
=
{
inputs
.
status_code_mapping
}
value
=
{
inputs
.
status_code_mapping
}
autoComplete
=
'new-password'
autoComplete
=
"new-password"
/>
/>
<
Typography
.
Text
<
Typography
.
Text
style
=
{{
style
=
{{
color
:
'rgba(var(--semi-blue-5), 1)'
,
color
:
'rgba(var(--semi-blue-5), 1)'
,
userSelect
:
'none'
,
userSelect
:
'none'
,
cursor
:
'pointer'
,
cursor
:
'pointer'
}}
}}
onClick
=
{()
=>
{
onClick
=
{()
=>
{
handleInputChange
(
handleInputChange
(
'status_code_mapping'
,
'status_code_mapping'
,
JSON
.
stringify
(
STATUS_CODE_MAPPING_EXAMPLE
,
null
,
2
)
,
JSON
.
stringify
(
STATUS_CODE_MAPPING_EXAMPLE
,
null
,
2
)
);
);
}}
}}
>
>
填入模板
填入模板
<
/Typography.Text
>
<
/Typography.Text
>
{
/*<div style={{ marginTop: 10 }}>*/
}
<
div
style
=
{{
marginTop
:
10
}}
>
{
/* <Typography.Text strong>*/
}
<
Typography
.
Text
strong
>
{
/* 最大请求token(0表示不限制):*/
}
渠道标签
{
/* </Typography.Text>*/
}
<
/Typography.Text
>
{
/*</div>*/
}
<
/div
>
{
/*<Input*/
}
<
Input
{
/* label='最大请求token'*/
}
label
=
"渠道标签"
{
/* name='max_input_tokens'*/
}
name
=
"tag"
{
/* placeholder='默认为0,表示不限制'*/
}
placeholder
=
{
'渠道标签'
}
{
/* onChange={(value) => {*/
}
onChange
=
{(
value
)
=>
{
{
/* handleInputChange('max_input_tokens', value);*/
}
handleInputChange
(
'tag'
,
value
);
{
/* }}*/
}
}}
{
/* value={inputs.max_input_tokens}*/
}
value
=
{
inputs
.
tag
}
{
/*/>*/
}
autoComplete
=
"new-password"
/>
<
div
style
=
{{
marginTop
:
10
}}
>
<
Typography
.
Text
strong
>
渠道优先级
<
/Typography.Text
>
<
/div
>
<
Input
label
=
"渠道优先级"
name
=
"priority"
placeholder
=
{
'渠道优先级'
}
onChange
=
{(
value
)
=>
{
handleInputChange
(
'priority'
,
parseInt
(
value
));
}}
value
=
{
inputs
.
priority
}
autoComplete
=
"new-password"
/>
<
div
style
=
{{
marginTop
:
10
}}
>
<
Typography
.
Text
strong
>
渠道权重
<
/Typography.Text
>
<
/div
>
<
Input
label
=
"渠道权重"
name
=
"weight"
placeholder
=
{
'渠道权重'
}
onChange
=
{(
value
)
=>
{
handleInputChange
(
'weight'
,
parseInt
(
value
));
}}
value
=
{
inputs
.
weight
}
autoComplete
=
"new-password"
/>
<
/Spin
>
<
/Spin
>
<
/SideSheet
>
<
/SideSheet
>
<
/
>
<
/
>
...
...
web/src/pages/Channel/EditTagModal.js
0 → 100644
View file @
325de987
import
React
,
{
useState
,
useEffect
}
from
'react'
;
import
{
API
,
showError
,
showSuccess
}
from
'../../helpers'
;
import
{
SideSheet
,
Space
,
Button
,
Input
,
Typography
,
Spin
,
Modal
}
from
'@douyinfe/semi-ui'
;
import
TextInput
from
'../../components/TextInput.js'
;
const
EditTagModal
=
(
props
)
=>
{
const
{
visible
,
tag
,
handleClose
,
refresh
}
=
props
;
const
[
loading
,
setLoading
]
=
useState
(
false
);
const
originInputs
=
{
tag
:
''
,
newTag
:
null
,
}
const
[
inputs
,
setInputs
]
=
useState
(
originInputs
);
const
handleSave
=
async
()
=>
{
setLoading
(
true
);
let
data
=
{
tag
:
tag
,
}
let
shouldSave
=
true
;
if
(
inputs
.
newTag
===
tag
)
{
setLoading
(
false
);
return
;
}
data
.
newTag
=
inputs
.
newTag
;
if
(
data
.
newTag
===
''
)
{
Modal
.
confirm
({
title
:
'解散标签'
,
content
:
'确定要解散标签吗?'
,
onCancel
:
()
=>
{
setLoading
(
false
);
},
onOk
:
async
()
=>
{
await
submit
(
data
);
}
});
}
else
{
await
submit
(
data
);
}
setLoading
(
false
);
};
const
submit
=
async
(
data
)
=>
{
try
{
const
res
=
await
API
.
put
(
'/api/channel/tag'
,
data
);
if
(
res
?.
data
?.
success
)
{
showSuccess
(
'标签更新成功!'
);
refresh
();
handleClose
();
}
}
catch
(
error
)
{
showError
(
error
);
}
}
useEffect
(()
=>
{
setInputs
({
...
originInputs
,
tag
:
tag
,
newTag
:
tag
,
})
},
[
visible
]);
return
(
<
SideSheet
title
=
"编辑标签"
visible
=
{
visible
}
onCancel
=
{
handleClose
}
footer
=
{
<
div
style
=
{{
display
:
'flex'
,
justifyContent
:
'flex-end'
}}
>
<
Space
>
<
Button
onClick
=
{
handleClose
}
>
取消
<
/Button
>
<
Button
type
=
"primary"
onClick
=
{
handleSave
}
loading
=
{
loading
}
>
保存
<
/Button
>
<
/Space
>
<
/div
>
}
>
<
Spin
spinning
=
{
loading
}
>
<
TextInput
label
=
"新标签(留空则解散标签,不会删除标签下的渠道)"
name
=
"newTag"
value
=
{
inputs
.
newTag
}
onChange
=
{(
value
)
=>
setInputs
({
...
inputs
,
newTag
:
value
})}
placeholder
=
"请输入新标签"
/>
<
/Spin
>
<
/SideSheet
>
);
};
export
default
EditTagModal
;
\ 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