diff --git a/frontend/packages/common/src/assets/localAI.svg b/frontend/packages/common/src/assets/localAI.svg
new file mode 100644
index 00000000..ea2ac931
--- /dev/null
+++ b/frontend/packages/common/src/assets/localAI.svg
@@ -0,0 +1,3 @@
+
diff --git a/frontend/packages/common/src/assets/onlineAI.svg b/frontend/packages/common/src/assets/onlineAI.svg
new file mode 100644
index 00000000..008a606f
--- /dev/null
+++ b/frontend/packages/common/src/assets/onlineAI.svg
@@ -0,0 +1,3 @@
+
diff --git a/frontend/packages/common/src/assets/restAPI.svg b/frontend/packages/common/src/assets/restAPI.svg
new file mode 100644
index 00000000..680b98f3
--- /dev/null
+++ b/frontend/packages/common/src/assets/restAPI.svg
@@ -0,0 +1,14 @@
+
diff --git a/frontend/packages/common/src/components/aoplatform/BasicLayout.tsx b/frontend/packages/common/src/components/aoplatform/BasicLayout.tsx
index 931bf7f0..471d78b5 100644
--- a/frontend/packages/common/src/components/aoplatform/BasicLayout.tsx
+++ b/frontend/packages/common/src/components/aoplatform/BasicLayout.tsx
@@ -182,7 +182,7 @@ function BasicLayout({ project = 'core' }: { project: string }) {
,
...((pluginSlotHub.getSlot('basicLayoutAfterBtns') as unknown[]) || [])
]
- }, [pluginSlotHub.getSlot('basicLayoutAfterBtns')])
+ }, [state.language, pluginSlotHub.getSlot('basicLayoutAfterBtns')])
return (
[] = [
dataIndex: ['team', 'name'],
ellipsis: true
},
+ {
+ title: '状态',
+ width: 140,
+ dataIndex: 'update_time',
+ // dataIndex: 'state',
+ ellipsis: true
+ },
{
title: 'API 数量',
dataIndex: 'apiNum',
diff --git a/frontend/packages/core/src/pages/aiSetting/AiSettingModal.tsx b/frontend/packages/core/src/pages/aiSetting/AiSettingModal.tsx
index a81a4c7d..f0df517d 100644
--- a/frontend/packages/core/src/pages/aiSetting/AiSettingModal.tsx
+++ b/frontend/packages/core/src/pages/aiSetting/AiSettingModal.tsx
@@ -4,31 +4,50 @@ import { BasicResponse, PLACEHOLDER, RESPONSE_TIPS, STATUS_CODE } from '@common/
import { useFetch } from '@common/hooks/http'
import { $t } from '@common/locales'
import { App, Form, InputNumber, Select, Switch, Tag, Tooltip } from 'antd'
-import { forwardRef, useEffect, useImperativeHandle, useState } from 'react'
-import { AiProviderLlmsItems, ModelDetailData } from './types'
+import { forwardRef, useEffect, useImperativeHandle, useRef, useState } from 'react'
+import { AiProviderLlmsItems, ModelDetailData, AiSettingListItem } from './types'
+import { MemberItem, SimpleTeamItem } from '@common/const/type'
+import { useGlobalContext } from '@common/contexts/GlobalStateContext'
export type AiSettingModalContentProps = {
entity: ModelDetailData & { defaultLlm: string }
readOnly: boolean
+ modelMode?: 'auto' | 'manual'
+ /** 如果是手动选择 AI 模型,那么需要更新 footer 底部的内容,所以需要这个方法去更新外部的 footer */
+ updateEntityData: (entity: ModelDetailData & { defaultLlm: string }) => void
}
export type AiSettingModalContentHandle = {
save: () => Promise
+ deployAIServer: () => Promise
}
const AiSettingModalContent = forwardRef((props, ref) => {
const [form] = Form.useForm()
const { message } = App.useApp()
- const { entity, readOnly } = props
+ const { entity, readOnly, modelMode = 'auto', updateEntityData } = props
const { fetchData } = useFetch()
const [llmList, setLlmList] = useState()
const [loading, setLoading] = useState(false)
- const [enableState, setEnableState] = useState(entity.status === 'enabled')
- const getLlmList = () => {
+ // AI 模型配置
+ const [localEntity, setLocalEntity] = useState(entity)
+ const [teamList, setTeamList] = useState([])
+ // AI 模型提供商列表
+ const modelProviderListRef = useRef([])
+ // 模型模式加载
+ const [modelModeLoading, setModelModeLoading] = useState(false)
+ const [enableState, setEnableState] = useState(localEntity?.status === 'enabled')
+ const { checkPermission } = useGlobalContext()
+
+ /**
+ * 获取 llm 列表
+ * @param id
+ */
+ const getLlmList = (id?: string) => {
setLoading(true)
fetchData>(`ai/provider/llms`, {
method: 'GET',
- eoParams: { provider: entity.id }
+ eoParams: { provider: id || localEntity.id }
})
.then((response) => {
const { code, data, msg } = response
@@ -43,25 +62,159 @@ const AiSettingModalContent = forwardRef {
- getLlmList()
+ /**
+ * 获取团队选项列表
+ * @returns
+ */
+ const getTeamOptionList = async (): any[] => {
+ const response = await fetchData>(
+ !checkPermission('system.workspace.team.view_all') ? 'simple/teams/mine' : 'simple/teams',
+ { method: 'GET', eoTransformKeys: [] }
+ )
+ const { code, data, msg } = response
+ if (code === STATUS_CODE.SUCCESS) {
+ const teamOptionList = data.teams?.map((x: MemberItem) => {
+ return { ...x, label: x.name, value: x.id }
+ })
+ setTeamList(teamOptionList)
+ if (form.getFieldValue('team') === undefined && data.teams?.length) {
+ form.setFieldValue('team', data.teams[0].id)
+ }
+ } else {
+ message.error(msg || $t(RESPONSE_TIPS.error))
+ return []
+ }
+ }
+
+ /**
+ * 获取未配置模型提供者列表
+ */
+ const getModelProviderList = () => {
+ setModelModeLoading(true)
+ fetchData>(`ai/providers/unconfigured`, {
+ method: 'GET',
+ eoTransformKeys: ['default_llm', 'default_llm_logo']
+ })
+ .then((response) => {
+ const { code, data, msg } = response
+ if (code === STATUS_CODE.SUCCESS) {
+ const providers = data.providers || []
+ modelProviderListRef.current = providers
+ if (providers.length) {
+ const id = providers[0].id
+ form.setFieldValue('modelMode', id)
+ getModelConfig(id)
+ }
+ } else {
+ message.error(msg || $t(RESPONSE_TIPS.error))
+ }
+ })
+ .finally(() => {
+ setModelModeLoading(false)
+ })
+ }
+
+ /**
+ * 获取模型配置
+ * @param id
+ */
+ const getModelConfig = (id: string) => {
+ getLlmList(id)
+ fetchData>(`ai/provider/config`, {
+ method: 'GET',
+ eoParams: { provider: id },
+ eoTransformKeys: ['get_apikey_url']
+ })
+ .then((response) => {
+ const { code, data, msg } = response
+ if (code === STATUS_CODE.SUCCESS) {
+ const modelEntity = {
+ ...data.provider,
+ defaultLlm: modelProviderListRef.current.find((x) => x.id === id)?.defaultLlm
+ }
+ setLocalEntity(modelEntity)
+ setFormFieldsValue(modelEntity)
+ updateEntityData(modelEntity)
+ } else {
+ message.error(msg || $t(RESPONSE_TIPS.error))
+ }
+ })
+ .finally(() => {
+ setModelModeLoading(false)
+ })
+ }
+
+ /**
+ * 设置表单字段值
+ * @param fieldsValue
+ */
+ const setFormFieldsValue = (fieldsValue: any) => {
try {
form.setFieldsValue({
- defaultLlm: entity.defaultLlm,
- config: entity!.config ? JSON.stringify(JSON.parse(entity!.config), null, 2) : '',
- priority: entity.priority || 1,
- enable: entity.status === 'enabled'
+ defaultLlm: fieldsValue.defaultLlm,
+ config: fieldsValue!.config ? JSON.stringify(JSON.parse(fieldsValue!.config), null, 2) : '',
+ priority: fieldsValue.priority || 1,
+ enable: fieldsValue.status === 'enabled'
})
} catch (e) {
form.setFieldsValue({
- defaultLlm: entity.defaultLlm,
+ defaultLlm: localEntity.defaultLlm,
config: '',
priority: 1,
enable: true
})
}
+ }
+ useEffect(() => {
+ // 如果是直接在 AI 模型配置,则获取默认模型列表和团队列表
+ if (modelMode === 'auto') {
+ getLlmList()
+ setFormFieldsValue(localEntity)
+ } else {
+ getModelProviderList()
+ getTeamOptionList()
+ }
}, [])
+ /**
+ * 部署 AI 服务
+ */
+ const deployAIServer: () => Promise = () => {
+ return new Promise((resolve, reject) => {
+ form
+ .validateFields()
+ .then((value) => {
+ const finalValue = {
+ config: value.config,
+ model: value.defaultLlm,
+ team: value.team,
+ provider: localEntity?.id
+ }
+ console.log(finalValue)
+ fetchData>('quick/service/ai', {
+ method: 'POST',
+ eoBody: finalValue
+ })
+ .then((response) => {
+ const { code, msg } = response
+ if (code === STATUS_CODE.SUCCESS) {
+ message.success(msg || $t(RESPONSE_TIPS.success))
+ resolve(true)
+ } else {
+ message.error(msg || $t(RESPONSE_TIPS.error))
+ reject(msg || $t(RESPONSE_TIPS.error))
+ }
+ })
+ .catch((errorInfo) => reject(errorInfo))
+ })
+ .catch((errorInfo) => reject(errorInfo))
+ })
+ }
+
+ /**
+ * 保存
+ * @returns
+ */
const save: () => Promise = () => {
return new Promise((resolve, reject) => {
form
@@ -74,7 +227,7 @@ const AiSettingModalContent = forwardRef>('ai/provider/config', {
method: 'PUT',
- eoParams: { provider: entity?.id },
+ eoParams: { provider: localEntity?.id },
eoBody: finalValue,
eoTransformKeys: ['defaultLlm']
// eoApiPrefix: 'http://uat.apikit.com:11204/mockApi/aoplatform/api/v1/'
@@ -103,7 +256,8 @@ const AiSettingModalContent = forwardRef ({
- save
+ save,
+ deployAIServer
}))
return (
@@ -117,6 +271,26 @@ const AiSettingModalContent = forwardRef
+ {modelMode === 'manual' && (
+ label={$t('模型来源')} name="modelMode" rules={[{ required: true }]}>
+
+
+ )}
label={$t('默认模型')} name="defaultLlm" rules={[{ required: true }]}>
-
-
- label={
-
- {$t('负载优先级')}
-
-
-
-
- }
- name="priority"
- rules={[
- { required: true },
- {
- validator: async (_, value) => {
- if (value <= 0) {
- throw new Error($t('优先级必须大于 0'))
- }
- return Promise.resolve()
- }
+ {modelMode === 'auto' && (
+
+ label={
+
+ {$t('负载优先级')}
+
+
+
+
}
- ]}
- initialValue={1}
- >
-
-
+ name="priority"
+ rules={[
+ { required: true },
+ {
+ validator: async (_, value) => {
+ if (value <= 0) {
+ throw new Error($t('优先级必须大于 0'))
+ }
+ return Promise.resolve()
+ }
+ }
+ ]}
+ initialValue={1}
+ >
+
+
+ )}
+ {modelMode === 'manual' && (
+
+
+
+ )}
label={$t('API Key(默认 Key)')} name="config">
- {entity.configured && (
+ {localEntity?.configured && (
{$t('当前调用状态:')}
- {entity.status === 'enabled' && {$t('正常')}}
- {entity.status === 'disabled' && {$t('停用')}}
- {entity.status === 'abnormal' && {$t('异常')}}
+ {localEntity.status === 'enabled' && {$t('正常')}}
+ {localEntity.status === 'disabled' && {$t('停用')}}
+ {localEntity.status === 'abnormal' && {$t('异常')}}
- {(entity.status === 'enabled' && !enableState) || (entity.status !== 'enabled' && enableState) ? (
+ {(localEntity.status === 'enabled' && !enableState) || (localEntity.status !== 'enabled' && enableState) ? (
* {getTooltipText(enableState)}
) : null}
diff --git a/frontend/packages/core/src/pages/aiSetting/types.ts b/frontend/packages/core/src/pages/aiSetting/types.ts
index 6ba2ea7a..6a8a4598 100644
--- a/frontend/packages/core/src/pages/aiSetting/types.ts
+++ b/frontend/packages/core/src/pages/aiSetting/types.ts
@@ -12,6 +12,7 @@ export interface ModelListData {
name: string
logo: string
defaultLlm: string
+ modelMode?: string
status: ModelStatus
api_count: number
key_count: number
diff --git a/frontend/packages/core/src/pages/guide/AIModelGuide.tsx b/frontend/packages/core/src/pages/guide/AIModelGuide.tsx
new file mode 100644
index 00000000..53c2288b
--- /dev/null
+++ b/frontend/packages/core/src/pages/guide/AIModelGuide.tsx
@@ -0,0 +1,427 @@
+import restAPIPic from '@common/assets/restAPI.svg'
+import onlineAIPic from '@common/assets/onlineAI.svg'
+import localAIPic from '@common/assets/localAI.svg'
+import { useGlobalContext } from '@common/contexts/GlobalStateContext'
+import { $t } from '@common/locales'
+import { Icon } from '@iconify/react/dist/iconify.js'
+import { App, Upload, UploadProps, Form, message, Select } from 'antd'
+import { Card } from 'antd'
+import { useRef, useState } from 'react'
+import { useNavigate } from 'react-router-dom'
+import WithPermission from '@common/components/aoplatform/WithPermission'
+import { BasicResponse, PLACEHOLDER, RESPONSE_TIPS, STATUS_CODE } from '@common/const/const'
+import { useFetch } from '@common/hooks/http'
+import { LocalModelItem, MemberItem, SimpleTeamItem } from '@common/const/type'
+import AiSettingModalContent, { AiSettingModalContentHandle } from '../aiSetting/AiSettingModal'
+import { checkAccess } from '@common/utils/permission'
+const { Dragger } = Upload
+export const AIModelGuide = () => {
+ const { modal } = App.useApp()
+ const [, forceUpdate] = useState(null)
+ const [form] = Form.useForm()
+ const entityData = useRef(null)
+ const { fetchData } = useFetch()
+ const navigateTo = useNavigate()
+ const { checkPermission, accessData } = useGlobalContext()
+ const modalRef = useRef()
+
+ /**
+ * 获取 team 选项列表
+ * @returns
+ */
+ const getTeamOptionList = async (): any[] => {
+ const response = await fetchData>(
+ !checkPermission('system.workspace.team.view_all') ? 'simple/teams/mine' : 'simple/teams',
+ { method: 'GET', eoTransformKeys: [] }
+ )
+ const { code, data, msg } = response
+ if (code === STATUS_CODE.SUCCESS) {
+ const teamOptionList = data.teams?.map((x: MemberItem) => {
+ return { ...x, label: x.name, value: x.id }
+ })
+ if (form.getFieldValue('team') === undefined && data.teams?.length) {
+ form.setFieldValue('team', data.teams[0].id)
+ }
+ return teamOptionList
+ } else {
+ message.error(msg || $t(RESPONSE_TIPS.error))
+ return []
+ }
+ }
+
+ /**
+ * 部署 rest 服务
+ * @param file
+ * @returns
+ */
+ const deployRestServer = async (file: File) => {
+ return new Promise((resolve, reject) => {
+ const formData = new FormData()
+ formData.append('file', file)
+ formData.append('type', file.type)
+ formData.append('team', form.getFieldValue('team'))
+ fetchData>('quick/service/rest', {
+ method: 'POST',
+ body: formData
+ }).then((response) => {
+ const { code, msg } = response
+ if (code === STATUS_CODE.SUCCESS) {
+ message.success(msg || $t(RESPONSE_TIPS.success))
+ resolve(true)
+ } else {
+ message.error(msg || $t(RESPONSE_TIPS.error))
+ reject(false)
+ }
+ })
+ })
+ }
+
+ const deployPopularModel = async (id: string, modalInstance: any) => {
+ await deployLocalModel({
+ modelID: id,
+ team: form.getFieldValue('team')
+ })
+ modalInstance.destroy()
+ navigateTo(`/service/list`)
+ }
+
+ /**
+ * 部署本地模型
+ * @param value
+ * @returns
+ */
+ const deployLocalModel = (value: { modelID: string; team?: number }) => {
+ return new Promise((resolve, reject) => {
+ const finalValue = {
+ model: value.modelID,
+ team: value?.team
+ }
+ console.log(finalValue)
+ fetchData>('model/local/deploy', {
+ method: 'POST',
+ eoBody: finalValue
+ })
+ .then((response) => {
+ const { code, msg } = response
+ if (code === STATUS_CODE.SUCCESS) {
+ message.success(msg || $t(RESPONSE_TIPS.success))
+ resolve(true)
+ } else {
+ message.error(msg || $t(RESPONSE_TIPS.error))
+ reject(false)
+ }
+ })
+ .catch((errorInfo) => reject(errorInfo))
+ })
+ }
+
+ /**
+ * 获取本地模型列表
+ * @returns 本地模型列表
+ */
+ const getLocalModelList = async (): any[] => {
+ const response = await fetchData>(
+ 'http://uat.apikit.com:11204/mockApi/aoplatform/api/v1/model/local/can_deploy',
+ // 'model/local/can_deploy'
+ { method: 'GET', custom: true, eoTransformKeys: ['is_popular'] }
+ )
+ // TODO_数据模拟
+ if (response.ok) {
+ const datas = await response.json()
+ const { code, data, msg } = datas
+ if (code === STATUS_CODE.SUCCESS) {
+ const modelList = data.models?.map((x: LocalModelItem) => {
+ return { ...x, label: x.name, value: x.id }
+ })
+ return modelList
+ } else {
+ message.error(msg || $t(RESPONSE_TIPS.error))
+ return []
+ }
+ } else {
+ console.error('HTTP error', response.status)
+ }
+ // const { code, data, msg } = response
+ // if (code === STATUS_CODE.SUCCESS) {
+ // const modelList = data.models?.map((x: LocalModelItem) => {
+ // return { ...x, label: x.name, value: x.id }
+ // })
+ // console.log('modelList===', modelList);
+
+ // return modelList
+ // } else {
+ // message.error(msg || $t(RESPONSE_TIPS.error))
+ // return []
+ // }
+ }
+
+ /**
+ * rest 服务卡片点击事件
+ */
+ const restCardClick = async () => {
+ form.resetFields()
+ const teamList = await getTeamOptionList()
+ const props: UploadProps = {
+ name: 'file',
+ multiple: false,
+ maxCount: 1,
+ beforeUpload: (file) => {
+ form.setFieldsValue({ key: file })
+ forceUpdate({})
+ return false
+ }
+ }
+
+ modal.confirm({
+ title: $t('添加 Rest 服务'),
+ content: (
+
+
+
+
+
+
+ {$t('选择 OpenAPI 文件 (.json / .yaml)')}
+
+
+
+
+
+
+
+ ),
+ onOk: () => {
+ return new Promise((resolve, reject) => {
+ form
+ .validateFields()
+ .then(async (value) => {
+ await deployRestServer(value.key.file)
+ resolve(true)
+ navigateTo(`/service/list`)
+ })
+ .catch((errorInfo) => reject(errorInfo))
+ })
+ },
+ width: 600,
+ okText: $t('确认'),
+ cancelText: $t('取消'),
+ closable: true,
+ icon: <>>
+ })
+ }
+
+ /**
+ * AI 模型配置弹窗
+ */
+ const aiCardClick = () => {
+ // 更新弹窗
+ const updateEntityData = (data: any) => {
+ entityData.current = data
+ // 更新弹窗
+ modalInstance.update({})
+ }
+ const modalInstance = modal.confirm({
+ title: $t('模型配置'),
+ content: (
+
+ ),
+ onOk: () => {
+ return modalRef.current?.deployAIServer().then((res) => {
+ if (res === true) {
+ navigateTo(`/service/list`)
+ }
+ })
+ },
+ width: 600,
+ okText: $t('确认'),
+ footer: (_, { OkBtn, CancelBtn }) => {
+ return (
+
+ )
+ },
+ cancelText: $t('取消'),
+ closable: true,
+ icon: <>>
+ })
+ }
+
+ /**
+ * 本地部署 AI 并生成 API
+ */
+ const localModelCardClick = async () => {
+ form.resetFields()
+ const teamList = await getTeamOptionList()
+ const modelList = await getLocalModelList()
+ const modalInstance = modal.confirm({
+ title: $t('部署 AI 模型'),
+ content: (
+
+
+
+
+
+ {$t('热点模型')}
+
+
+ {modelList.length &&
+ modelList
+ .filter((item) => item.is_popular)
+ .map((item) => (
+ {
+ deployPopularModel(item.id, modalInstance)
+ }}
+ >
+ {item.name}
+
+ ))}
+
+
+
+
+
+
+
+ ),
+ onOk: () => {
+ return new Promise((resolve, reject) => {
+ form
+ .validateFields()
+ .then(async (value) => {
+ await deployLocalModel(value)
+ resolve(true)
+ navigateTo(`/service/list`)
+ })
+ .catch((errorInfo) => reject(errorInfo))
+ })
+ },
+ width: 600,
+ okText: $t('确认'),
+ cancelText: $t('取消'),
+ closable: true,
+ icon: <>>
+ })
+ }
+ const deployDeepSeek = (e: any) => {
+ e.stopPropagation()
+ deployLocalModel({
+ modelID: 'deepseek-r1'
+ })
+ }
+
+ const cardList = [
+ {
+ imgSrc: restAPIPic,
+ title: $t('添加 Rest 服务'),
+ description: $t('支持批量添加现有 API 文档以实现统一的外部访问。'),
+ click: restCardClick
+ },
+ {
+ imgSrc: onlineAIPic,
+ title: $t('添加在线 AI API'),
+ description: $t('快速调用 AI 模型的云服务 API,方便管理提示词和统一计费。'),
+ click: aiCardClick
+ },
+ {
+ imgSrc: localAIPic,
+ title: $t('本地部署 AI 并生成 API'),
+ description: $t('快速在本地部署开源模型并自动生成 API。'),
+ click: localModelCardClick,
+ bottomRender: (
+
+
+ {$t('部署')} Deepseek-R1
+
+ )
+ }
+ ]
+ return (
+
+ {cardList.map((item, itemIndex) => (
+
+
+ {item.title}
+ {item.description}
+ {item.bottomRender ? item.bottomRender : null}
+
+ ))}
+
+ )
+}
diff --git a/frontend/packages/core/src/pages/guide/Guide.tsx b/frontend/packages/core/src/pages/guide/Guide.tsx
index 9e79f2e8..ed8a085c 100644
--- a/frontend/packages/core/src/pages/guide/Guide.tsx
+++ b/frontend/packages/core/src/pages/guide/Guide.tsx
@@ -1,232 +1,321 @@
-import InsidePage from "@common/components/aoplatform/InsidePage"
-import { useGlobalContext } from "@common/contexts/GlobalStateContext"
-import { $t } from "@common/locales"
-import { Icon } from "@iconify/react/dist/iconify.js"
-import { Button, Card, Collapse } from "antd"
-import { Dispatch, SetStateAction, useEffect, useState } from "react"
-import { useLocation, useNavigate } from "react-router-dom"
+import InsidePage from '@common/components/aoplatform/InsidePage'
+import { useGlobalContext } from '@common/contexts/GlobalStateContext'
+import { $t } from '@common/locales'
+import { Icon } from '@iconify/react/dist/iconify.js'
+import { Button, Card, Collapse } from 'antd'
+import { Dispatch, SetStateAction, useEffect, useState } from 'react'
+import { useLocation, useNavigate } from 'react-router-dom'
+import { AIModelGuide } from './AIModelGuide'
-export default function Guide(){
- const [showGuide, setShowGuide] = useState(localStorage.getItem('showGuide') !== 'false' )
- const [showAdvancedGuide, setShowAdvancedGuide] = useState(localStorage.getItem('showAdvancedGuide') !== 'false' )
- const [, forceUpdate] = useState(null);
- const {state} = useGlobalContext()
- const location = useLocation()
- const currentUrl = location.pathname
- const navigator = useNavigate()
- const guideSections = [
+export default function Guide() {
+ const [showGuide, setShowGuide] = useState(localStorage.getItem('showGuide') !== 'false')
+ const [showAdvancedGuide, setShowAdvancedGuide] = useState(localStorage.getItem('showAdvancedGuide') !== 'false')
+ const [, forceUpdate] = useState(null)
+ const { state } = useGlobalContext()
+ const location = useLocation()
+ const currentUrl = location.pathname
+ const navigator = useNavigate()
+ const guideSections = [
+ {
+ title: $t('快速接入 AI'),
+ items: [
{
- title: $t('快速接入 AI'),
- items: [
- {
- title: $t("配置你的 AI 模型"),
- description: $t('通过 APIPark 快速接入各种 AI 模型,使用统一的格式来调用API,并且可以随意切换模型。'),
- link: 'https://docs.apipark.com/docs/system_setting/ai_model_providers'
- },
- {
- title: $t("创建 AI 服务和 API"),
- description: $t('创建 AI 类型的服务,并且你可以将 Prompt 提示词设置为一个 API,简化使用 AI 的流程。'),
- link: 'https://docs.apipark.com/docs/services/ai_services'
- },
- {
- title: $t("创建调用 Token"),
- description: $t('为了安全地调用 API,你需要创建一个消费者以及Token。'),
- link: 'https://docs.apipark.com/docs/consumers'
- },
- {
- title: $t("调用"),
- description: $t('现在你可以通过 Token 来调用这些 API。'),
- link: 'https://docs.apipark.com/docs/call_api'
- }
- ]
+ title: $t('配置你的 AI 模型'),
+ description: $t('通过 APIPark 快速接入各种 AI 模型,使用统一的格式来调用API,并且可以随意切换模型。'),
+ link: 'https://docs.apipark.com/docs/system_setting/ai_model_providers'
},
{
- title: $t('快速接入 REST API'),
- items: [
- {
- title: $t("创建 REST 服务和 API"),
- description: $t('创建 AI 类型的服务,并且你可以将 Prompt 提示词设置为一个 API,简化使用 AI 的流程。'),
- link: 'https://docs.apipark.com/docs/services/rest_services'
- },
- {
- title: $t("创建调用 Token"),
- description: $t('为了安全地调用 API,你需要创建一个消费者以及Token。'),
- link: 'https://docs.apipark.com/docs/consumers'
- },
- {
- title: $t("调用"),
- description: $t('现在你可以通过 Token 来调用这些 API。'),
- link: 'https://docs.apipark.com/docs/call_api'
- }
- ]
+ title: $t('创建 AI 服务和 API'),
+ description: $t('创建 AI 类型的服务,并且你可以将 Prompt 提示词设置为一个 API,简化使用 AI 的流程。'),
+ link: 'https://docs.apipark.com/docs/services/ai_services'
},
{
- title: $t('仪表盘'),
- items: [
- {
- title: $t("统计 API 调用情况"),
- description: $t('仪表盘中提供了多种统计图表,帮助我们了解 API 的运行情况。'),
- link: 'https://docs.apipark.com/docs/analysis'
- }
- ]
+ title: $t('创建调用 Token'),
+ description: $t('为了安全地调用 API,你需要创建一个消费者以及Token。'),
+ link: 'https://docs.apipark.com/docs/consumers'
+ },
+ {
+ title: $t('调用'),
+ description: $t('现在你可以通过 Token 来调用这些 API。'),
+ link: 'https://docs.apipark.com/docs/call_api'
}
- ];
- const advanceGuideSections = [
+ ]
+ },
+ {
+ title: $t('快速接入 REST API'),
+ items: [
{
- title: $t('核心功能'),
- items: [
- {
- title: $t("账号与角色"),
- description: $t('邀请你的团队成员加入 APIPark,共同管理和调用 API。'),
- link: 'https://docs.apipark.com/docs/system_setting/account_role'
- },
- {
- title: $t("团队"),
- description: $t('团队中包含了人员、消费者和服务,不同团队之间的消费者和服务数据是隔离的,可用于管理企业内部不同的部门/项目组/团队。'),
- link: 'https://docs.apipark.com/docs/teams'
- },
- {
- title: $t("服务"),
- description: $t('服务内包含一组 API,并且可以发布到 API 市场被其他团队使用。'),
- link: 'https://docs.apipark.com/docs/category/-%E6%9C%8D%E5%8A%A1'
- }
- ]
+ title: $t('创建 REST 服务和 API'),
+ description: $t('创建 AI 类型的服务,并且你可以将 Prompt 提示词设置为一个 API,简化使用 AI 的流程。'),
+ link: 'https://docs.apipark.com/docs/services/rest_services'
},
{
- title: $t('权限管理'),
- items: [
- {
- title: $t("订阅服务"),
- description: $t('如果需要调用某个服务的 API,需要先订阅该服务,并且等待提供服务的团队审核后才可发起 API 请求。'),
- link: 'https://docs.apipark.com/docs/developer_portal'
- },
- {
- title: $t("审核订阅申请"),
- description: $t('提供服务的团队可以审核来自其他团队的订阅申请,审核通过后的消费者才可发起 API 请求。'),
- link: 'https://docs.apipark.com/docs/services/review_consumers'
- }
- ]
+ title: $t('创建调用 Token'),
+ description: $t('为了安全地调用 API,你需要创建一个消费者以及Token。'),
+ link: 'https://docs.apipark.com/docs/consumers'
},
{
- title: $t('集成'),
- items: [
- {
- title: $t("日志"),
- description: $t('APIPark 提供详尽的 API 调用日志,帮助企业监控、分析和审计 API 的运行状况。'),
- link: 'https://docs.apipark.com/docs/system_setting/log/'
- }
- ]
+ title: $t('调用'),
+ description: $t('现在你可以通过 Token 来调用这些 API。'),
+ link: 'https://docs.apipark.com/docs/call_api'
}
- ];
- useEffect(()=>{
- localStorage.setItem('showGuide', showGuide.toString())
- },[showGuide])
- useEffect(()=>{
- localStorage.setItem('showAdvancedGuide', showAdvancedGuide.toString())
- },[showAdvancedGuide])
-
- useEffect(()=>{
- if(currentUrl === '/guide'){
- setTimeout(()=>{
- navigator('/guide/page')
- },0)
+ ]
+ },
+ {
+ title: $t('仪表盘'),
+ items: [
+ {
+ title: $t('统计 API 调用情况'),
+ description: $t('仪表盘中提供了多种统计图表,帮助我们了解 API 的运行情况。'),
+ link: 'https://docs.apipark.com/docs/analysis'
}
- },[])
- useEffect(()=>{forceUpdate({})},[state.language])
- return (
-
- 👋
- {$t('Hello!欢迎使用 APIPark')}
-
- }
- description={
-
{$t("你能通过 APIPark 快速在企业内部构建 API 开放门户/市场,享受极致的转发性能、API 可观测、服务治理、多租户管理、订阅审核流程等诸多好处。")}
-
{$t("如果你喜欢我们的产品,欢迎给我们 Star 或提供产品反馈意见。")}
-
}
- showBorder={false}
- scrollPage={false}
- contentClassName=" w-full pr-PAGE_INSIDE_X pb-PAGE_INSIDE_B"
- >
-
- {showGuide &&
-
-
- 🚀{`${$t('快速入门')}`}
- {$t("我们提供了一些任务来帮你快速了解 APIPark")}
,
- children: }]}
- />}
- {showAdvancedGuide &&
-
-
- 🏍️{`${$t('进阶教程')}`}
- {$t("了解 APIPark 如何更好地管理 API 和 AI")}
,
- children: }]}
- />}
-
-
- )
+ ]
+ }
+ ]
+ const advanceGuideSections = [
+ {
+ title: $t('核心功能'),
+ items: [
+ {
+ title: $t('账号与角色'),
+ description: $t('邀请你的团队成员加入 APIPark,共同管理和调用 API。'),
+ link: 'https://docs.apipark.com/docs/system_setting/account_role'
+ },
+ {
+ title: $t('团队'),
+ description: $t(
+ '团队中包含了人员、消费者和服务,不同团队之间的消费者和服务数据是隔离的,可用于管理企业内部不同的部门/项目组/团队。'
+ ),
+ link: 'https://docs.apipark.com/docs/teams'
+ },
+ {
+ title: $t('服务'),
+ description: $t('服务内包含一组 API,并且可以发布到 API 市场被其他团队使用。'),
+ link: 'https://docs.apipark.com/docs/category/-%E6%9C%8D%E5%8A%A1'
+ }
+ ]
+ },
+ {
+ title: $t('权限管理'),
+ items: [
+ {
+ title: $t('订阅服务'),
+ description: $t(
+ '如果需要调用某个服务的 API,需要先订阅该服务,并且等待提供服务的团队审核后才可发起 API 请求。'
+ ),
+ link: 'https://docs.apipark.com/docs/developer_portal'
+ },
+ {
+ title: $t('审核订阅申请'),
+ description: $t('提供服务的团队可以审核来自其他团队的订阅申请,审核通过后的消费者才可发起 API 请求。'),
+ link: 'https://docs.apipark.com/docs/services/review_consumers'
+ }
+ ]
+ },
+ {
+ title: $t('集成'),
+ items: [
+ {
+ title: $t('日志'),
+ description: $t('APIPark 提供详尽的 API 调用日志,帮助企业监控、分析和审计 API 的运行状况。'),
+ link: 'https://docs.apipark.com/docs/system_setting/log/'
+ }
+ ]
+ }
+ ]
+ useEffect(() => {
+ localStorage.setItem('showGuide', showGuide.toString())
+ }, [showGuide])
+ useEffect(() => {
+ localStorage.setItem('showAdvancedGuide', showAdvancedGuide.toString())
+ }, [showAdvancedGuide])
+
+ useEffect(() => {
+ if (currentUrl === '/guide') {
+ setTimeout(() => {
+ navigator('/guide/page')
+ }, 0)
+ }
+ }, [])
+ useEffect(() => {
+ forceUpdate({})
+ }, [state.language])
+ return (
+
+ 👋
+ {$t('Hello!欢迎使用 APIPark')}
+
+ }
+ description={
+
+
+ {$t(
+ '你能通过 APIPark 快速在企业内部构建 API 开放门户/市场,享受极致的转发性能、API 可观测、服务治理、多租户管理、订阅审核流程等诸多好处。'
+ )}
+
+
+ {$t('如果你喜欢我们的产品,欢迎给我们 Star 或提供产品反馈意见。')}
+ {$t('点击这里')}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {$t('点击')}
+
+
+
+
+ Star
+
+
+ }
+ showBorder={false}
+ scrollPage={false}
+ contentClassName=" w-full pr-PAGE_INSIDE_X pb-PAGE_INSIDE_B"
+ >
+
+
+ {showGuide && (
+
+
+ 🚀
+ {`${$t('快速入门')}`}{' '}
+
+ {$t('我们提供了一些任务来帮你快速了解 APIPark')}
+
+ ),
+ children:
+ }
+ ]}
+ />
+ )}
+ {showAdvancedGuide && (
+
+
+ 🏍️
+ {`${$t('进阶教程')}`}{' '}
+
+ {$t('了解 APIPark 如何更好地管理 API 和 AI')}
+
+ ),
+ children: (
+
+ )
+ }
+ ]}
+ />
+ )}
+
+
+ )
}
-const QuickGuideContent = ({changeGuideShow,guideSections}:{changeGuideShow:Dispatch>,guideSections: {
- title: string;
+const QuickGuideContent = ({
+ changeGuideShow,
+ guideSections
+}: {
+ changeGuideShow: Dispatch>
+ guideSections: {
+ title: string
items: {
- title: string;
- description: string;
- link: string;
- }[];
-}[]})=>{
-
-
- return (<>
-
- {guideSections.map((section, index) => (
-
-
-
- {section.title}
-
-
-
- {section.items.map((item, itemIndex) => (
- { window.open(item.link, '_blank') }}
- >
- {item.description}
-
- ))}
-
-
-
- ))}
-
-
-
- } iconPosition="end" classNames={{icon:'h-[22px] flex items-center'}} href="https://docs.apipark.com" target="_blank" className="text-[14px] font-bold px-0">{$t('了解更多功能')}
- } onClick={()=>changeGuideShow((prev)=>!prev)} classNames={{icon:'h-[22px] flex items-center'}} className="text-[14px] font-bold">{$t('隐藏该教程')}
-
-
-
- >)
-}
\ No newline at end of file
+ title: string
+ description: string
+ link: string
+ }[]
+ }[]
+}) => {
+ return (
+ <>
+
+ {guideSections.map((section, index) => (
+
+
+
+ {section.title}
+
+
+
+ {section.items.map((item, itemIndex) => (
+ {
+ window.open(item.link, '_blank')
+ }}
+ >
+ {item.description}
+
+ ))}
+
+
+
+ ))}
+
+
+
+ }
+ iconPosition="end"
+ classNames={{ icon: 'h-[22px] flex items-center' }}
+ href="https://docs.apipark.com"
+ target="_blank"
+ className="text-[14px] font-bold px-0"
+ >
+ {$t('了解更多功能')}
+
+ }
+ onClick={() => changeGuideShow((prev) => !prev)}
+ classNames={{ icon: 'h-[22px] flex items-center' }}
+ className="text-[14px] font-bold"
+ >
+ {$t('隐藏该教程')}
+
+
+
+
+ >
+ )
+}
diff --git a/frontend/packages/core/src/pages/system/SystemConfig.tsx b/frontend/packages/core/src/pages/system/SystemConfig.tsx
index 55a833da..b449c81c 100644
--- a/frontend/packages/core/src/pages/system/SystemConfig.tsx
+++ b/frontend/packages/core/src/pages/system/SystemConfig.tsx
@@ -440,7 +440,6 @@ const SystemConfig = forwardRef((_, ref) => {
diff --git a/frontend/packages/core/src/pages/system/SystemList.tsx b/frontend/packages/core/src/pages/system/SystemList.tsx
index e397ab58..5c08f906 100644
--- a/frontend/packages/core/src/pages/system/SystemList.tsx
+++ b/frontend/packages/core/src/pages/system/SystemList.tsx
@@ -14,6 +14,7 @@ import { useNavigate } from 'react-router-dom'
import { SERVICE_KIND_OPTIONS, SYSTEM_TABLE_COLUMNS } from '../../const/system/const.tsx'
import { SystemConfigHandle, SystemTableListItem } from '../../const/system/type.ts'
import SystemConfig from './SystemConfig.tsx'
+import { ServiceDeployment } from './serviceDeployment/ServiceDeployment.tsx'
const SystemList: FC = () => {
const navigate = useNavigate()
@@ -23,7 +24,7 @@ const SystemList: FC = () => {
const { fetchData } = useFetch()
const [tableListDataSource, setTableListDataSource] = useState([])
const [tableHttpReload, setTableHttpReload] = useState(true)
- const { message } = App.useApp()
+ const { message, modal } = App.useApp()
const pageListRef = useRef(null)
const [memberValueEnum, setMemberValueEnum] = useState<{ [k: string]: { text: string } }>({})
const [open, setOpen] = useState(false)
@@ -128,7 +129,22 @@ const SystemList: FC = () => {
const onClose = () => {
setOpen(false)
}
+ const openLogsModal = (record: any) => {
+ console.log('record', record)
+ modal.confirm({
+ title: $t('部署过程'),
+ content: ,
+ onOk: () => {
+ console.log('ok')
+ },
+ width: 600,
+ okText: $t('确认'),
+ cancelText: $t('取消'),
+ closable: true,
+ icon: <>>
+ })
+ }
const columns = useMemo(() => {
const res = SYSTEM_TABLE_COLUMNS.map((x) => {
const dataIndex = x.dataIndex as string[]
@@ -145,6 +161,21 @@ const SystemList: FC = () => {
;(x.valueEnum as any)[option.value] = { text: $t(option.label) }
})
}
+ if ((x.dataIndex as string) === 'update_time') {
+ x.render = (text: any, record: any) => (
+ .ant-typography]:text-[#2196f3]' : ''}`}
+ onClick={(e) => {
+ if (record.can_delete) {
+ e?.stopPropagation();
+ openLogsModal(record)
+ }
+ }}
+ >
+ {text}
+
+ )
+ }
return { ...x, title: typeof x.title === 'string' ? $t(x.title as string) : x.title }
})
diff --git a/frontend/packages/core/src/pages/system/serviceDeployment/ServiceDeployment.tsx b/frontend/packages/core/src/pages/system/serviceDeployment/ServiceDeployment.tsx
new file mode 100644
index 00000000..7f75a6e9
--- /dev/null
+++ b/frontend/packages/core/src/pages/system/serviceDeployment/ServiceDeployment.tsx
@@ -0,0 +1,44 @@
+import { SystemTableListItem } from '@core/const/system/type'
+import type { StepsProps } from 'antd'
+import { Popover, Steps } from 'antd'
+import { CheckCircleOutlined, LoadingOutlined } from '@ant-design/icons'
+
+const customDot: StepsProps['progressDot'] = (dot, { status, index }) => (
+
+ step {index} status: {status}
+
+ }
+ >
+ {dot}
+
+)
+
+export const ServiceDeployment = (props: { record: SystemTableListItem }) => {
+ const { record } = props
+ console.log('record', record)
+
+ const items = [
+ {
+ title: 'Download',
+ description: '4.7 GB / 4.7 GB'
+ },
+ {
+ title: 'Deploy',
+ },
+ {
+ title: 'Initializing',
+ }
+ ]
+ return (
+
+ {/* */}
+
+
+ )
+}