feat: add banner

This commit is contained in:
scarqin
2025-01-02 08:21:33 +08:00
parent 1c536df3c8
commit fa0a211db9
4 changed files with 119 additions and 69 deletions
@@ -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