mirror of
https://github.com/APIParkLab/APIPark.git
synced 2026-06-14 20:41:15 +08:00
Merge branch 'feature/1.7-cxx' into 'main'
feature/1.7-MCP See merge request apipark/APIPark!318
This commit is contained in:
@@ -234,12 +234,16 @@ export default function ApiEdit({
|
||||
<>
|
||||
<Space.Compact className="w-full mb-btnybase">
|
||||
<Select
|
||||
showSearch
|
||||
optionFilterProp="label"
|
||||
className="w-[15%] min-w-[100px]"
|
||||
value={apiInfo?.protocol || 'HTTP'}
|
||||
disabled={true}
|
||||
options={protocolOptionList}
|
||||
/>
|
||||
<Select
|
||||
showSearch
|
||||
optionFilterProp="label"
|
||||
className="w-[15%] min-w-[100px]"
|
||||
value={apiInfo?.method}
|
||||
disabled={true}
|
||||
|
||||
@@ -359,7 +359,7 @@ export const SERVICE_APPROVAL_OPTIONS = [
|
||||
{ label: '人工审核:仅允许通过人工审核的消费者调用该服务', value: 'manual' }
|
||||
]
|
||||
export const MCP_OPTIONS = [
|
||||
{ label: '关闭', value: false },
|
||||
{ label: '禁用', value: false },
|
||||
{ label: '开启:AI Agent 等产品能够通过 MCP 方式调用服务', value: true }
|
||||
]
|
||||
export const SERVICE_KIND_OPTIONS = [
|
||||
|
||||
@@ -1109,6 +1109,11 @@ p{
|
||||
width: 16px !important;
|
||||
justify-content:center;
|
||||
}
|
||||
.ant-select .ant-select-clear {
|
||||
height:16px !important;
|
||||
width: 16px !important;
|
||||
top: 45%;
|
||||
}
|
||||
|
||||
.ant-table-wrapper .ant-table{
|
||||
scrollbar-color: none !important;
|
||||
|
||||
@@ -163,6 +163,8 @@ const AiServiceRouterModelConfig = forwardRef<AiServiceRouterModelConfigHandle,
|
||||
>
|
||||
<Form.Item<AiServiceRouterModelConfigField> label={$t('模型类型')} name="type" rules={[{ required: true }]}>
|
||||
<Select
|
||||
showSearch
|
||||
optionFilterProp="label"
|
||||
className="w-INPUT_NORMAL"
|
||||
placeholder={$t(PLACEHOLDER.select)}
|
||||
options={modelTypeList}
|
||||
|
||||
@@ -390,6 +390,8 @@ const AiSettingModalContent = forwardRef<AiSettingModalContentHandle, AiSettingM
|
||||
{source === 'guide' && (
|
||||
<Form.Item label={$t('所属团队')} name="team" className="mt-[16px]" rules={[{ required: true }]}>
|
||||
<Select
|
||||
showSearch
|
||||
optionFilterProp="label"
|
||||
className="w-INPUT_NORMAL"
|
||||
placeholder={$t(PLACEHOLDER.input)}
|
||||
options={teamList}
|
||||
|
||||
@@ -181,6 +181,8 @@ const LocalAiDeploy = forwardRef<LocalAiDeployHandle, any>((props: any, ref: any
|
||||
</Form.Item>
|
||||
<Form.Item label={$t('所属团队')} name="team" className="mt-[16px]" rules={[{ required: true }]}>
|
||||
<Select
|
||||
showSearch
|
||||
optionFilterProp="label"
|
||||
className="w-INPUT_NORMAL"
|
||||
placeholder={$t(PLACEHOLDER.input)}
|
||||
options={teamList}
|
||||
|
||||
@@ -106,6 +106,8 @@ const RestAIDeploy = forwardRef<RestAIDeployHandle, any>((props: any, ref: any)
|
||||
</Form.Item>
|
||||
<Form.Item label={$t('所属团队')} name="team" className="mt-[16px]" rules={[{ required: true }]}>
|
||||
<Select
|
||||
showSearch
|
||||
optionFilterProp="label"
|
||||
className="w-INPUT_NORMAL"
|
||||
placeholder={$t(PLACEHOLDER.input)}
|
||||
options={teamList}
|
||||
|
||||
@@ -171,6 +171,8 @@ const AddLoadBalancingModel = forwardRef<LoadBalancingHandle>((props, ref: any)
|
||||
>
|
||||
<Form.Item<LoadModelDetailData> label={$t('模型类型')} name="type" rules={[{ required: true }]}>
|
||||
<Select
|
||||
showSearch
|
||||
optionFilterProp="label"
|
||||
className="w-INPUT_NORMAL"
|
||||
placeholder={$t(PLACEHOLDER.select)}
|
||||
options={modelTypeList}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { App, Button, Card, Empty, Select } from 'antd'
|
||||
import { App, Button, Card, CascaderProps, Empty, Select } from 'antd'
|
||||
import { $t } from '@common/locales/index.ts'
|
||||
import { Icon } from '@iconify/react/dist/iconify.js'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import { forwardRef, useEffect, useImperativeHandle, useRef, useState } from 'react'
|
||||
import ReactJson from 'react-json-view'
|
||||
import { IconButton } from '@common/components/postcat/api/IconButton'
|
||||
import { BasicResponse, RESPONSE_TIPS, STATUS_CODE } from '@common/const/const'
|
||||
@@ -13,6 +13,7 @@ import { useNavigate } from 'react-router-dom'
|
||||
import { ServiceDetailType } from '@market/const/serviceHub/type'
|
||||
import useCopyToClipboard from '@common/hooks/copy'
|
||||
import { useGlobalContext } from '@common/contexts/GlobalStateContext'
|
||||
import { Cascader } from 'antd/lib'
|
||||
|
||||
type ConfigList = {
|
||||
openApi?: {
|
||||
@@ -33,27 +34,49 @@ type ApiKeyItem = {
|
||||
name: string
|
||||
value: string
|
||||
}
|
||||
interface Option {
|
||||
value: string
|
||||
label: string
|
||||
children?: Option[]
|
||||
}
|
||||
|
||||
const IntegrationAIContainer = ({
|
||||
type,
|
||||
handleToolsChange,
|
||||
customClassName,
|
||||
service,
|
||||
serviceId,
|
||||
currentTab
|
||||
}: {
|
||||
type ServiceApiKeyList = {
|
||||
id: string
|
||||
name: string
|
||||
apikeys: Array<{
|
||||
id: string
|
||||
name: string
|
||||
value: string
|
||||
expired: number
|
||||
}>
|
||||
}
|
||||
export interface IntegrationAIContainerRef {
|
||||
getServiceKeysList: () => void;
|
||||
}
|
||||
export interface IntegrationAIContainerProps {
|
||||
type: 'global' | 'service'
|
||||
handleToolsChange: (value: Tool[]) => void
|
||||
customClassName?: string
|
||||
service?: ServiceDetailType
|
||||
serviceId?: string
|
||||
currentTab?: string
|
||||
}) => {
|
||||
openModal?: (type: 'apply') => void
|
||||
}
|
||||
export const IntegrationAIContainer = forwardRef<IntegrationAIContainerRef, IntegrationAIContainerProps>(
|
||||
({
|
||||
type,
|
||||
handleToolsChange,
|
||||
customClassName,
|
||||
service,
|
||||
serviceId,
|
||||
currentTab,
|
||||
openModal
|
||||
}: IntegrationAIContainerProps, ref) => {
|
||||
const [activeTab, setActiveTab] = useState(type === 'service' ? 'openApi' : 'mcp')
|
||||
const { message } = App.useApp()
|
||||
const [configContent, setConfigContent] = useState<string>('')
|
||||
const [apiKey, setApiKey] = useState<string>('')
|
||||
const [apiKeyList, setApiKeyList] = useState<{ value: string; label: string }[]>([])
|
||||
const [apiKeyList, setApiKeyList] = useState<any[]>([])
|
||||
const [mcpServerUrl, setMcpServerUrl] = useState<string>('')
|
||||
const { state } = useGlobalContext()
|
||||
const navigator = useNavigate()
|
||||
@@ -103,9 +126,12 @@ const IntegrationAIContainer = ({
|
||||
}
|
||||
}
|
||||
|
||||
const handleChange = (value: string) => {
|
||||
const handleSelectChange = (value: string) => {
|
||||
setApiKey(value)
|
||||
}
|
||||
const handleChange: CascaderProps<Option>['onChange'] = (value) => {
|
||||
setApiKey(value.at(-1) || '')
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取全局 MCP 配置
|
||||
@@ -139,10 +165,10 @@ const IntegrationAIContainer = ({
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 API Key 列表
|
||||
* 获取全局 API Key 列表
|
||||
*/
|
||||
const getKeysList = () => {
|
||||
fetchData<BasicResponse<null>>(type === 'global' ? 'simple/system/apikeys' : `my/apikeys/${serviceId}`, {
|
||||
const getGlobalKeysList = () => {
|
||||
fetchData<BasicResponse<null>>('simple/system/apikeys', {
|
||||
method: 'GET'
|
||||
})
|
||||
.then((response) => {
|
||||
@@ -167,6 +193,39 @@ const IntegrationAIContainer = ({
|
||||
message.error(errorInfo || $t(RESPONSE_TIPS.error))
|
||||
})
|
||||
}
|
||||
useImperativeHandle(ref, () => ({
|
||||
getServiceKeysList
|
||||
}))
|
||||
/**
|
||||
* 获取服务 API Key 列表
|
||||
*/
|
||||
const getServiceKeysList = () => {
|
||||
fetchData<BasicResponse<null>>(`my/apikeys/${serviceId}`, {
|
||||
method: 'GET'
|
||||
})
|
||||
.then((response) => {
|
||||
const { code, msg, data } = response
|
||||
if (code === STATUS_CODE.SUCCESS) {
|
||||
if (data.apps && data.apps.length > 0) {
|
||||
// 转换数据结构为 Cascader 所需格式
|
||||
const transformedData = data.apps.map((app: ServiceApiKeyList) => ({
|
||||
value: app.id,
|
||||
label: app.name,
|
||||
children: app.apikeys.map((key) => ({
|
||||
...key,
|
||||
label: key.name
|
||||
}))
|
||||
}))
|
||||
setApiKeyList(transformedData)
|
||||
}
|
||||
} else {
|
||||
message.error(msg || $t(RESPONSE_TIPS.error))
|
||||
}
|
||||
})
|
||||
.catch((errorInfo) => {
|
||||
message.error(errorInfo || $t(RESPONSE_TIPS.error))
|
||||
})
|
||||
}
|
||||
|
||||
const clearError = (tabKey: keyof typeof errors) => {
|
||||
setErrors((prev) => ({ ...prev, [tabKey]: null }))
|
||||
@@ -238,7 +297,11 @@ const IntegrationAIContainer = ({
|
||||
} else {
|
||||
service?.basic.enableMcp && setMcpServerUrl(`mcp/service/${serviceId}/sse`)
|
||||
}
|
||||
getKeysList()
|
||||
if (type === 'global') {
|
||||
getGlobalKeysList()
|
||||
} else {
|
||||
getServiceKeysList()
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
@@ -307,125 +370,164 @@ const IntegrationAIContainer = ({
|
||||
/>
|
||||
{$t('AI 代理集成')}
|
||||
</p>
|
||||
<div className="tab-container mt-3">
|
||||
{type === 'service' && service?.basic.enableMcp && (
|
||||
<div className="tab-nav flex rounded-md overflow-hidden border border-solid border-[#3D46F2] w-fit">
|
||||
<div
|
||||
className={`tab-item px-5 py-1.5 cursor-pointer text-sm transition-colors ${activeTab === 'openApi' ? 'bg-[#3D46F2] text-white' : 'bg-white text-[#3D46F2]'}`}
|
||||
onClick={() => setActiveTab('openApi')}
|
||||
>
|
||||
Open API
|
||||
{type === 'service' && service?.basic.enableMcp && (
|
||||
<div className="mt-3 tab-nav flex rounded-md overflow-hidden border border-solid border-[#3D46F2] w-fit">
|
||||
<div
|
||||
className={`tab-item px-5 py-1.5 cursor-pointer text-sm transition-colors ${activeTab === 'openApi' ? 'bg-[#3D46F2] text-white' : 'bg-white text-[#3D46F2]'}`}
|
||||
onClick={() => setActiveTab('openApi')}
|
||||
>
|
||||
Open API
|
||||
</div>
|
||||
<div
|
||||
className={`tab-item px-5 py-1.5 cursor-pointer text-sm transition-colors ${activeTab === 'mcp' ? 'bg-[#3D46F2] text-white' : 'bg-white text-[#3D46F2]'}`}
|
||||
onClick={() => setActiveTab('mcp')}
|
||||
>
|
||||
MCP
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{type === 'service' && !apiKeyList.length ? (
|
||||
<>
|
||||
<Card
|
||||
style={{ borderRadius: '10px' }}
|
||||
className={`w-full mt-3`}
|
||||
classNames={{
|
||||
body: 'p-[10px]'
|
||||
}}
|
||||
>
|
||||
<div className="flex flex-col items-center justify-center py-3">
|
||||
<span className="text-[14px] mb-5">{$t('请先订阅该服务')}</span>
|
||||
<Button type="primary" onClick={() => openModal?.('apply')}>
|
||||
{$t('申请')}
|
||||
</Button>
|
||||
</div>
|
||||
<div
|
||||
className={`tab-item px-5 py-1.5 cursor-pointer text-sm transition-colors ${activeTab === 'mcp' ? 'bg-[#3D46F2] text-white' : 'bg-white text-[#3D46F2]'}`}
|
||||
onClick={() => setActiveTab('mcp')}
|
||||
>
|
||||
MCP
|
||||
</Card>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<div className="tab-container mt-3">
|
||||
<div className="tab-content font-semibold mt-[10px]">
|
||||
{activeTab === 'openApi' ? tabContent.openApi?.title : tabContent.mcp.title}
|
||||
</div>
|
||||
{/* 标签页内容区域 */}
|
||||
<div className="bg-[#0a0b21] text-white p-4 rounded-md my-2 font-mono text-sm overflow-auto relative">
|
||||
{activeTab === 'mcp' ? (
|
||||
<ReactJson
|
||||
src={
|
||||
configContent
|
||||
? typeof configContent === 'string'
|
||||
? (() => {
|
||||
try {
|
||||
return JSON.parse(configContent)
|
||||
} catch (e) {
|
||||
return {}
|
||||
}
|
||||
})()
|
||||
: configContent
|
||||
: {}
|
||||
}
|
||||
theme="monokai"
|
||||
indentWidth={2}
|
||||
displayDataTypes={false}
|
||||
displayObjectSize={false}
|
||||
name={false}
|
||||
collapsed={false}
|
||||
enableClipboard={false}
|
||||
style={{
|
||||
backgroundColor: 'transparent',
|
||||
wordBreak: 'break-word',
|
||||
whiteSpace: 'normal'
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
<pre className="whitespace-pre-wrap break-words">{configContent || ''}</pre>
|
||||
</>
|
||||
)}
|
||||
<IconButton
|
||||
name="copy"
|
||||
onClick={() => handleCopy(configContent)}
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
top: '5px',
|
||||
right: '5px',
|
||||
color: '#999',
|
||||
transition: 'none',
|
||||
'&.MuiButtonBase-root:hover': {
|
||||
background: 'transparent',
|
||||
color: '#3D46F2',
|
||||
transition: 'none'
|
||||
}
|
||||
}}
|
||||
></IconButton>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div className="tab-content font-semibold mt-[10px]">
|
||||
{activeTab === 'openApi' ? tabContent.openApi?.title : tabContent.mcp.title}
|
||||
</div>
|
||||
{/* 标签页内容区域 */}
|
||||
<div className="bg-[#0a0b21] text-white p-4 rounded-md my-2 font-mono text-sm overflow-auto relative">
|
||||
{activeTab === 'mcp' ? (
|
||||
<ReactJson
|
||||
src={
|
||||
configContent
|
||||
? typeof configContent === 'string'
|
||||
? (() => {
|
||||
try {
|
||||
return JSON.parse(configContent);
|
||||
} catch (e) {
|
||||
return {};
|
||||
}
|
||||
})()
|
||||
: configContent
|
||||
: {}
|
||||
}
|
||||
theme="monokai"
|
||||
indentWidth={2}
|
||||
displayDataTypes={false}
|
||||
displayObjectSize={false}
|
||||
name={false}
|
||||
collapsed={false}
|
||||
enableClipboard={false}
|
||||
style={{
|
||||
backgroundColor: 'transparent',
|
||||
wordBreak: 'break-word',
|
||||
whiteSpace: 'normal'
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
{activeTab === 'mcp' && (
|
||||
<>
|
||||
<pre className="whitespace-pre-wrap break-words">{configContent || ''}</pre>
|
||||
<div className="tab-content font-semibold my-[10px]">API Key</div>
|
||||
{apiKeyList.length ? (
|
||||
<>
|
||||
{type === 'global' ? (
|
||||
<>
|
||||
<Select
|
||||
showSearch
|
||||
optionFilterProp="label"
|
||||
value={apiKey}
|
||||
className="w-full"
|
||||
onChange={handleSelectChange}
|
||||
options={apiKeyList}
|
||||
/>
|
||||
<Card
|
||||
style={{ borderRadius: '5px' }}
|
||||
className="w-full mt-[5px] "
|
||||
classNames={{
|
||||
body: 'p-[5px]'
|
||||
}}
|
||||
>
|
||||
<div className="relative h-[25px]">
|
||||
{apiKey}
|
||||
<IconButton
|
||||
name="copy"
|
||||
onClick={() => handleCopy(apiKey)}
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
top: '0px',
|
||||
right: '5px',
|
||||
color: '#999',
|
||||
transition: 'none',
|
||||
'&.MuiButtonBase-root:hover': {
|
||||
background: 'transparent',
|
||||
color: '#3D46F2',
|
||||
transition: 'none'
|
||||
}
|
||||
}}
|
||||
></IconButton>
|
||||
</div>
|
||||
</Card>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Cascader
|
||||
allowClear={false}
|
||||
options={apiKeyList}
|
||||
onChange={handleChange}
|
||||
placeholder={$t('选择 API Key')}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description={''}>
|
||||
<Button onClick={addKey} type="primary">
|
||||
{$t('新增 API Key')}
|
||||
</Button>
|
||||
</Empty>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
<IconButton
|
||||
name="copy"
|
||||
onClick={() => handleCopy(configContent)}
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
top: '5px',
|
||||
right: '5px',
|
||||
color: '#999',
|
||||
transition: 'none',
|
||||
'&.MuiButtonBase-root:hover': {
|
||||
background: 'transparent',
|
||||
color: '#3D46F2',
|
||||
transition: 'none'
|
||||
}
|
||||
}}
|
||||
></IconButton>
|
||||
</div>
|
||||
</div>
|
||||
{activeTab === 'mcp' && (
|
||||
<>
|
||||
<div className="tab-content font-semibold my-[10px]">API Key</div>
|
||||
{apiKeyList.length ? (
|
||||
<>
|
||||
<Select value={apiKey} className="w-full" onChange={handleChange} options={apiKeyList} />
|
||||
<Card
|
||||
style={{ borderRadius: '5px' }}
|
||||
className="w-full mt-[5px] "
|
||||
classNames={{
|
||||
body: 'p-[5px]'
|
||||
}}
|
||||
>
|
||||
<div className="relative h-[25px]">
|
||||
{apiKey}
|
||||
<IconButton
|
||||
name="copy"
|
||||
onClick={() => handleCopy(apiKey)}
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
top: '0px',
|
||||
right: '5px',
|
||||
color: '#999',
|
||||
transition: 'none',
|
||||
'&.MuiButtonBase-root:hover': {
|
||||
background: 'transparent',
|
||||
color: '#3D46F2',
|
||||
transition: 'none'
|
||||
}
|
||||
}}
|
||||
></IconButton>
|
||||
</div>
|
||||
</Card>
|
||||
</>
|
||||
) : (
|
||||
<Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description={''}>
|
||||
<Button onClick={addKey} type="primary">
|
||||
{$t('新增 API Key')}
|
||||
</Button>
|
||||
</Empty>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</Card>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default IntegrationAIContainer
|
||||
})
|
||||
@@ -1,6 +1,6 @@
|
||||
import InsidePage from "@common/components/aoplatform/InsidePage"
|
||||
import { $t } from '@common/locales/index.ts'
|
||||
import IntegrationAIContainer from "./IntegrationAIContainer"
|
||||
import { IntegrationAIContainer } from "./IntegrationAIContainer"
|
||||
import { Tool } from "@modelcontextprotocol/sdk/types.js"
|
||||
import { useEffect, useState } from "react"
|
||||
import McpToolsContainer from "./McpToolsContainer"
|
||||
|
||||
@@ -480,6 +480,8 @@ const MemberList = () => {
|
||||
render: (_, entity) => (
|
||||
<WithPermission access="system.organization.member.edit">
|
||||
<Select
|
||||
showSearch
|
||||
optionFilterProp="label"
|
||||
className="w-full"
|
||||
mode="multiple"
|
||||
value={entity.roles?.map((x: EntityItem) => x.id)}
|
||||
|
||||
@@ -60,7 +60,13 @@ export type DashboardSettingEditProps = {
|
||||
name="driver"
|
||||
rules={[{ required: true }]}
|
||||
>
|
||||
<Select className="w-INPUT_NORMAL" placeholder={$t(PLACEHOLDER.select)} options={[...DASHBOARD_SETTING_DRIVER_OPTION_LIST]}/>
|
||||
<Select
|
||||
showSearch
|
||||
optionFilterProp="label"
|
||||
className="w-INPUT_NORMAL"
|
||||
placeholder={$t(PLACEHOLDER.select)}
|
||||
options={[...DASHBOARD_SETTING_DRIVER_OPTION_LIST]}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item<PartitionDashboardConfigFieldType>
|
||||
|
||||
@@ -301,7 +301,13 @@ const FilterForm = forwardRef<FilterFormHandle, FilterFormProps>(
|
||||
return (
|
||||
<Form form={form} layout="vertical" onValuesChange={handleValuesChange}>
|
||||
<Form.Item name="name" label={$t('属性名称')} rules={[{ required: true }]}>
|
||||
<Select disabled={disabled} onChange={handleTypeChange} options={filterOptionsList} />
|
||||
<Select
|
||||
showSearch
|
||||
optionFilterProp="label"
|
||||
disabled={disabled}
|
||||
onChange={handleTypeChange}
|
||||
options={filterOptionsList}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="values"
|
||||
|
||||
@@ -139,8 +139,13 @@ const DataMaskingConfig = forwardRef<DataMaskingConfigHandle>((_,ref) => {
|
||||
name="type"
|
||||
rules={[{ required: true }]}
|
||||
>
|
||||
<Select className="w-INPUT_NORMAL" placeholder={$t(PLACEHOLDER.input)} options={policyOptions} >
|
||||
</Select>
|
||||
<Select
|
||||
showSearch
|
||||
optionFilterProp="label"
|
||||
className="w-INPUT_NORMAL"
|
||||
placeholder={$t(PLACEHOLDER.input)}
|
||||
options={policyOptions}
|
||||
></Select>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item<DataMaskingConfigFieldType>
|
||||
|
||||
@@ -146,6 +146,8 @@ const DataMaskRuleForm: React.FC<DataMaskRuleFormProps> = ({
|
||||
<Form form={form} layout="vertical" className="p-4">
|
||||
<Form.Item name={['match', 'type']} label={$t('匹配类型')} rules={[{ required: true }]}>
|
||||
<Select
|
||||
showSearch
|
||||
optionFilterProp="label"
|
||||
placeholder={$t(PLACEHOLDER.select)}
|
||||
onChange={handleMatchTypeChange}
|
||||
options={matchRuleOptions}
|
||||
@@ -156,6 +158,8 @@ const DataMaskRuleForm: React.FC<DataMaskRuleFormProps> = ({
|
||||
<Form.Item name={['match', 'value']} label={$t('匹配值')} rules={[{ required: true }]}>
|
||||
{matchType === 'inner' ? (
|
||||
<Select
|
||||
showSearch
|
||||
optionFilterProp="label"
|
||||
placeholder={$t(PLACEHOLDER.select)}
|
||||
onChange={handleMatchValueChange}
|
||||
options={dataFormatOptions}
|
||||
@@ -168,6 +172,8 @@ const DataMaskRuleForm: React.FC<DataMaskRuleFormProps> = ({
|
||||
|
||||
<Form.Item name={['mask', 'type']} label={$t('脱敏类型')} rules={[{ required: true }]}>
|
||||
<Select
|
||||
showSearch
|
||||
optionFilterProp="label"
|
||||
placeholder={$t(PLACEHOLDER.select)}
|
||||
onChange={handleMaskTypeChange}
|
||||
options={
|
||||
@@ -197,6 +203,8 @@ const DataMaskRuleForm: React.FC<DataMaskRuleFormProps> = ({
|
||||
rules={[{ required: true }]}
|
||||
>
|
||||
<Select
|
||||
showSearch
|
||||
optionFilterProp="label"
|
||||
placeholder={$t(PLACEHOLDER.select)}
|
||||
onChange={handleReplaceTypeChange}
|
||||
options={dataMaskReplaceStrOptions}
|
||||
|
||||
@@ -551,6 +551,8 @@ const SystemConfig = forwardRef<SystemConfigHandle>((_, ref) => {
|
||||
{!onEdit && (
|
||||
<Form.Item<SystemConfigFieldType> label={$t('所属团队')} name="team" rules={[{ required: true }]}>
|
||||
<Select
|
||||
showSearch
|
||||
optionFilterProp="label"
|
||||
className="w-INPUT_NORMAL"
|
||||
disabled={onEdit}
|
||||
placeholder={$t(PLACEHOLDER.input)}
|
||||
@@ -627,6 +629,8 @@ const SystemConfig = forwardRef<SystemConfigHandle>((_, ref) => {
|
||||
|
||||
<Form.Item<SystemConfigFieldType> label={$t('标签')} name="tags">
|
||||
<Select
|
||||
showSearch
|
||||
optionFilterProp="label"
|
||||
className="w-INPUT_NORMAL"
|
||||
mode="tags"
|
||||
placeholder={$t(PLACEHOLDER.select)}
|
||||
|
||||
@@ -180,10 +180,24 @@ const SystemList: FC = () => {
|
||||
<span
|
||||
className={`text-[13px] `}
|
||||
>
|
||||
{$t(SERVICE_KIND_OPTIONS.find((x) => x.value === record.service_kind)?.label || '-')}
|
||||
{record.enable_mcp && (
|
||||
<Tag color="#ffc107" className="text-[#000] ml-[5px]">MCP</Tag>
|
||||
)}
|
||||
<Tag
|
||||
color={`#${record.service_kind === 'ai' ? 'EADEFF' : 'DEFFE7'}`}
|
||||
className={`text-[#000] font-normal border-0 mr-[10px] max-w-[150px] truncate`}
|
||||
bordered={false}
|
||||
title={record.service_kind || '-'}
|
||||
>
|
||||
{SERVICE_KIND_OPTIONS.find((x) => x.value === record.service_kind)?.label || '-'}
|
||||
</Tag>
|
||||
{record?.enable_mcp && (
|
||||
<Tag
|
||||
color="#FFF0C1"
|
||||
className="text-[#000] font-normal border-0 mr-[12px] max-w-[150px] truncate"
|
||||
bordered={false}
|
||||
title={'MCP'}
|
||||
>
|
||||
MCP
|
||||
</Tag>
|
||||
)}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -30,7 +30,10 @@ const SystemInsideApiProxy = forwardRef<SystemInsideApiProxyHandle,SystemInsideA
|
||||
const ProxyHeadeerConfig = useMemo(()=>PROXY_HEADER_CONFIG.map((x)=>({
|
||||
...x,
|
||||
...(x.key === 'optType' ? {
|
||||
component: <Select className="w-INPUT_NORMAL" options={UPSTREAM_PROXY_HEADER_TYPE_OPTIONS.map((x)=>({...x, label:$t(x.label)}))}/>
|
||||
component: <Select
|
||||
showSearch
|
||||
optionFilterProp="label"
|
||||
className="w-INPUT_NORMAL" options={UPSTREAM_PROXY_HEADER_TYPE_OPTIONS.map((x)=>({...x, label:$t(x.label)}))}/>
|
||||
} : {})
|
||||
}))
|
||||
,[state.language])
|
||||
|
||||
@@ -179,6 +179,8 @@ const SystemInsideRouterCreate = forwardRef<SystemInsideRouterCreateHandle, Syst
|
||||
...item,
|
||||
component: (
|
||||
<Select
|
||||
showSearch
|
||||
optionFilterProp="label"
|
||||
className="w-INPUT_NORMAL"
|
||||
options={Object.entries(MatchPositionEnum)?.map(([key, value]) => {
|
||||
return { label: $t(value), value: key }
|
||||
@@ -192,6 +194,8 @@ const SystemInsideRouterCreate = forwardRef<SystemInsideRouterCreateHandle, Syst
|
||||
...item,
|
||||
component: (
|
||||
<Select
|
||||
showSearch
|
||||
optionFilterProp="label"
|
||||
className="w-INPUT_NORMAL"
|
||||
options={Object.entries(MatchTypeEnum)?.map(([key, value]) => {
|
||||
return { label: $t(value), value: key }
|
||||
@@ -252,6 +256,8 @@ const SystemInsideRouterCreate = forwardRef<SystemInsideRouterCreateHandle, Syst
|
||||
|
||||
<Form.Item<SystemApiProxyFieldType> label={$t('请求协议')} name="protocols" rules={[{ required: true }]}>
|
||||
<Select
|
||||
showSearch
|
||||
optionFilterProp="label"
|
||||
className="w-INPUT_NORMAL"
|
||||
placeholder={$t(PLACEHOLDER.select)}
|
||||
mode="multiple"
|
||||
@@ -271,6 +277,8 @@ const SystemInsideRouterCreate = forwardRef<SystemInsideRouterCreateHandle, Syst
|
||||
noStyle
|
||||
>
|
||||
<Select
|
||||
showSearch
|
||||
optionFilterProp="label"
|
||||
placeholder={$t(PLACEHOLDER.select)}
|
||||
options={apiPathMatchRulesOptions}
|
||||
className="w-[30%] min-w-[100px]"
|
||||
@@ -307,6 +315,8 @@ const SystemInsideRouterCreate = forwardRef<SystemInsideRouterCreateHandle, Syst
|
||||
|
||||
<Form.Item<SystemApiProxyFieldType> label={$t('请求方式')} name="methods" rules={[{ required: true }]}>
|
||||
<Select
|
||||
showSearch
|
||||
optionFilterProp="label"
|
||||
className="w-INPUT_NORMAL"
|
||||
placeholder={$t(PLACEHOLDER.select)}
|
||||
mode="multiple"
|
||||
|
||||
@@ -129,7 +129,7 @@ const globalConfigNodesRule: FormItemProps['rules'] = [
|
||||
const ProxyHeadeerConfig = useMemo(()=>PROXY_HEADER_CONFIG.map((x)=>({
|
||||
...x,
|
||||
...(x.key === 'optType' ? {
|
||||
component: <Select className="w-INPUT_NORMAL" options={UPSTREAM_PROXY_HEADER_TYPE_OPTIONS.map((x)=>({...x, label:$t(x.label)}))}/>
|
||||
component: <Select showSearch optionFilterProp="label" className="w-INPUT_NORMAL" options={UPSTREAM_PROXY_HEADER_TYPE_OPTIONS.map((x)=>({...x, label:$t(x.label)}))}/>
|
||||
} : {})
|
||||
}))
|
||||
,[state.language])
|
||||
@@ -175,7 +175,7 @@ const globalConfigNodesRule: FormItemProps['rules'] = [
|
||||
name="scheme"
|
||||
rules={[{ required: true }]}
|
||||
>
|
||||
<Select className="w-INPUT_NORMAL" placeholder={$t(PLACEHOLDER.select)} options={schemeOptions}>
|
||||
<Select showSearch optionFilterProp="label" className="w-INPUT_NORMAL" placeholder={$t(PLACEHOLDER.select)} options={schemeOptions}>
|
||||
</Select>
|
||||
</Form.Item>
|
||||
|
||||
@@ -192,7 +192,7 @@ const globalConfigNodesRule: FormItemProps['rules'] = [
|
||||
name="passHost"
|
||||
rules={[{ required: true }]}
|
||||
>
|
||||
<Select className="w-INPUT_NORMAL" placeholder={$t(PLACEHOLDER.select)} options={passHostOptions} onChange={(val)=>setFormShowHost(val === 'rewrite')}>
|
||||
<Select showSearch optionFilterProp="label" className="w-INPUT_NORMAL" placeholder={$t(PLACEHOLDER.select)} options={passHostOptions} onChange={(val)=>setFormShowHost(val === 'rewrite')}>
|
||||
</Select>
|
||||
</Form.Item>
|
||||
|
||||
|
||||
@@ -205,6 +205,8 @@ const TeamConfig = forwardRef<TeamConfigHandle, TeamConfigProps>((props, ref) =>
|
||||
rules={[{ required: true }]}
|
||||
>
|
||||
<Select
|
||||
showSearch
|
||||
optionFilterProp="label"
|
||||
className="w-INPUT_NORMAL"
|
||||
placeholder={$t(PLACEHOLDER.select)}
|
||||
options={managerOption}
|
||||
|
||||
@@ -324,6 +324,8 @@ const TeamInsideMember: FC = () => {
|
||||
render: (_, entity) => (
|
||||
<WithPermission access="team.team.member.edit">
|
||||
<Select
|
||||
showSearch
|
||||
optionFilterProp="label"
|
||||
className="w-full"
|
||||
mode="multiple"
|
||||
maxTagCount="responsive"
|
||||
|
||||
@@ -200,6 +200,8 @@ export default function MonitorApiPage(props: MonitorApiPageProps) {
|
||||
<div className="flex flex-nowrap items-center pt-btnybase mr-btnybase">
|
||||
<label className="inline-block whitespace-nowrap">{$t('服务')}:</label>
|
||||
<Select
|
||||
showSearch
|
||||
optionFilterProp="label"
|
||||
className="w-[346px]"
|
||||
value={queryData?.services}
|
||||
options={projectOptionList}
|
||||
@@ -216,6 +218,8 @@ export default function MonitorApiPage(props: MonitorApiPageProps) {
|
||||
<div className="flex flex-nowrap items-center pt-btnybase mr-btnybase">
|
||||
<label className=" whitespace-nowrap inline-block w-[42px] text-right">API :</label>
|
||||
<Select
|
||||
showSearch
|
||||
optionFilterProp="label"
|
||||
className="w-[346px]"
|
||||
value={queryData?.apis}
|
||||
options={apiOptionList}
|
||||
|
||||
@@ -176,6 +176,8 @@ export default function MonitorAppPage(props: MonitorAppPageProps) {
|
||||
<div>
|
||||
<label className="inline-block whitespace-nowrap">{$t('消费者')}:</label>
|
||||
<Select
|
||||
showSearch
|
||||
optionFilterProp="label"
|
||||
className="w-[346px]"
|
||||
mode="multiple"
|
||||
maxTagCount={1}
|
||||
|
||||
@@ -181,6 +181,8 @@ export default function MonitorSubPage(props: MonitorSubPageProps) {
|
||||
<div>
|
||||
<label className="inline-block whitespace-nowrap">服务:</label>
|
||||
<Select
|
||||
showSearch
|
||||
optionFilterProp="label"
|
||||
className="w-[346px]"
|
||||
mode="multiple"
|
||||
maxTagCount={1}
|
||||
|
||||
@@ -1053,6 +1053,11 @@ p{
|
||||
.ant-table-wrapper .ant-table{
|
||||
scrollbar-color: none !important;
|
||||
}
|
||||
.ant-select .ant-select-clear {
|
||||
height:16px !important;
|
||||
width: 16px !important;
|
||||
top: 45%;
|
||||
}
|
||||
|
||||
.eo_page_drag .ant-table-body{
|
||||
overflow-y: auto !important;
|
||||
|
||||
@@ -70,6 +70,8 @@ export const ApplyServiceModal = forwardRef<ApplyServiceHandle, ApplyServiceProp
|
||||
</Row>
|
||||
<Form.Item label={$t('消费者')} name="applications" rules={[{ required: true }]}>
|
||||
<Select
|
||||
showSearch
|
||||
optionFilterProp="label"
|
||||
className="w-INPUT_NORMAL"
|
||||
disabled={reApply}
|
||||
placeholder={$t('搜索或选择消费者')}
|
||||
|
||||
@@ -15,7 +15,7 @@ import { ApplyServiceHandle, ServiceBasicInfoType, ServiceDetailType } from '../
|
||||
import { ApplyServiceModal } from './ApplyServiceModal.tsx'
|
||||
import ServiceHubApiDocument from './ServiceHubApiDocument.tsx'
|
||||
import { SERVICE_KIND_OPTIONS } from '@core/const/system/const.tsx'
|
||||
import IntegrationAIContainer from '@core/pages/mcpService/IntegrationAIContainer.tsx'
|
||||
import { IntegrationAIContainer, IntegrationAIContainerRef } from '@core/pages/mcpService/IntegrationAIContainer.tsx'
|
||||
import { Tool } from '@modelcontextprotocol/sdk/types.js'
|
||||
import McpToolsContainer from '@core/pages/mcpService/McpToolsContainer.tsx'
|
||||
import { useGlobalContext } from '@common/contexts/GlobalStateContext.tsx'
|
||||
@@ -48,6 +48,7 @@ const ServiceHubDetail = () => {
|
||||
const [tabItem, setTabItem] = useState<TabItemType[]>([])
|
||||
const [currentTab, setCurrentTab] = useState('')
|
||||
const { state } = useGlobalContext()
|
||||
const integrationAIContainerRef = useRef<IntegrationAIContainerRef>(null)
|
||||
const navigate = useNavigate()
|
||||
|
||||
const modifyApiDoc = (apiDoc: string, apiPrefix: string) => {
|
||||
@@ -191,7 +192,7 @@ servers:
|
||||
content: serviceBasicInfo?.catalogue?.name || '-'
|
||||
},
|
||||
{
|
||||
color: '#fbe5e5',
|
||||
color: `#${serviceBasicInfo?.serviceKind === 'ai' ? 'EADEFF' : 'DEFFE7'}`,
|
||||
textColor: 'text-[#000]',
|
||||
title: serviceBasicInfo?.serviceKind || '-',
|
||||
content: SERVICE_KIND_OPTIONS.find((x) => x.value === serviceBasicInfo?.serviceKind)?.label || '-'
|
||||
@@ -201,7 +202,7 @@ servers:
|
||||
// 如果启用了MCP,添加MCP标签
|
||||
if (serviceBasicInfo?.enableMcp) {
|
||||
tags.push({
|
||||
color: '#ffc107',
|
||||
color: '#FFF0C1',
|
||||
textColor: 'text-[#000]',
|
||||
title: 'MCP',
|
||||
content: 'MCP'
|
||||
@@ -263,7 +264,9 @@ servers:
|
||||
),
|
||||
onOk: () => {
|
||||
return applyRef.current?.apply().then((res) => {
|
||||
// if(res === true) setApplied(true)
|
||||
if (res === true) {
|
||||
integrationAIContainerRef.current?.getServiceKeysList()
|
||||
}
|
||||
})
|
||||
},
|
||||
okText: $t('确认'),
|
||||
@@ -490,11 +493,13 @@ servers:
|
||||
items={tabItem}
|
||||
/>
|
||||
<IntegrationAIContainer
|
||||
ref={integrationAIContainerRef}
|
||||
service={service}
|
||||
currentTab={currentTab}
|
||||
serviceId={serviceId}
|
||||
customClassName="mt-[70px] max-h-[calc(100vh-420px)] overflow-auto"
|
||||
type={'service'}
|
||||
openModal={openModal}
|
||||
handleToolsChange={handleToolsChange}
|
||||
></IntegrationAIContainer>
|
||||
</div>
|
||||
|
||||
@@ -119,7 +119,7 @@ export const ServiceHubGroup = ({ children, filterOption, dispatch }: ServiceHub
|
||||
|
||||
return (
|
||||
<div className="flex flex-1 h-full">
|
||||
<div className="w-[220px] border-0 border-solid border-r-[1px] border-r-BORDER">
|
||||
<div className="w-[220px] border-0">
|
||||
<div className=" h-full">
|
||||
<Input
|
||||
className="rounded-SEARCH_RADIUS m-[10px] h-[40px] bg-[#f8f8f8] w-[200px]"
|
||||
|
||||
@@ -45,7 +45,7 @@ export const initialServiceHubListState = {
|
||||
selectedTag: [] as string[],
|
||||
keyword: '',
|
||||
getCateAndTagData: false,
|
||||
listLoading: false
|
||||
listLoading: true
|
||||
}
|
||||
|
||||
function reducer(state: typeof initialServiceHubListState, action: ServiceHubListActionType) {
|
||||
@@ -142,7 +142,13 @@ const ServiceHubList: FC = () => {
|
||||
<Spin
|
||||
className="h-full"
|
||||
wrapperClassName="h-full"
|
||||
indicator={<LoadingOutlined style={{ fontSize: 24 }} spin />}
|
||||
indicator={
|
||||
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
|
||||
<div style={{ transform: 'scale(1.5)' }}>
|
||||
<LoadingOutlined style={{ fontSize: 30 }} spin />
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
spinning={filterOption.listLoading}
|
||||
>
|
||||
{filterOption.showServicesList && filterOption.showServicesList.length > 0 ? (
|
||||
@@ -189,7 +195,12 @@ const ServiceHubList: FC = () => {
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />
|
||||
<>
|
||||
{!filterOption.listLoading &&
|
||||
(!filterOption.showServicesList || filterOption.showServicesList.length === 0) && (
|
||||
<Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</Spin>
|
||||
</div>
|
||||
@@ -225,7 +236,7 @@ const CardTitle = (service: ServiceHubTableListItem) => {
|
||||
<div className="mt-[10px] h-[20px] flex items-center font-normal">
|
||||
<Tag
|
||||
color="#7371fc1b"
|
||||
className="text-theme font-normal border-0 mr-[12px] max-w-[150px] truncate"
|
||||
className="text-theme font-normal border-0 mr-[12px] max-w-[100px] truncate"
|
||||
key={service.id}
|
||||
bordered={false}
|
||||
title={service.catalogue?.name || '-'}
|
||||
@@ -233,8 +244,8 @@ const CardTitle = (service: ServiceHubTableListItem) => {
|
||||
{service.catalogue?.name || '-'}
|
||||
</Tag>
|
||||
<Tag
|
||||
color="#fbe5e5"
|
||||
className="text-[#000] font-normal border-0 mr-[12px] max-w-[150px] truncate"
|
||||
color={`#${service.serviceKind === 'ai' ? 'EADEFF' : 'DEFFE7'}`}
|
||||
className={`text-[#000] font-normal border-0 mr-[12px] max-w-[150px] truncate`}
|
||||
bordered={false}
|
||||
title={service.serviceKind || '-'}
|
||||
>
|
||||
@@ -242,7 +253,7 @@ const CardTitle = (service: ServiceHubTableListItem) => {
|
||||
</Tag>
|
||||
{service?.enableMcp && (
|
||||
<Tag
|
||||
color="#ffc107"
|
||||
color="#FFF0C1"
|
||||
className="text-[#000] font-normal border-0 mr-[12px] max-w-[150px] truncate"
|
||||
bordered={false}
|
||||
title={'MCP'}
|
||||
|
||||
@@ -65,6 +65,8 @@ export const ManagementAuthorityConfig = forwardRef<ManagementAuthorityConfigHan
|
||||
const prefixSelector = (
|
||||
<Form.Item name="position" noStyle>
|
||||
<Select
|
||||
showSearch
|
||||
optionFilterProp="label"
|
||||
style={{ width: 90 }}
|
||||
options={[
|
||||
{ label: 'Header', value: 'Header' },
|
||||
@@ -127,6 +129,8 @@ export const ManagementAuthorityConfig = forwardRef<ManagementAuthorityConfigHan
|
||||
|
||||
<Form.Item<EditAuthFieldType> label={$t('鉴权类型')} name="driver" rules={[{ required: true }]}>
|
||||
<Select
|
||||
showSearch
|
||||
optionFilterProp="label"
|
||||
disabled={type === 'edit'}
|
||||
className="w-INPUT_NORMAL"
|
||||
options={[
|
||||
@@ -179,6 +183,8 @@ export const ManagementAuthorityConfig = forwardRef<ManagementAuthorityConfigHan
|
||||
rules={[{ required: true }]}
|
||||
>
|
||||
<Select
|
||||
showSearch
|
||||
optionFilterProp="label"
|
||||
className="w-INPUT_NORMAL"
|
||||
options={ALGORITHM_ITEM}
|
||||
onChange={(value) => {
|
||||
@@ -205,6 +211,8 @@ export const ManagementAuthorityConfig = forwardRef<ManagementAuthorityConfigHan
|
||||
|
||||
<Form.Item<EditAuthFieldType> label={$t('校验字段')} name={['config', 'claimsToVerify']}>
|
||||
<Select
|
||||
showSearch
|
||||
optionFilterProp="label"
|
||||
className="w-INPUT_NORMAL"
|
||||
mode="multiple"
|
||||
options={[
|
||||
|
||||
@@ -204,6 +204,8 @@ const ManagementConfig = forwardRef<ManagementConfigHandle, ManagementConfigProp
|
||||
{dataShowType === 'list' && (
|
||||
<Form.Item<ManagementConfigFieldType> label={$t('所属团队')} name="team" rules={[{ required: true }]}>
|
||||
<Select
|
||||
showSearch
|
||||
optionFilterProp="label"
|
||||
className="w-INPUT_NORMAL"
|
||||
disabled={type === 'edit'}
|
||||
placeholder={$t(PLACEHOLDER.input)}
|
||||
|
||||
Reference in New Issue
Block a user