Merge branch 'feature/1.7-cxx' into 'main'

feature/1.7-MCP

See merge request apipark/APIPark!318
This commit is contained in:
lichunxian
2025-04-15 18:58:14 +08:00
32 changed files with 378 additions and 154 deletions
@@ -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 = [
+5
View File
@@ -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}
+5
View File
@@ -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)}