mirror of
https://github.com/labring/FastGPT.git
synced 2025-07-27 00:17:31 +00:00
feat: ai proxy v1 (#3898)
* feat: ai proxy v1 * perf: ai proxy channel crud * feat: ai proxy logs * feat: channel test * doc * update lock
This commit is contained in:
@@ -98,7 +98,6 @@ const MultipleSelect = <T = any,>({
|
||||
return (
|
||||
<MenuItem
|
||||
key={i}
|
||||
{...menuItemStyles}
|
||||
{...(isSelected
|
||||
? {
|
||||
color: 'primary.600'
|
||||
@@ -114,6 +113,7 @@ const MultipleSelect = <T = any,>({
|
||||
whiteSpace={'pre-wrap'}
|
||||
fontSize={'sm'}
|
||||
gap={2}
|
||||
{...menuItemStyles}
|
||||
>
|
||||
<Checkbox isChecked={isSelected} />
|
||||
{item.icon && <MyAvatar src={item.icon} w={'1rem'} borderRadius={'0'} />}
|
||||
@@ -204,6 +204,7 @@ const MultipleSelect = <T = any,>({
|
||||
}}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
onclickItem(item.value);
|
||||
}}
|
||||
/>
|
||||
@@ -230,7 +231,6 @@ const MultipleSelect = <T = any,>({
|
||||
overflowY={'auto'}
|
||||
>
|
||||
<MenuItem
|
||||
{...menuItemStyles}
|
||||
color={isSelectAll ? 'primary.600' : 'myGray.900'}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
@@ -241,6 +241,7 @@ const MultipleSelect = <T = any,>({
|
||||
fontSize={'sm'}
|
||||
gap={2}
|
||||
mb={1}
|
||||
{...menuItemStyles}
|
||||
>
|
||||
<Checkbox isChecked={isSelectAll} />
|
||||
<Box flex={'1 0 0'}>{t('common:common.All')}</Box>
|
||||
|
@@ -4,7 +4,8 @@ import React, {
|
||||
useMemo,
|
||||
useEffect,
|
||||
useImperativeHandle,
|
||||
ForwardedRef
|
||||
ForwardedRef,
|
||||
useState
|
||||
} from 'react';
|
||||
import {
|
||||
Menu,
|
||||
@@ -15,7 +16,8 @@ import {
|
||||
MenuButton,
|
||||
Box,
|
||||
css,
|
||||
Flex
|
||||
Flex,
|
||||
Input
|
||||
} from '@chakra-ui/react';
|
||||
import type { ButtonProps, MenuItemProps } from '@chakra-ui/react';
|
||||
import MyIcon from '../Icon';
|
||||
@@ -33,8 +35,10 @@ import { useScrollPagination } from '../../../hooks/useScrollPagination';
|
||||
export type SelectProps<T = any> = ButtonProps & {
|
||||
value?: T;
|
||||
placeholder?: string;
|
||||
isSearch?: boolean;
|
||||
list: {
|
||||
alias?: string;
|
||||
icon?: string;
|
||||
label: string | React.ReactNode;
|
||||
description?: string;
|
||||
value: T;
|
||||
@@ -49,6 +53,7 @@ const MySelect = <T = any,>(
|
||||
{
|
||||
placeholder,
|
||||
value,
|
||||
isSearch = false,
|
||||
width = '100%',
|
||||
list = [],
|
||||
onchange,
|
||||
@@ -63,6 +68,7 @@ const MySelect = <T = any,>(
|
||||
const ButtonRef = useRef<HTMLButtonElement>(null);
|
||||
const MenuListRef = useRef<HTMLDivElement>(null);
|
||||
const SelectedItemRef = useRef<HTMLDivElement>(null);
|
||||
const SearchInputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
const menuItemStyles: MenuItemProps = {
|
||||
borderRadius: 'sm',
|
||||
@@ -79,6 +85,18 @@ const MySelect = <T = any,>(
|
||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||
const selectItem = useMemo(() => list.find((item) => item.value === value), [list, value]);
|
||||
|
||||
const [search, setSearch] = useState('');
|
||||
const filterList = useMemo(() => {
|
||||
if (!isSearch || !search) {
|
||||
return list;
|
||||
}
|
||||
return list.filter((item) => {
|
||||
const text = `${item.label?.toString()}${item.alias}${item.value}`;
|
||||
const regx = new RegExp(search, 'gi');
|
||||
return regx.test(text);
|
||||
});
|
||||
}, [list, search, isSearch]);
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
focus() {
|
||||
onOpen();
|
||||
@@ -90,17 +108,19 @@ const MySelect = <T = any,>(
|
||||
const menu = MenuListRef.current;
|
||||
const selectedItem = SelectedItemRef.current;
|
||||
menu.scrollTop = selectedItem.offsetTop - menu.offsetTop - 100;
|
||||
|
||||
if (isSearch) {
|
||||
setSearch('');
|
||||
}
|
||||
}
|
||||
}, [isOpen]);
|
||||
}, [isSearch, isOpen]);
|
||||
|
||||
const { runAsync: onChange, loading } = useRequest2((val: T) => onchange?.(val));
|
||||
|
||||
const isSelecting = loading || isLoading;
|
||||
|
||||
const ListRender = useMemo(() => {
|
||||
return (
|
||||
<>
|
||||
{list.map((item, i) => (
|
||||
{filterList.map((item, i) => (
|
||||
<Box key={i}>
|
||||
<MenuItem
|
||||
{...menuItemStyles}
|
||||
@@ -123,7 +143,10 @@ const MySelect = <T = any,>(
|
||||
fontSize={'sm'}
|
||||
display={'block'}
|
||||
>
|
||||
<Box>{item.label}</Box>
|
||||
<Flex alignItems={'center'}>
|
||||
{item.icon && <MyIcon mr={2} name={item.icon as any} w={'1rem'} />}
|
||||
{item.label}
|
||||
</Flex>
|
||||
{item.description && (
|
||||
<Box color={'myGray.500'} fontSize={'xs'}>
|
||||
{item.description}
|
||||
@@ -135,7 +158,9 @@ const MySelect = <T = any,>(
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}, [list, value]);
|
||||
}, [filterList, value]);
|
||||
|
||||
const isSelecting = loading || isLoading;
|
||||
|
||||
return (
|
||||
<Box
|
||||
@@ -176,8 +201,33 @@ const MySelect = <T = any,>(
|
||||
{...props}
|
||||
>
|
||||
<Flex alignItems={'center'}>
|
||||
{isSelecting && <MyIcon mr={2} name={'common/loading'} w={'16px'} />}
|
||||
{selectItem?.alias || selectItem?.label || placeholder}
|
||||
{isSelecting && <MyIcon mr={2} name={'common/loading'} w={'1rem'} />}
|
||||
{isSearch && isOpen ? (
|
||||
<Input
|
||||
ref={SearchInputRef}
|
||||
autoFocus
|
||||
variant={'unstyled'}
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.target.value)}
|
||||
placeholder={
|
||||
selectItem?.alias ||
|
||||
(typeof selectItem?.label === 'string' ? selectItem?.label : placeholder)
|
||||
}
|
||||
size={'sm'}
|
||||
w={'100%'}
|
||||
color={'myGray.700'}
|
||||
onBlur={() => {
|
||||
setTimeout(() => {
|
||||
SearchInputRef?.current?.focus();
|
||||
}, 0);
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
{selectItem?.icon && <MyIcon mr={2} name={selectItem.icon as any} w={'1rem'} />}
|
||||
{selectItem?.alias || selectItem?.label || placeholder}
|
||||
</>
|
||||
)}
|
||||
</Flex>
|
||||
</MenuButton>
|
||||
|
||||
|
Reference in New Issue
Block a user