mirror of
https://github.com/APIParkLab/APIPark.git
synced 2026-06-14 20:41:15 +08:00
feat: add banner
This commit is contained in:
@@ -7,7 +7,8 @@ import { BasicResponse, RESPONSE_TIPS, STATUS_CODE } from '@common/const/const'
|
||||
import { useFetch } from '@common/hooks/http'
|
||||
import { $t } from '@common/locales'
|
||||
import AIProviderSelect, { AIProvider } from '@core/components/AIProviderSelect'
|
||||
import { App, Button, Typography } from 'antd'
|
||||
import { getTime } from '@dashboard/utils/dashboard'
|
||||
import { Alert, App, Button, Typography } from 'antd'
|
||||
import dayjs from 'dayjs'
|
||||
import React, { useEffect, useRef, useState } from 'react'
|
||||
import { useSearchParams } from 'react-router-dom'
|
||||
@@ -37,16 +38,22 @@ const ApiSettings: React.FC = () => {
|
||||
if (!selectedProvider) return
|
||||
setQueryBtnLoading(true)
|
||||
try {
|
||||
const eoParams = {
|
||||
provider: selectedProvider,
|
||||
page_size: params.pageSize,
|
||||
keyword: searchWord,
|
||||
page: params.current,
|
||||
start: timeRange.start,
|
||||
end: timeRange.end
|
||||
}
|
||||
if (!timeRange || !timeRange.start) {
|
||||
const { startTime, endTime } = getTime(timeButton, [])
|
||||
eoParams.start = startTime
|
||||
eoParams.end = endTime
|
||||
}
|
||||
const response = await fetchData<BasicResponse<{ data: APIKey[] }>>('ai/apis', {
|
||||
method: 'GET',
|
||||
eoParams: {
|
||||
provider: selectedProvider,
|
||||
page_size: params.pageSize,
|
||||
keyword: searchWord,
|
||||
page: params.current,
|
||||
start: timeRange.start,
|
||||
end: timeRange.end
|
||||
}
|
||||
eoParams
|
||||
})
|
||||
setQueryBtnLoading(false)
|
||||
if (response.code === STATUS_CODE.SUCCESS) {
|
||||
@@ -94,7 +101,7 @@ const ApiSettings: React.FC = () => {
|
||||
|
||||
const columns: PageProColumns<APIKey>[] = [
|
||||
{
|
||||
title: 'AI 服务(name)',
|
||||
title: 'AI 服务',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
width: 180
|
||||
@@ -108,28 +115,38 @@ const ApiSettings: React.FC = () => {
|
||||
},
|
||||
{
|
||||
title: '模型',
|
||||
dataIndex: 'model',
|
||||
dataIndex: ['model', 'name'],
|
||||
key: 'model',
|
||||
width: 150
|
||||
width: 150,
|
||||
filters: true,
|
||||
onFilter: true,
|
||||
valueType: 'select',
|
||||
valueEnum: {}
|
||||
},
|
||||
{
|
||||
title: '已用 Token',
|
||||
dataIndex: 'use_token',
|
||||
key: 'use_token',
|
||||
width: 120
|
||||
width: 120,
|
||||
sorter: true
|
||||
},
|
||||
{
|
||||
title: '是否放行',
|
||||
dataIndex: 'disabled',
|
||||
key: 'disabled',
|
||||
width: 100,
|
||||
render: (disabled: boolean) => <Typography.Text>{disabled ? '禁用' : '启用'}</Typography.Text>
|
||||
ellipsis: true,
|
||||
filters: true,
|
||||
onFilter: true,
|
||||
valueType: 'select',
|
||||
valueEnum: {
|
||||
true: { text: <Typography.Text type="danger">{$t('拦截')}</Typography.Text> },
|
||||
false: { text: <Typography.Text type="success">{$t('放行')}</Typography.Text> }
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '编辑时间',
|
||||
dataIndex: 'update_time',
|
||||
key: 'update_time',
|
||||
width: 160,
|
||||
width: 200,
|
||||
render: (time: string) => <Typography.Text>{dayjs(time).format('YYYY-MM-DD HH:mm:ss')}</Typography.Text>
|
||||
},
|
||||
...operation
|
||||
@@ -145,56 +162,77 @@ const ApiSettings: React.FC = () => {
|
||||
pageListRef.current?.reload()
|
||||
}
|
||||
|
||||
const renderProviderBanner = () => {
|
||||
if (provider?.disabled) {
|
||||
return (
|
||||
<Alert
|
||||
message={$t('当前供应商已停用,以下 API 均临时调用 Google Gemini 下的 Gemini Pro 模型能力。')}
|
||||
type="warning"
|
||||
showIcon
|
||||
style={{ marginBottom: 16 }}
|
||||
action={
|
||||
<Button size="small" type="link" onClick={() => window.open('/details')}>
|
||||
{$t('查看详情')}
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<InsidePage
|
||||
className="overflow-y-auto gap-4 pb-PAGE_INSIDE_B pr-PAGE_INSIDE_X"
|
||||
pageTitle={$t('AI API 列表')}
|
||||
description={
|
||||
<>
|
||||
{$t('支持查看调用某个 AI 供应商的所有 AI 服务 API 清单')}
|
||||
<div className="mt-4">
|
||||
<div className="flex gap-2 items-center">
|
||||
<AIProviderSelect
|
||||
value={selectedProvider}
|
||||
onChange={(value: string, provider: AIProvider) => {
|
||||
onChange={(value, option) => {
|
||||
setSelectedProvider(value)
|
||||
setProvider(provider)
|
||||
setProvider(option)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{renderProviderBanner()}
|
||||
</>
|
||||
}
|
||||
showBorder={false}
|
||||
scrollPage={false}
|
||||
>
|
||||
<div className="flex items-center flex-wrap pb-[10px] px-btnbase content-before bg-MAIN_BG pr-PAGE_INSIDE_X">
|
||||
<TimeRangeSelector
|
||||
labelSize="small"
|
||||
hideBtns={['hour']}
|
||||
initialTimeButton={timeButton}
|
||||
onTimeButtonChange={setTimeButton}
|
||||
onTimeRangeChange={($event) => {
|
||||
setTimeRange($event)
|
||||
}}
|
||||
/>
|
||||
<div className="flex flex-nowrap items-center pt-btnybase">
|
||||
<Button onClick={resetQuery}>{$t('重置')}</Button>
|
||||
<Button
|
||||
className="ml-btnybase"
|
||||
type="primary"
|
||||
loading={queryBtnLoading}
|
||||
onClick={() => {
|
||||
setQueryBtnLoading(true)
|
||||
getData()
|
||||
}}
|
||||
>
|
||||
{$t('查询')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="h-[calc(100%-1rem-36px)]">
|
||||
<PageList
|
||||
ref={pageListRef}
|
||||
rowKey="id"
|
||||
afterNewBtn={
|
||||
<div className="flex items-center flex-wrap pb-[10px] px-btnbase content-before bg-MAIN_BG pr-PAGE_INSIDE_X">
|
||||
<TimeRangeSelector
|
||||
labelSize="small"
|
||||
hideBtns={['hour']}
|
||||
initialTimeButton={timeButton}
|
||||
onTimeButtonChange={setTimeButton}
|
||||
onTimeRangeChange={($event) => {
|
||||
setTimeRange($event)
|
||||
}}
|
||||
/>
|
||||
<div className="flex flex-nowrap items-center pt-btnybase">
|
||||
<Button onClick={resetQuery}>{$t('重置')}</Button>
|
||||
<Button
|
||||
className="ml-btnybase"
|
||||
type="primary"
|
||||
loading={queryBtnLoading}
|
||||
onClick={() => {
|
||||
setQueryBtnLoading(true)
|
||||
getData()
|
||||
}}
|
||||
>
|
||||
{$t('查询')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
request={requestApis}
|
||||
onSearchWordChange={(e) => {
|
||||
setSearchWord(e.target.value)
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import { BasicResponse } from '@common/const/const'
|
||||
import { useGlobalContext } from '@common/contexts/GlobalStateContext'
|
||||
import { useFetch } from '@common/hooks/http'
|
||||
import { $t } from '@common/locales'
|
||||
import {
|
||||
CoordinateExtent,
|
||||
Edge,
|
||||
@@ -15,7 +16,7 @@ import {
|
||||
useNodesState
|
||||
} from '@xyflow/react'
|
||||
import '@xyflow/react/dist/style.css'
|
||||
import { Button, Space } from 'antd'
|
||||
import { Button, Space, Spin } from 'antd'
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import CustomEdge from './components/CustomEdge'
|
||||
@@ -66,6 +67,7 @@ const edgeTypes: EdgeTypes = {
|
||||
|
||||
const AIFlowChart = () => {
|
||||
const [modelData, setModelData] = useState<ModelListData[]>([])
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [nodes, setNodes, onNodesChange] = useNodesState<Node>([])
|
||||
const [edges, setEdges, onEdgesChange] = useEdgesState<Edge>([])
|
||||
const { fetchData } = useFetch()
|
||||
@@ -73,14 +75,19 @@ const AIFlowChart = () => {
|
||||
const navigate = useNavigate()
|
||||
|
||||
useEffect(() => {
|
||||
setLoading(true)
|
||||
fetchData<ApiResponse>('ai/providers/configured', {
|
||||
method: 'GET',
|
||||
eoTransformKeys: ['default_llm']
|
||||
// eoApiPrefix: 'http://uat.apikit.com:11204/mockApi/aoplatform/api/v1/'
|
||||
}).then((response) => {
|
||||
const mockApiResponse: ApiResponse = response as ApiResponse
|
||||
setModelData(mockApiResponse.data.providers)
|
||||
})
|
||||
.then((response) => {
|
||||
const mockApiResponse: ApiResponse = response as ApiResponse
|
||||
setModelData(mockApiResponse.data.providers)
|
||||
})
|
||||
.finally(() => {
|
||||
setLoading(false)
|
||||
})
|
||||
}, [aiConfigFlushed])
|
||||
|
||||
useEffect(() => {
|
||||
@@ -211,11 +218,15 @@ const AIFlowChart = () => {
|
||||
|
||||
return (
|
||||
<div className="w-full h-full">
|
||||
{modelData.length === 0 ? (
|
||||
{loading ? (
|
||||
<div className="flex justify-center items-center h-full">
|
||||
<Spin size="large" />
|
||||
</div>
|
||||
) : modelData.length === 0 ? (
|
||||
<Space className="flex flex-col justify-center items-center h-[200px]">
|
||||
<div>No AI model configured</div>
|
||||
<div>{$t('未配置 AI 模型')}</div>
|
||||
<Button type="primary" onClick={() => navigate('/aisetting?status=unconfigure')}>
|
||||
Go to Settings
|
||||
{$t('前往设置')}
|
||||
</Button>
|
||||
</Space>
|
||||
) : (
|
||||
|
||||
@@ -309,7 +309,7 @@ const KeySettings: React.FC = () => {
|
||||
render: (dom: React.ReactNode, entity: APIKey) => {
|
||||
return entity.expire_time === 0
|
||||
? $t('永不过期')
|
||||
: dayjs(Number(entity.expire_time)).format('YYYY-MM-DD HH:mm:ss')
|
||||
: dayjs(Number(entity.expire_time) * 1000).format('YYYY-MM-DD HH:mm:ss')
|
||||
}
|
||||
},
|
||||
...operation
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
import { CloseOutlined, ExpandOutlined, SearchOutlined } from '@ant-design/icons'
|
||||
import { Select, Input, Button, App, Drawer } from 'antd'
|
||||
import { debounce } from 'lodash-es'
|
||||
import { useState, useEffect, useRef } from 'react'
|
||||
import { MonitorApiData, SearchBody } from '@dashboard/const/type'
|
||||
import { getTime } from '../utils/dashboard'
|
||||
import ScrollableSection from '@common/components/aoplatform/ScrollableSection'
|
||||
import TimeRangeSelector, {
|
||||
RangeValue,
|
||||
TimeRange,
|
||||
TimeRangeButton
|
||||
} from '@common/components/aoplatform/TimeRangeSelector'
|
||||
import MonitorTable, { MonitorTableHandler } from './MonitorTable'
|
||||
import { BasicResponse, RESPONSE_TIPS, STATUS_CODE } from '@common/const/const'
|
||||
import { DefaultOptionType } from 'antd/es/select'
|
||||
import { useExcelExport } from '@common/hooks/excel'
|
||||
import { API_TABLE_GLOBAL_COLUMNS_CONFIG } from '@dashboard/const/const'
|
||||
import { useFetch } from '@common/hooks/http'
|
||||
import { EntityItem } from '@common/const/type'
|
||||
import { useExcelExport } from '@common/hooks/excel'
|
||||
import { useFetch } from '@common/hooks/http'
|
||||
import { $t } from '@common/locales'
|
||||
import { API_TABLE_GLOBAL_COLUMNS_CONFIG } from '@dashboard/const/const'
|
||||
import { MonitorApiData, SearchBody } from '@dashboard/const/type'
|
||||
import { App, Button, Drawer, Input, Select } from 'antd'
|
||||
import { DefaultOptionType } from 'antd/es/select'
|
||||
import { debounce } from 'lodash-es'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import { getTime } from '../utils/dashboard'
|
||||
import MonitorTable, { MonitorTableHandler } from './MonitorTable'
|
||||
export type MonitorApiPageProps = {
|
||||
fetchTableData: (body: SearchBody) => Promise<BasicResponse<{ statistics: MonitorApiData[] }>>
|
||||
detailDrawerContent: React.ReactNode
|
||||
@@ -100,6 +100,7 @@ export default function MonitorApiPage(props: MonitorApiPageProps) {
|
||||
const getMonitorData = () => {
|
||||
let query = queryData
|
||||
if (!queryData || queryData.start === undefined) {
|
||||
console.log(timeButton, datePickerValue)
|
||||
const { startTime, endTime } = getTime(timeButton, datePickerValue || [])
|
||||
query = { ...query, start: startTime, end: endTime }
|
||||
}
|
||||
@@ -186,7 +187,7 @@ export default function MonitorApiPage(props: MonitorApiPageProps) {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="h-full overflow-hidden">
|
||||
<div className="overflow-hidden h-full">
|
||||
<ScrollableSection>
|
||||
<div className="pl-btnbase pr-btnrbase pb-btnbase content-before">
|
||||
<TimeRangeSelector
|
||||
@@ -196,8 +197,8 @@ export default function MonitorApiPage(props: MonitorApiPageProps) {
|
||||
initialDatePickerValue={datePickerValue}
|
||||
onTimeRangeChange={handleTimeRangeChange}
|
||||
/>
|
||||
<div className="flex flex-nowrap items-center pt-btnybase mr-btnybase">
|
||||
<label className=" whitespace-nowrap inline-block">{$t('服务')}:</label>
|
||||
<div className="flex flex-nowrap items-center pt-btnybase mr-btnybase">
|
||||
<label className="inline-block whitespace-nowrap">{$t('服务')}:</label>
|
||||
<Select
|
||||
className="w-[346px]"
|
||||
value={queryData?.services}
|
||||
@@ -212,7 +213,7 @@ export default function MonitorApiPage(props: MonitorApiPageProps) {
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-nowrap items-center pt-btnybase mr-btnybase">
|
||||
<div className="flex flex-nowrap items-center pt-btnybase mr-btnybase">
|
||||
<label className=" whitespace-nowrap inline-block w-[42px] text-right">API :</label>
|
||||
<Select
|
||||
className="w-[346px]"
|
||||
@@ -226,7 +227,7 @@ export default function MonitorApiPage(props: MonitorApiPageProps) {
|
||||
setQueryData((prevData) => ({ ...(prevData || {}), apis: value }))
|
||||
}}
|
||||
/>
|
||||
<label className="ml-btnybase whitespace-nowrap">{$t('路径')}:</label>
|
||||
<label className="whitespace-nowrap ml-btnybase">{$t('路径')}:</label>
|
||||
<div className="w-[346px] inline-block">
|
||||
{/* <SearchInputGroup eoSingle={false} eoInputVal={queryData.path} eoClick={() => setQueryData({ ...queryData, path: '' })} /> */}
|
||||
<Input
|
||||
|
||||
Reference in New Issue
Block a user