From 652c73b1b359309889ae361c599f0ee867a3f487 Mon Sep 17 00:00:00 2001 From: Gavan <994259213@qq.com> Date: Tue, 15 Jul 2025 10:16:29 +0800 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20=E6=A8=A1=E5=9E=8B=E8=B0=83?= =?UTF-8?q?=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ui/src/api/Model.ts | 32 ++ ui/src/api/types.ts | 100 +++- ui/src/assets/fonts/iconfont.js | 2 +- ui/src/components/form/index.tsx | 2 +- ui/src/pages/model/components/modelCard.tsx | 10 +- ui/src/pages/model/components/modelModal.tsx | 538 +++++++++++++------ ui/src/pages/model/constant.ts | 41 +- ui/src/pages/model/index.tsx | 8 +- ui/src/pages/user/dashboard/index.tsx | 32 -- 9 files changed, 543 insertions(+), 222 deletions(-) diff --git a/ui/src/api/Model.ts b/ui/src/api/Model.ts index b6f5f6d..71a1c4b 100644 --- a/ui/src/api/Model.ts +++ b/ui/src/api/Model.ts @@ -15,9 +15,11 @@ import { DomainAllModelResp, DomainCheckModelReq, DomainCreateModelReq, + DomainGetProviderModelListResp, DomainModel, DomainModelTokenUsageResp, DomainUpdateModelReq, + GetGetProviderModelListParams, GetGetTokenUsageParams, GetMyModelListParams, WebResp, @@ -169,6 +171,36 @@ export const getMyModelList = ( ...params, }); +/** + * @description 获取供应商支持的模型列表 + * + * @tags Model + * @name GetGetProviderModelList + * @summary 获取供应商支持的模型列表 + * @request GET:/api/v1/model/provider/supported + * @response `200` `(WebResp & { + data?: DomainGetProviderModelListResp, + +})` OK + */ + +export const getGetProviderModelList = ( + query: GetGetProviderModelListParams, + params: RequestParams = {}, +) => + request< + WebResp & { + data?: DomainGetProviderModelListResp; + } + >({ + path: `/api/v1/model/provider/supported`, + method: "GET", + query: query, + type: ContentType.Json, + format: "json", + ...params, + }); + /** * @description 获取模型token使用情况 * diff --git a/ui/src/api/types.ts b/ui/src/api/types.ts index 34adf65..81ae5aa 100644 --- a/ui/src/api/types.ts +++ b/ui/src/api/types.ts @@ -35,6 +35,19 @@ export enum ConstsModelStatus { ModelStatusInactive = "inactive", } +export enum ConstsModelProvider { + ModelProviderSiliconFlow = "SiliconFlow", + ModelProviderOpenAI = "OpenAI", + ModelProviderOllama = "Ollama", + ModelProviderDeepSeek = "DeepSeek", + ModelProviderMoonshot = "Moonshot", + ModelProviderAzureOpenAI = "AzureOpenAI", + ModelProviderBaiZhiCloud = "BaiZhiCloud", + ModelProviderHunyuan = "Hunyuan", + ModelProviderBaiLian = "BaiLian", + ModelProviderVolcengine = "Volcengine", +} + export enum ConstsChatRole { ChatRoleUser = "user", ChatRoleAssistant = "assistant", @@ -133,12 +146,15 @@ export interface DomainChatRecord { export interface DomainCheckModelReq { /** 接口地址 */ api_base: string; + api_header?: string; /** 接口密钥 */ api_key: string; + api_version?: string; /** 模型名称 */ model_name: string; /** 提供商 */ - provider: string; + provider: ConstsModelProvider; + type: "llm" | "coder" | "embedding" | "rerank"; } export interface DomainCompletionInfo { @@ -174,15 +190,29 @@ export interface DomainCreateAdminReq { export interface DomainCreateModelReq { /** 接口地址 如:https://api.qwen.com */ - api_base?: string; + api_base: string; + api_header?: string; /** 接口密钥 如:sk-xxxx */ api_key?: string; + api_version?: string; /** 模型名称 如: deepseek-v3 */ - model_name?: string; + model_name: string; /** 模型类型 llm:对话模型 coder:代码模型 */ model_type?: ConstsModelType; /** 提供商 */ - provider?: string; + provider: + | "SiliconFlow" + | "OpenAI" + | "Ollama" + | "DeepSeek" + | "Moonshot" + | "AzureOpenAI" + | "BaiZhiCloud" + | "Hunyuan" + | "BaiLian" + | "Volcengine"; + /** 模型显示名称 */ + show_name?: string; } export interface DomainCustomOAuth { @@ -253,6 +283,10 @@ export interface DomainDingtalkOAuthReq { enable?: boolean; } +export interface DomainGetProviderModelListResp { + models?: DomainProviderModelListItem[]; +} + export interface DomainIPInfo { /** ASN */ asn?: string; @@ -332,8 +366,12 @@ export interface DomainLoginResp { export interface DomainModel { /** 接口地址 如:https://api.qwen.com */ api_base?: string; + /** 接口头 如:Authorization: Bearer sk-xxxx */ + api_header?: string; /** 接口密钥 如:sk-xxxx */ api_key?: string; + /** 接口版本 如:2023-05-15 */ + api_version?: string; /** 创建时间 */ created_at?: number; /** 模型ID */ @@ -349,7 +387,9 @@ export interface DomainModel { /** 输出token数 */ output?: number; /** 提供商 */ - provider?: string; + provider?: ConstsModelProvider; + /** 模型显示名称 */ + show_name?: string; /** 状态 active:启用 inactive:禁用 */ status?: ConstsModelStatus; /** 更新时间 */ @@ -362,7 +402,17 @@ export interface DomainModelBasic { /** 模型名称 */ name?: string; /** 提供商 */ - provider?: string; + provider: + | "SiliconFlow" + | "OpenAI" + | "Ollama" + | "DeepSeek" + | "Moonshot" + | "AzureOpenAI" + | "BaiZhiCloud" + | "Hunyuan" + | "BaiLian" + | "Volcengine"; } export interface DomainModelData { @@ -411,6 +461,10 @@ export interface DomainProviderModel { provider?: string; } +export interface DomainProviderModelListItem { + model?: string; +} + export interface DomainRegisterReq { /** 邀请码 */ code: string; @@ -506,14 +560,28 @@ export interface DomainTimeStat { export interface DomainUpdateModelReq { /** 接口地址 如:https://api.qwen.com */ api_base?: string; + api_header?: string; /** 接口密钥 如:sk-xxxx */ api_key?: string; + api_version?: string; /** 模型ID */ id?: string; /** 模型名称 */ model_name?: string; /** 提供商 */ - provider?: string; + provider: + | "SiliconFlow" + | "OpenAI" + | "Ollama" + | "DeepSeek" + | "Moonshot" + | "AzureOpenAI" + | "BaiZhiCloud" + | "Hunyuan" + | "BaiLian" + | "Volcengine"; + /** 模型显示名称 */ + show_name?: string; /** 状态 active:启用 inactive:禁用 */ status?: ConstsModelStatus; } @@ -805,6 +873,24 @@ export interface GetMyModelListParams { model_type?: "llm" | "coder" | "embedding" | "audio" | "reranker"; } +export interface GetGetProviderModelListParams { + api_header?: string; + api_key?: string; + base_url: string; + provider: + | "SiliconFlow" + | "OpenAI" + | "Ollama" + | "DeepSeek" + | "Moonshot" + | "AzureOpenAI" + | "BaiZhiCloud" + | "Hunyuan" + | "BaiLian" + | "Volcengine"; + type: "llm" | "coder" | "embedding" | "audio" | "reranker"; +} + export interface GetGetTokenUsageParams { /** 模型类型 llm:对话模型 coder:代码模型 */ model_type: "llm" | "coder" | "embedding" | "audio" | "reranker"; diff --git a/ui/src/assets/fonts/iconfont.js b/ui/src/assets/fonts/iconfont.js index 2b997c2..abc174c 100644 --- a/ui/src/assets/fonts/iconfont.js +++ b/ui/src/assets/fonts/iconfont.js @@ -1 +1 @@ -window._iconfont_svg_string_4940939='',(l=>{var a=(c=(c=document.getElementsByTagName("script"))[c.length-1]).getAttribute("data-injectcss"),c=c.getAttribute("data-disable-injectsvg");if(!c){var t,h,i,o,e,m=function(a,c){c.parentNode.insertBefore(a,c)};if(a&&!l.__iconfont__svg__cssinject__){l.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(a){console&&console.log(a)}}t=function(){var a,c=document.createElement("div");c.innerHTML=l._iconfont_svg_string_4940939,(c=c.getElementsByTagName("svg")[0])&&(c.setAttribute("aria-hidden","true"),c.style.position="absolute",c.style.width=0,c.style.height=0,c.style.overflow="hidden",c=c,(a=document.body).firstChild?m(c,a.firstChild):a.appendChild(c))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(t,0):(h=function(){document.removeEventListener("DOMContentLoaded",h,!1),t()},document.addEventListener("DOMContentLoaded",h,!1)):document.attachEvent&&(i=t,o=l.document,e=!1,z(),o.onreadystatechange=function(){"complete"==o.readyState&&(o.onreadystatechange=null,n())})}function n(){e||(e=!0,i())}function z(){try{o.documentElement.doScroll("left")}catch(a){return void setTimeout(z,50)}n()}})(window); \ No newline at end of file +window._iconfont_svg_string_4940939='',(l=>{var a=(c=(c=document.getElementsByTagName("script"))[c.length-1]).getAttribute("data-injectcss"),c=c.getAttribute("data-disable-injectsvg");if(!c){var h,t,i,o,e,m=function(a,c){c.parentNode.insertBefore(a,c)};if(a&&!l.__iconfont__svg__cssinject__){l.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(a){console&&console.log(a)}}h=function(){var a,c=document.createElement("div");c.innerHTML=l._iconfont_svg_string_4940939,(c=c.getElementsByTagName("svg")[0])&&(c.setAttribute("aria-hidden","true"),c.style.position="absolute",c.style.width=0,c.style.height=0,c.style.overflow="hidden",c=c,(a=document.body).firstChild?m(c,a.firstChild):a.appendChild(c))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(h,0):(t=function(){document.removeEventListener("DOMContentLoaded",t,!1),h()},document.addEventListener("DOMContentLoaded",t,!1)):document.attachEvent&&(i=h,o=l.document,e=!1,z(),o.onreadystatechange=function(){"complete"==o.readyState&&(o.onreadystatechange=null,d())})}function d(){e||(e=!0,i())}function z(){try{o.documentElement.doScroll("left")}catch(a){return void setTimeout(z,50)}d()}})(window); \ No newline at end of file diff --git a/ui/src/components/form/index.tsx b/ui/src/components/form/index.tsx index 646717b..08be17e 100644 --- a/ui/src/components/form/index.tsx +++ b/ui/src/components/form/index.tsx @@ -16,7 +16,7 @@ export const FormItem = ({ children, required, }: { - label: string; + label: string | React.ReactNode; children: React.ReactNode; required?: boolean; }) => { diff --git a/ui/src/pages/model/components/modelCard.tsx b/ui/src/pages/model/components/modelCard.tsx index 42bf7fb..e81635f 100644 --- a/ui/src/pages/model/components/modelCard.tsx +++ b/ui/src/pages/model/components/modelCard.tsx @@ -2,7 +2,7 @@ import React, { useState } from 'react'; import Card from '@/components/card'; import { useRequest } from 'ahooks'; import { getMyModelList, putUpdateModel } from '@/api/Model'; -import { DomainModel, ConstsModelStatus } from '@/api/types'; +import { DomainModel, ConstsModelStatus, ConstsModelType } from '@/api/types'; import { Stack, Box, Button, Grid2 as Grid, ButtonBase } from '@mui/material'; import StyledLabel from '@/components/label'; import { Icon, Modal, message } from '@c-x/ui'; @@ -40,6 +40,7 @@ const ModelItem = ({ putUpdateModel({ id: data.id, status: ConstsModelStatus.ModelStatusInactive, + provider: data.provider!, }).then(() => { message.success('停用成功'); refresh(); @@ -64,6 +65,7 @@ const ModelItem = ({ putUpdateModel({ id: data.id, status: ConstsModelStatus.ModelStatusActive, + provider: data.provider!, }).then(() => { message.success('激活成功'); refresh(); @@ -110,7 +112,7 @@ const ModelItem = ({ @@ -202,7 +204,7 @@ const ModelItem = ({ interface IModelCardProps { title: string; - modelType: 'llm' | 'coder'; + modelType: ConstsModelType; } const ModelCard: React.FC = ({ title, modelType }) => { @@ -257,7 +259,7 @@ const ModelCard: React.FC = ({ title, modelType }) => { }} refresh={refresh} data={editData} - modelType={modelType} + type={modelType} /> ); diff --git a/ui/src/pages/model/components/modelModal.tsx b/ui/src/pages/model/components/modelModal.tsx index e6fec16..3437758 100644 --- a/ui/src/pages/model/components/modelModal.tsx +++ b/ui/src/pages/model/components/modelModal.tsx @@ -1,48 +1,62 @@ import { postCreateModel, - putUpdateModel, + getGetProviderModelList, postCheckModel, - getListModel, -} from '@/api/Model'; + putUpdateModel, +} from '@/api'; +import { ConstsModelType, DomainModel } from '@/api/types'; import Card from '@/components/card'; -import { ModelProvider } from '../constant'; -import { Icon, message, Modal } from '@c-x/ui'; -import { StyledFormLabel } from '@/components/form'; +import { ModelProvider } from '@/pages/model/constant'; import { Box, + Button, MenuItem, Stack, TextField, useTheme, - alpha, + alpha as addOpacityToColor, } from '@mui/material'; -import { useEffect, useMemo, useState } from 'react'; +import { Icon, message, Modal } from '@c-x/ui'; +import { useEffect, useState } from 'react'; import { Controller, useForm } from 'react-hook-form'; -import { - ConstsModelType, - DomainUpdateModelReq, - DomainCreateModelReq, - DomainProviderModel, -} from '@/api/types'; - -type ModelItem = any; - interface AddModelProps { open: boolean; - data?: ModelItem; + data: DomainModel | null; + type: ConstsModelType; onClose: () => void; refresh: () => void; - modelType: 'llm' | 'coder'; } -const ModelModal = ({ +interface AddModelForm { + provider: keyof typeof ModelProvider; + model: string; + base_url: string; + api_version: string; + api_key: string; + api_header_key: string; + api_header_value: string; + type: ConstsModelType; +} + +const titleMap = { + [ConstsModelType.ModelTypeLLM]: '对话模型', + [ConstsModelType.ModelTypeCoder]: '代码补全模型', + [ConstsModelType.ModelTypeEmbedding]: '向量模型', + [ConstsModelType.ModelTypeAudio]: '音频模型', + [ConstsModelType.ModelTypeReranker]: '重排序模型', +}; + +const ModelAdd = ({ open, onClose, refresh, data, - modelType, + type = ConstsModelType.ModelTypeLLM, }: AddModelProps) => { const theme = useTheme(); + + const providers: Record = ModelProvider; + const { formState: { errors }, handleSubmit, @@ -50,64 +64,134 @@ const ModelModal = ({ reset, setValue, watch, - } = useForm>({ + } = useForm({ defaultValues: { - provider: data?.provider || 'DeepSeek', - api_base: data?.api_base || ModelProvider.DeepSeek.defaultBaseUrl, - model_name: data?.model_name || '', - api_key: data?.api_key || '', + type, + provider: 'BaiZhiCloud', + base_url: providers['BaiZhiCloud'].defaultBaseUrl, + model: '', + api_version: '', + api_key: '', + api_header_key: '', + api_header_value: '', }, }); - const providerBrand = watch('provider') as keyof typeof ModelProvider; + const providerBrand = watch('provider'); - const [modelUserList, setModelUserList] = useState([]); + const [modelUserList, setModelUserList] = useState<{ model: string }[]>([]); const [loading, setLoading] = useState(false); const [modelLoading, setModelLoading] = useState(false); const [error, setError] = useState(''); + const [success, setSuccess] = useState(false); - const onCreateModel = (value: DomainCreateModelReq) => { - return postCreateModel({ - ...value, - model_type: modelType as ConstsModelType, - }).then(() => { - message.success('添加成功'); - reset(); - onClose(); - refresh(); + const handleReset = () => { + onClose(); + reset({ + type, + provider: 'BaiZhiCloud', + model: '', + base_url: '', + api_key: '', + api_version: '', + api_header_key: '', + api_header_value: '', }); + setModelUserList([]); + setSuccess(false); + setLoading(false); + setModelLoading(false); + setError(''); + refresh(); }; - const onUpdateModel = (value: DomainUpdateModelReq) => { - return putUpdateModel({ - ...value, - }).then(() => { - message.success('修改成功'); - reset(); - onClose(); - refresh(); - }); + const getModel = (value: AddModelForm) => { + let header = ''; + if (value.api_header_key && value.api_header_value) { + header = value.api_header_key + '=' + value.api_header_value; + } + setModelLoading(true); + getGetProviderModelList({ + type, + api_key: value.api_key, + base_url: value.base_url, + provider: value.provider as Exclude, + api_header: header, + }) + .then((res) => { + setModelUserList( + (res.models || []) + .filter((item): item is { model: string } => !!item.model) + .sort((a, b) => a.model!.localeCompare(b.model!)) + ); + if ( + data && + (res.models || []).find((it) => it.model === data.model_name) + ) { + setValue('model', data.model_name!); + } else { + setValue('model', res.models?.[0]?.model || ''); + } + setSuccess(true); + }) + .finally(() => { + setModelLoading(false); + }); }; - const onSubmit = (value: Required) => { + const onSubmit = (value: AddModelForm) => { + let header = ''; + if (value.api_header_key && value.api_header_value) { + header = value.api_header_key + '=' + value.api_header_value; + } setError(''); setLoading(true); postCheckModel({ - ...value, + // @ts-ignore + type, + api_key: value.api_key, + api_base: value.base_url, + api_version: value.api_version, + // @ts-ignore + provider: value.provider, + model_name: value.model, + api_header: header, }) .then((res) => { if (data) { - onUpdateModel({ - ...value, + putUpdateModel({ + api_key: value.api_key, + api_base: value.base_url, + model_name: value.model, + api_header: header, + api_version: value.api_version, id: data.id, - }).finally(() => { - setLoading(false); - }); + provider: value.provider as Exclude, + }) + .then(() => { + message.success('修改成功'); + handleReset(); + }) + .finally(() => { + setLoading(false); + }); } else { - onCreateModel(value).finally(() => { - setLoading(false); - }); + postCreateModel({ + model_type: type, + api_key: value.api_key, + api_base: value.base_url, + model_name: value.model, + api_header: header, + provider: value.provider as Exclude, + }) + .then(() => { + message.success('添加成功'); + handleReset(); + }) + .finally(() => { + setLoading(false); + }); } }) .catch(() => { @@ -115,72 +199,64 @@ const ModelModal = ({ }); }; + const resetCurData = (value: DomainModel) => { + // @ts-ignore + if (value.provider && value.provider !== 'Other') { + getModel({ + api_key: value.api_key || '', + base_url: value.api_base || '', + model: value.model_name || '', + provider: value.provider, + api_version: value.api_version || '', + api_header_key: value.api_header?.split('=')[0] || '', + api_header_value: value.api_header?.split('=')[1] || '', + type, + }); + } + reset({ + type, + provider: value.provider || 'Other', + model: value.model_name || '', + base_url: value.api_base || '', + api_key: value.api_key || '', + api_version: value.api_version || '', + api_header_key: value.api_header?.split('=')[0] || '', + api_header_value: value.api_header?.split('=')[1] || '', + }); + }; + useEffect(() => { if (open) { if (data) { - reset( - { - provider: data.provider || 'Other', - model_name: data.model_name || '', - api_base: data.api_base || '', - api_key: data.api_key || '', - }, - { - keepDefaultValues: true, - } - ); + console.log(data); + resetCurData(data); } else { - reset(); + reset({ + type, + provider: 'BaiZhiCloud', + model: '', + base_url: providers['BaiZhiCloud'].defaultBaseUrl, + api_key: '', + api_version: '', + api_header_key: '', + api_header_value: '', + }); } } // eslint-disable-next-line react-hooks/exhaustive-deps }, [data, open]); - useEffect(() => { - if (open) { - getListModel().then((res) => { - setModelUserList(res.providers || []); - if (!data) { - setValue('provider', res.providers?.[0].provider || 'DeepSeek'); - } - }); - } - }, [open]); - - const currentModelList = useMemo(() => { - return ( - modelUserList.find((it) => it.provider === providerBrand)?.models || [] - ); - }, [modelUserList, providerBrand]); - - useEffect(() => { - if (currentModelList.length > 0) { - if (data) { - setValue('api_base', data.api_base || ''); - setValue('model_name', data.model_name || ''); - } else { - setValue('api_base', currentModelList[0].api_base || ''); - setValue('model_name', currentModelList[0].name || ''); - } - } - }, [currentModelList, data]); - return ( { - reset(); - setModelUserList([]); - setLoading(false); - setError(''); - onClose(); - }} + onCancel={handleReset} okText='保存' onOk={handleSubmit(onSubmit)} okButtonProps={{ loading, + disabled: !success && providerBrand !== 'Other', }} > @@ -189,7 +265,7 @@ const ModelModal = ({ sx={{ width: 200, flexShrink: 0, - bgcolor: 'rgb(248, 249, 250)', + bgcolor: 'background.paper2', borderRadius: '10px', p: 1, }} @@ -199,12 +275,12 @@ const ModelModal = ({ > 模型供应商 - {modelUserList.map((it) => ( + {Object.values(providers).map((it) => ( { - if (data) return; - setError(''); - reset( - { - provider: it.provider as keyof typeof ModelProvider, - api_base: '', - model_name: '', + if (data && data.provider === it.label) { + resetCurData(data); + } else { + setModelUserList([]); + setError(''); + setModelLoading(false); + setSuccess(false); + reset({ + provider: it.label as keyof typeof ModelProvider, + base_url: + it.label === 'AzureOpenAI' ? '' : it.defaultBaseUrl, + model: '', + api_version: '', api_key: '', - }, - { - keepDefaultValues: true, - } - ); + api_header_key: '', + api_header_value: '', + }); + } }} > - - {it.provider} + + {it.cn || it.label || '其他'} ))} - API 地址 + + API 地址{' '} + + * + + { field.onChange(e.target.value); setModelUserList([]); - setValue('model_name', ''); + setValue('model', ''); + setSuccess(false); }} /> )} @@ -281,12 +363,16 @@ const ModelModal = ({ justifyContent={'space-between'} sx={{ fontSize: 14, lineHeight: '32px', mt: 2 }} > - + API Secret - - {ModelProvider[providerBrand].modelDocumentUrl && ( + {providers[providerBrand].secretRequired && ( + + {' '} + * + + )} + + {providers[providerBrand].modelDocumentUrl && ( window.open( - ModelProvider[providerBrand].modelDocumentUrl, + providers[providerBrand].modelDocumentUrl, '_blank' ) } @@ -311,7 +397,7 @@ const ModelModal = ({ name='api_key' rules={{ required: { - value: ModelProvider[providerBrand].secretRequired, + value: providers[providerBrand].secretRequired, message: 'API Secret 不能为空', }, }} @@ -325,36 +411,148 @@ const ModelModal = ({ helperText={errors.api_key?.message} onChange={(e) => { field.onChange(e.target.value); + setModelUserList([]); + setValue('model', ''); + setSuccess(false); }} /> )} /> - - - 模型名称 - - - ( - - {currentModelList.map((it) => ( - - {it.name} - - ))} - - )} - /> + {providerBrand === 'AzureOpenAI' && ( + <> + + API Version + + ( + { + field.onChange(e.target.value); + setModelUserList([]); + setValue('model', ''); + setSuccess(false); + }} + /> + )} + /> + + )} + {providerBrand === 'Other' ? ( + <> + + 模型名称{' '} + + * + + + ( + + )} + /> + + 需要与模型供应商提供的名称完全一致,不要随便填写 + + + ) : modelUserList.length === 0 ? ( + + ) : ( + <> + + 模型名称{' '} + + * + + + ( + + {modelUserList.map((it) => ( + + {it.model} + + ))} + + )} + /> + {providers[providerBrand].customHeader && ( + <> + + Header + + + ( + + )} + /> + = + ( + + )} + /> + + + )} + + )} {error && ( { return ( - - + + ); }; diff --git a/ui/src/pages/user/dashboard/index.tsx b/ui/src/pages/user/dashboard/index.tsx index 9185d88..6c154bd 100644 --- a/ui/src/pages/user/dashboard/index.tsx +++ b/ui/src/pages/user/dashboard/index.tsx @@ -17,38 +17,6 @@ const Dashboard = () => { const [memberData, setMemberData] = useState(null); const [timeRange, setTimeRange] = useState('24h'); - const { data: userData, refresh } = useRequest( - () => - getListUser({ - page: 1, - size: 99999, - }), - { - manual: true, - onSuccess: (res) => { - if (id) { - setMemberData(res.users?.find((item) => item.id === id) || null); - } else { - setMemberData(res.users?.[0] || null); - navigate(`/dashboard/member/${res.users?.[0]?.id}`); - } - }, - } - ); - const userList = useMemo(() => { - return userData?.users || []; - }, [userData]); - useEffect(() => { - if (tabValue === 'member') { - refresh(); - } - }, [tabValue]); - - const onMemberChange = (data: DomainUser) => { - setMemberData(data); - navigate(`/dashboard/member/${data.id}`); - }; - return ( From 8324ff130fa78782de2344d613dd0d5bfb38edba Mon Sep 17 00:00:00 2001 From: Gavan <994259213@qq.com> Date: Tue, 15 Jul 2025 17:57:27 +0800 Subject: [PATCH 2/2] =?UTF-8?q?fix:=20=E7=94=A8=E6=88=B7=E5=88=97=E8=A1=A8?= =?UTF-8?q?=E5=B1=95=E7=A4=BA=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ui/src/pages/completion/index.tsx | 2 +- ui/src/pages/user/completion/index.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/src/pages/completion/index.tsx b/ui/src/pages/completion/index.tsx index 6f6e7af..2e8d16f 100644 --- a/ui/src/pages/completion/index.tsx +++ b/ui/src/pages/completion/index.tsx @@ -44,7 +44,7 @@ const Completion = () => { const { data: userOptions = { users: [] } } = useRequest(() => getListUser({ page: 1, - size: 10, + size: 9999, }) ); diff --git a/ui/src/pages/user/completion/index.tsx b/ui/src/pages/user/completion/index.tsx index 1f6b429..e7f19c3 100644 --- a/ui/src/pages/user/completion/index.tsx +++ b/ui/src/pages/user/completion/index.tsx @@ -44,7 +44,7 @@ const Completion = () => { const { data: userOptions = { users: [] } } = useRequest(() => getListUser({ page: 1, - size: 10, + size: 9999, }) );