diff --git a/frontend/package.json b/frontend/package.json index 7217f6e4..5998344c 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -43,7 +43,7 @@ "react-dom": "^18.2.0", "react-i18next": "^15.0.1", "react-joyride": "^2.8.2", - "react-router-dom": "^6.20.0", + "react-router-dom": "6.20.0", "swagger-ui-react": "^5.17.14", "tailwindcss": "^3.3.5", "uuid": "^9.0.1", diff --git a/frontend/packages/common/src/components/aoplatform/BasicLayout.tsx b/frontend/packages/common/src/components/aoplatform/BasicLayout.tsx index 19767e95..7cda49e0 100644 --- a/frontend/packages/common/src/components/aoplatform/BasicLayout.tsx +++ b/frontend/packages/common/src/components/aoplatform/BasicLayout.tsx @@ -241,6 +241,9 @@ function BasicLayout({ project = 'core' }: { project: string }) { headerTitleRender={() => (
navigator(mainPage)} /> + + +
)} logo={Logo} @@ -280,9 +283,9 @@ function BasicLayout({ project = 'core' }: { project: string }) { collapsedButtonRender={false} >
diff --git a/frontend/packages/common/src/components/aoplatform/InsidePage.tsx b/frontend/packages/common/src/components/aoplatform/InsidePage.tsx index 3260e886..40d55930 100644 --- a/frontend/packages/common/src/components/aoplatform/InsidePage.tsx +++ b/frontend/packages/common/src/components/aoplatform/InsidePage.tsx @@ -22,6 +22,7 @@ class InsidePageProps { headerClassName?: string = '' /** 整个页面滚动 */ scrollPage?: boolean = true + customPadding?: boolean customBtn?: ReactNode } @@ -41,6 +42,7 @@ const InsidePage: FC = ({ contentClassName = '', headerClassName = '', scrollPage = true, + customPadding = false, customBtn }) => { const navigate = useNavigate() @@ -57,7 +59,7 @@ const InsidePage: FC = ({ {!pageTitle && !description && !backUrl && !customBtn ? ( <> ) : ( -
+
{backUrl && (
+
+ + ) + } + + const getOllamaData = async () => { + const response = await fetchData>('model/local/source/ollama', { + method: 'GET' + }) + + if (response.code === STATUS_CODE.SUCCESS) { + setOllamaAddress(response.data?.config?.address || '') + } else { + message.error(response.msg || $t(RESPONSE_TIPS.error)) + } + } + const handleEdit = (record: ModelListData) => { modal.confirm({ title: $t('模型设置'), - content: , + content: ( + + ), onOk: () => { return EditLocalModelModalRef.current?.save().then((res) => { if (res === true) { @@ -243,7 +323,7 @@ const LocalModelList: React.FC = () => { { title: '', key: 'option', - btnNums: 4, + btnNums: 2, fixed: 'right', valueType: 'option', render: (_: React.ReactNode, entity: ModelListData) => [ @@ -283,7 +363,9 @@ const LocalModelList: React.FC = () => { } const modalInstance = modal.confirm({ title: $t('部署过程'), - content: , + content: ( + + ), footer: () => { return }, @@ -319,13 +401,14 @@ const LocalModelList: React.FC = () => { } }} > - {stateColumnMap[entity?.state as string]?.text || '-'} + {$t(stateColumnMap[entity?.state as string]?.text || '-')} ) }, { title: $t('Apis'), dataIndex: 'apiCount', + width: 100, render: (dom: React.ReactNode, record: ModelListData) => ( { { setSearchWord(e.target.value) pageListRef.current?.reload() }} + beforeNewBtn={ + [] + } showPagination={true} searchPlaceholder={$t('请输入名称搜索')} columns={columns} diff --git a/frontend/packages/core/src/pages/guide/AIModelGuide.tsx b/frontend/packages/core/src/pages/guide/AIModelGuide.tsx index 0ca41ad1..89fef2da 100644 --- a/frontend/packages/core/src/pages/guide/AIModelGuide.tsx +++ b/frontend/packages/core/src/pages/guide/AIModelGuide.tsx @@ -6,16 +6,18 @@ import { $t } from '@common/locales' import { Icon } from '@iconify/react/dist/iconify.js' import { App } from 'antd' import { Card } from 'antd' -import { useRef } from 'react' +import { useEffect, useRef, useState } from 'react' import { useNavigate } from 'react-router-dom' import AiSettingModalContent, { AiSettingModalContentHandle } from '../aiSetting/AiSettingModal' import { checkAccess } from '@common/utils/permission' import LocalAiDeploy, { LocalAiDeployHandle } from './LocalAiDeploy' import useDeployLocalModel from './deployModelUtil' import RestAIDeploy, { RestAIDeployHandle } from './RestAIDeploy' +import { BasicResponse, RESPONSE_TIPS, STATUS_CODE } from '@common/const/const' +import { useFetch } from '@common/hooks/http' export const AIModelGuide = () => { - const { modal } = App.useApp() + const { message, modal } = App.useApp() const entityData = useRef(null) const navigateTo = useNavigate() const { accessData } = useGlobalContext() @@ -23,6 +25,8 @@ export const AIModelGuide = () => { const localAiDeployRef = useRef() const restAiDeployRef = useRef() const { deployLocalModel } = useDeployLocalModel() + const { fetchData } = useFetch() + const [ollamaAddress, setOllamaAddress] = useState('') const dumpServerPage = () => { navigateTo('/service/list') @@ -105,10 +109,31 @@ export const AIModelGuide = () => { }) } + + const getOllamaData = async () => { + const response = await fetchData>('model/local/source/ollama', { + method: 'GET' + }) + + if (response.code === STATUS_CODE.SUCCESS) { + setOllamaAddress(response.data?.config?.address || '') + } else { + message.error(response.msg || $t(RESPONSE_TIPS.error)) + } + } + + useEffect(() => { + getOllamaData() + }, []) + /** * 本地部署 AI 并生成 API */ const localModelCardClick = async () => { + if (!ollamaAddress) { + navigateTo('/aisetting?status=unconfigure') + return + } const modalInstance = modal.confirm({ title: $t('部署本地模型'), content: { @@ -131,6 +156,10 @@ export const AIModelGuide = () => { } const deployDeepSeek = async (e: any) => { e.stopPropagation() + if (!ollamaAddress) { + navigateTo('/aisetting?status=unconfigure') + return + } await deployLocalModel({ modelID: 'deepseek-r1' }) @@ -141,13 +170,13 @@ export const AIModelGuide = () => { { imgSrc: restAPIPic, title: $t('添加 Rest 服务'), - description: $t('支持批量添加现有 API 文档以实现统一的外部访问。'), + description: $t('导入OpenAPI文档,将现有系统的API发布到APIPark。'), click: restCardClick }, { imgSrc: onlineAIPic, title: $t('添加在线 AI API'), - description: $t('快速调用 AI 模型的云服务 API,方便管理提示词和统一计费。'), + description: $t('添加公有云AI模型的 API Key,通过APIPark 统一调用公有云的AI模型。'), click: aiCardClick }, { @@ -164,7 +193,9 @@ export const AIModelGuide = () => { } ] return ( -
+ <> +

{$t('⚡您可快速通过以下方式开放API供大家使用:')}

+
{cardList.map((item, itemIndex) => ( { ))}
+ ) } diff --git a/frontend/packages/core/src/pages/guide/Guide.tsx b/frontend/packages/core/src/pages/guide/Guide.tsx index deeff7fe..72801b99 100644 --- a/frontend/packages/core/src/pages/guide/Guide.tsx +++ b/frontend/packages/core/src/pages/guide/Guide.tsx @@ -151,12 +151,13 @@ export default function Guide() { description={

+ 🦄 APIPark {$t( - '你能通过 APIPark 快速在企业内部构建 API 开放门户/市场,享受极致的转发性能、API 可观测、服务治理、多租户管理、订阅审核流程等诸多好处。' + '是开源的一站式 AI 网关与 API 门户,可快速接入 OpenAI/DeepSeek 等各类 AI 模型,通过统一请求格式避免模型切换对业务造成影响,提供企业级 API 安全防护(鉴权/限流/敏感词过滤)与实时用量监控,支持团队内 API 共享协作,管理接口订阅授权并保证您的API安全。' )}

- {$t('如果你喜欢我们的产品,欢迎给我们 Star 或提供产品反馈意见。')} + {$t('✨ 欢迎在 Github 为我们 Star 或提供产品反馈意见。')} {$t('点击这里')} @@ -184,7 +185,9 @@ export default function Guide() { } showBorder={false} scrollPage={false} - contentClassName=" w-full pr-PAGE_INSIDE_X pb-PAGE_INSIDE_B" + customPadding={true} + headerClassName="pt-[30px] pl-[40px]" + contentClassName=" w-full pr-PAGE_INSIDE_X pb-PAGE_INSIDE_B pl-[40px]" >

diff --git a/frontend/packages/core/src/pages/guide/LocalAiDeploy.tsx b/frontend/packages/core/src/pages/guide/LocalAiDeploy.tsx index 64d3fc3c..e131687b 100644 --- a/frontend/packages/core/src/pages/guide/LocalAiDeploy.tsx +++ b/frontend/packages/core/src/pages/guide/LocalAiDeploy.tsx @@ -114,7 +114,7 @@ const LocalAiDeploy = forwardRef((props: any, ref: any name="partitionInsideCert" autoComplete="off" > - + + + {!onEdit && ( - - label={$t('服务类型')} - name="serviceKind" - rules={[{ required: true }]} - > + label={$t('服务类型')} name="serviceKind" rules={[{ required: true }]}> { + onChange={(e) => { setShowAI(e.target.value === 'ai') }} > @@ -398,39 +438,40 @@ const SystemConfig = forwardRef((_, ref) => { )} {showAI && ( - - label={$t('默认 AI 供应商')} - name="provider" - rules={[{ required: true }]} - extra={ - serviceId - ? $t('创建 API 时会默认选择该供应商,修改默认供应商不会影响现有 API') - : '' - } - > - {providerOptionList && providerOptionList.length > 0 ? ( - - ) : ( -

- {$t('未配置任何 AI 模型供应商,')} - setAiConfigFlushed(false)}> - {$t('立即配置')} - -

- )} - + <> + + label={$t('默认 AI 供应商')} + name="provider" + rules={[{ required: true }]} + extra={serviceId ? $t('创建 API 时会默认选择该供应商,修改默认供应商不会影响现有 API') : ''} + > + {providerOptionList && providerOptionList.length > 0 ? ( + + ) : ( +

+ {$t('未配置任何 AI 模型供应商,')} + setAiConfigFlushed(false)}> + {$t('立即配置')} + +

+ )} + + label={$t('默认模型')} name="model" rules={[{ required: true }]}> + + + )} - label={$t('API 调用前缀')} name="prefix" - extra={$t( - '作为服务内所有API的前缀,比如host/{service_name}/{api_path},影响较大,谨慎修改' - )} + extra={$t('作为服务内所有API的前缀,比如host/{service_name}/{api_path},影响较大,谨慎修改')} rules={[ { required: true, whitespace: true }, { @@ -438,18 +479,10 @@ const SystemConfig = forwardRef((_, ref) => { } ]} > - + {!onEdit && ( - - label={$t('所属团队')} - name="team" - rules={[{ required: true }]} - > + label={$t('所属团队')} name="team" rules={[{ required: true }]}> + - OR + {$t('或')}