fix: 插件、AI应用集、知识库测试BUG修复及代码优化

main
jiangxucong 1 month ago
parent 8004ee2e79
commit 4a637b8c07

@ -1,10 +1,9 @@
'use client';
import {PropsWithChildren, memo, useEffect, useState} from 'react';
import {createStyles} from "antd-style";
import {Anchor, Button, Card, Image} from "antd";
import {RightCircleOutlined, LikeFilled, WechatFilled, HomeFilled, DribbbleSquareFilled, CopyFilled, MedicineBoxFilled, IeCircleFilled, IdcardFilled, HourglassFilled, InsuranceFilled} from "@ant-design/icons";
// import Link from "next/link";
import { memo, useEffect, useState } from 'react';
import { createStyles } from "antd-style";
import { Anchor, Button, Card, Image } from "antd";
import { RightCircleOutlined, LikeFilled, WechatFilled, HomeFilled, DribbbleSquareFilled, CopyFilled, MedicineBoxFilled, IeCircleFilled, IdcardFilled, HourglassFilled, InsuranceFilled } from "@ant-design/icons";
import request from '@/app/request';
import Title from "../discover/components/Title";
@ -15,7 +14,61 @@ const stList = [
id: '1',
title: 'XXX助手',
},
]
];
// Anchor 导航配置
const anchorItems = [
{
href: '#rmtj',
key: '1',
title: { icon: LikeFilled, text: '热门推荐', style: 'first' }
},
{
href: '#ailt',
key: '2',
title: { icon: WechatFilled, text: 'AI聊天', style: 'sec' }
},
{
href: '#aixz',
key: '3',
title: { icon: CopyFilled, text: 'AI写作', style: 'thd' }
},
{
href: '#aihh',
key: '4',
title: { icon: DribbbleSquareFilled, text: 'AI绘画', style: 'four' }
},
{
href: '#aisp',
key: '5',
title: { icon: HomeFilled, text: 'AI视频', style: 'fif' }
},
{
href: '#aibg',
key: '6',
title: { icon: HourglassFilled, text: 'AI办公', style: 'six' }
},
{
href: '#aiszr',
key: '7',
title: { icon: IdcardFilled, text: 'AI数字人', style: 'seven' }
},
{
href: '#aijy',
key: '8',
title: { icon: IeCircleFilled, text: 'AI教育', style: 'egiht' }
},
{
href: '#aibc',
key: '9',
title: { icon: InsuranceFilled, text: 'AI编程', style: 'nine' }
},
{
href: '#aiyp',
key: '10',
title: { icon: MedicineBoxFilled, text: 'AI音频', style: 'ten' }
},
];
const useStyles = createStyles(({css}) => ({
actionItem: css`
@ -187,7 +240,7 @@ const useStyles = createStyles(({css}) => ({
`,
}))
const getContainer = () => document.querySelector("#fileRight")
const getContainer = () => document.querySelector("#fileRight") as HTMLElement;
const handleClickCard = (e) => {
window.open(e.website, '_blank');
}
@ -200,11 +253,11 @@ const onClickAncho = (e) => {
// block: 'start'
// })
}
const ApplicationSet = memo<PropsWithChildren>(( ) => {
const ApplicationSet = memo(() => {
const { styles, cx } = useStyles()
const [val, setVal] = useState("AI")
const [achVal, setAchVal] = useState("#rmtj")
const [stData, setStData] = useState("")
const [val, setVal] = useState("AI");
const [achVal, setAchVal] = useState("#rmtj");
const [stData, setStData] = useState<Record<string, any[]>>({});
useEffect(() => {
const fetchData = () => {
request({
@ -230,6 +283,48 @@ const ApplicationSet = memo<PropsWithChildren>(( ) => {
setAchVal(e)
}
// 通用卡片渲染函数
const renderCards = (data, onClick = handleClickCard) => {
return data?.map((e) => (
<Card
actions={[
<span className={styles.actionSpan} key={e.id} onClick={() => onClick(e)}>
<RightCircleOutlined className={styles.rightCo}/>
</span>
]}
className={cx(styles.card)}
key={e.id}
>
<Card.Meta
avatar={<Image alt="" className={styles.avImg} preview={false} src={e.iconurl}/>}
className={styles.cardMeta}
description={<span className={styles.desText}>{e.remark}</span>}
title={<span className={styles.title}>{e.name}</span>}
/>
</Card>
));
}
// 渲染 Anchor 项目
const renderAnchorItems = () => {
return anchorItems.map(item => {
const IconComponent = item.title.icon;
const isActive = achVal === item.href;
const activeStyleClass = `sele${item.title.style.charAt(0).toUpperCase() + item.title.style.slice(1)}`;
return {
href: item.href,
key: item.key,
title: (
<div className={cx(styles.actionItem, isActive && styles[activeStyleClass])}>
<IconComponent className={cx(styles.actionItemIcon, styles[item.title.style])}/>
{item.title.text}
</div>
),
};
});
}
return (
<>
<div className={cx(styles.topBtn)}>
@ -242,68 +337,7 @@ const ApplicationSet = memo<PropsWithChildren>(( ) => {
<Anchor
affix={false}
getContainer={getContainer}
items={[
{
href: '#rmtj',
key: '1',
title: <div className={cx(styles.actionItem, achVal === '#rmtj' && styles.seleFir)}><LikeFilled
className={cx(styles.actionItemIcon, styles.first)}/></div>,
},
{
href: '#ailt',
key: '2',
title: <div className={cx(styles.actionItem, achVal === '#ailt' && styles.seleSec)}><WechatFilled
className={cx(styles.actionItemIcon, styles.sec)}/>AI</div>,
},
{
href: '#aixz',
key: '3',
title: <div className={cx(styles.actionItem, achVal === '#aixz' && styles.seleThd)}><CopyFilled
className={cx(styles.actionItemIcon, styles.thd)}/>AI</div>,
},
{
href: '#aihh',
key: '4',
title: <div className={cx(styles.actionItem, achVal === '#aihh' && styles.seleFou)}>
<DribbbleSquareFilled className={cx(styles.actionItemIcon, styles.four)}/>AI</div>,
},
{
href: '#aisp',
key: '5',
title: <div className={cx(styles.actionItem, achVal === '#aisp' && styles.seleFif)}><HomeFilled
className={cx(styles.actionItemIcon, styles.fif)}/>AI</div>,
},
{
href: '#aibg',
key: '6',
title: <div className={cx(styles.actionItem, achVal === '#aibg' && styles.seleSix)}><HourglassFilled
className={cx(styles.actionItemIcon, styles.six)}/>AI</div>,
},
{
href: '#aiszr',
key: '7',
title: <div className={cx(styles.actionItem, achVal === '#aiszr' && styles.seleSeve)}><IdcardFilled
className={cx(styles.actionItemIcon, styles.seven)}/>AI</div>,
},
{
href: '#aijy',
key: '8',
title: <div className={cx(styles.actionItem, achVal === '#aijy' && styles.seleEight)}><IeCircleFilled
className={cx(styles.actionItemIcon, styles.egiht)}/>AI</div>,
},
{
href: '#aibc',
key: '9',
title: <div className={cx(styles.actionItem, achVal === '#aibc' && styles.seleNine)}><InsuranceFilled
className={cx(styles.actionItemIcon, styles.nine)}/>AI</div>,
},
{
href: '#aiyp',
key: '10',
title: <div className={cx(styles.actionItem, achVal === '#aiyp' && styles.seleTen)}><MedicineBoxFilled
className={cx(styles.actionItemIcon, styles.ten)}/>AI</div>,
},
]}
items={renderAnchorItems()}
onChange={(e) => onChangeAnchor(e)}
onClick={onClickAncho}
style={{ background: "#fff", width: '200px' }}
@ -313,240 +347,61 @@ const ApplicationSet = memo<PropsWithChildren>(( ) => {
<div id="rmtj">
<div className={cx(styles.leftTitle)}></div>
<div>
{
stData['rmtj'] && stData['rmtj'].map((e) => {
return (
<Card
actions={[<span className={styles.actionSpan} key={e.id} onClick={() => handleClickCard(e)}><RightCircleOutlined
className={styles.rightCo}/></span>]}
className={cx(styles.card)}
key={e.id}
>
<Card.Meta
avatar={<Image alt="" className={styles.avImg} preview={false} src={e.iconurl}/>}
className={styles.cardMeta}
description={<span className={styles.desText}>{e.remark}</span>}
title={<span className={styles.title}>{e.name}</span>}
/>
</Card>
)
})
}
{renderCards(stData['rmtj'])}
</div>
</div>
<div id="ailt">
<div className={cx(styles.leftTitle)}>AI</div>
<div>
{
stData['chat'] && stData['chat'].map((e) => {
return (
<Card
actions={[<span className={styles.actions} key={e.id} onClick={() => handleClickCard(e)}><RightCircleOutlined className={styles.rightCo}/></span>]}
className={cx(styles.card)}
key={e.id}
>
<Card.Meta
avatar={<Image alt="" className={styles.avImg} preview={false} src={e.iconurl}/>}
className={styles.cardMeta}
description={<span className={styles.desText}>{e.remark}</span>}
title={<span className={styles.title}>{e.name}</span>}
/>
</Card>
)
})
}
{renderCards(stData['chat'])}
</div>
</div>
<div id="aixz">
<div className={cx(styles.leftTitle)}>AI</div>
<div>
{
stData['writing'] && stData['writing'].map((e) => {
return (
<Card
actions={[<span className={styles.actionSpan} key={e.id} onClick={() => handleClickCard(e)}><RightCircleOutlined
className={styles.rightCo}/></span>]}
className={cx(styles.card)}
key={e.id}
>
<Card.Meta
avatar={<Image alt="" className={styles.avImg} preview={false} src={e.iconurl}/>}
className={styles.cardMeta}
description={<span className={styles.desText}>{e.remark}</span>}
title={<span className={styles.title}>{e.name}</span>}
/>
</Card>
)
})
}
{renderCards(stData['writing'])}
</div>
</div>
<div id="aihh">
<div className={cx(styles.leftTitle)}>AI</div>
<div>
{
stData['conversation'] && stData['conversation'].map((e) => {
return (
<Card
actions={[<span className={styles.actionSpan} key={e.id} onClick={() => handleClickCard(e)}><RightCircleOutlined
className={styles.rightCo}/></span>]}
className={cx(styles.card)}
key={e.id}
>
<Card.Meta
avatar={<Image alt="" className={styles.avImg} preview={false} src={e.iconurl}/>}
className={styles.cardMeta}
description={<span className={styles.desText}>{e.remark}</span>}
title={<span className={styles.title}>{e.name}</span>}
/>
</Card>
)
})
}
{renderCards(stData['conversation'])}
</div>
</div>
<div id="aisp">
<div className={cx(styles.leftTitle)}>AI</div>
<div>
{
stData['video'] && stData['video'].map((e) => {
return (
<Card
actions={[<span className={styles.actionSpan} key={e.id} onClick={() => handleClickCard(e)}><RightCircleOutlined
className={styles.rightCo}/></span>]}
className={cx(styles.card)}
key={e.id}
>
<Card.Meta
avatar={<Image alt="" className={styles.avImg} preview={false} src={e.iconurl}/>}
className={styles.cardMeta}
description={<span className={styles.desText}>{e.remark}</span>}
title={<span className={styles.title}>{e.name}</span>}
/>
</Card>
)
})
}
{renderCards(stData['video'])}
</div>
</div>
<div id="aibg">
<div className={cx(styles.leftTitle)}>AI</div>
<div>
{
stData['office'] && stData['office'].map((e) => {
return (
<Card
actions={[<span className={styles.actionSpan} key={e.id} onClick={() => handleClickCard(e)}><RightCircleOutlined
className={styles.rightCo}/></span>]}
className={cx(styles.card)}
key={e.id}
>
<Card.Meta
avatar={<Image alt="" className={styles.avImg} preview={false} src={e.iconurl}/>}
className={styles.cardMeta}
description={<span className={styles.desText}>{e.remark}</span>}
title={<span className={styles.title}>{e.name}</span>}
/>
</Card>
)
})
}
{renderCards(stData['office'])}
</div>
</div>
<div id="aiszr">
<div className={cx(styles.leftTitle)}>AI</div>
<div>
{
stData['digitalPeople'] && stData['digitalPeople'].map((e) => {
return (
<Card
actions={[<span className={styles.actionSpan} key={e.id} onClick={() => handleClickCard(e)}><RightCircleOutlined
className={styles.rightCo}/></span>]}
className={cx(styles.card)}
key={e.id}
>
<Card.Meta
avatar={<Image alt="" className={styles.avImg} preview={false} src={e.iconurl}/>}
className={styles.cardMeta}
description={<span className={styles.desText}>{e.remark}</span>}
title={<span className={styles.title}>{e.name}</span>}
/>
</Card>
)
})
}
{renderCards(stData['digitalPeople'])}
</div>
</div>
<div id="aijy">
<div className={cx(styles.leftTitle)}>AI</div>
<div>
{
stData['education'] && stData['education'].map((e) => {
return (
<Card
actions={[<span className={styles.actionSpan} key={e.id} onClick={() => handleClickCard(e)}><RightCircleOutlined
className={styles.rightCo}/></span>]}
className={cx(styles.card)}
key={e.id}
>
<Card.Meta
avatar={<Image alt="" className={styles.avImg} preview={false} src={e.iconurl}/>}
className={styles.cardMeta}
description={<span className={styles.desText}>{e.remark}</span>}
title={<span className={styles.title}>{e.name}</span>}
/>
</Card>
)
})
}
{renderCards(stData['education'])}
</div>
</div>
<div id="aibc">
<div className={cx(styles.leftTitle)}>AI</div>
<div>
{
stData['programming'] && stData['programming'].map((e) => {
return (
<Card
actions={[<span className={styles.actionSpan} key={e.id} onClick={() => handleClickCard(e)}><RightCircleOutlined
className={styles.rightCo}/></span>]}
className={cx(styles.card)}
key={e.id}
>
<Card.Meta
avatar={<Image alt="" className={styles.avImg} preview={false} src={e.iconurl}/>}
className={styles.cardMeta}
description={<span className={styles.desText}>{e.remark}</span>}
title={<span className={styles.title}>{e.name}</span>}
/>
</Card>
)
})
}
{renderCards(stData['programming'])}
</div>
</div>
<div id="aiyp">
<div className={cx(styles.leftTitle)}>AI</div>
<div>
{
stData['audio'] && stData['audio'].map((e) => {
return (
<Card
actions={[<span className={styles.actionSpan} key={e.id} onClick={() => handleClickCard(e)}><RightCircleOutlined
className={styles.rightCo}/></span>]}
className={cx(styles.card)}
key={e.id}
>
<Card.Meta
avatar={<Image alt="" className={styles.avImg} preview={false} src={e.iconurl}/>}
className={styles.cardMeta}
description={<span className={styles.desText}>{e.remark}</span>}
title={<span className={styles.title}>{e.name}</span>}
/>
</Card>
)
})
}
{renderCards(stData['audio'])}
</div>
</div>
</div>

@ -22,12 +22,6 @@ const SettingButton = memo<{ mobile?: boolean }>(({ mobile }) => {
return (
<>
{/*<ActionIcon*/}
{/* icon={AlignJustify}*/}
{/* onClick={() => openChatSettings()}*/}
{/* size={mobile ? MOBILE_HEADER_ICON_SIZE : DESKTOP_HEADER_ICON_SIZE}*/}
{/* title={t('header.session', { ns: 'setting' })}*/}
{/*/>*/}
<Button onClick={() => openChatSettings()} shape="round" style={{borderColor: '#2E62FF', color: "#2E62FF", fontSize: '13px',height: '30px' }}></Button>
<AgentSettings key={id} />
</>

@ -26,12 +26,6 @@ export const generateMetadata = async (props: DynamicLayoutProps) => {
const Page = async (props: DynamicLayoutProps) => {
const { hideDocs, showChangelog } = serverFeatureFlags();
const { isMobile, locale } = await RouteVariants.getVariantsFromProps(props);
// const { t } = await translation('metadata', locale);
// const ld = ldModule.generate({
// description: t('chat.description', { appName: BRANDING_NAME }),
// title: t('chat.title', { appName: BRANDING_NAME }),
// url: '/chat',
// });
return (
<>
@ -46,6 +40,4 @@ const Page = async (props: DynamicLayoutProps) => {
);
};
// Page.displayName = 'Chat';
export default Page;

@ -8,9 +8,6 @@ const DesktopLayout = ({ children }: PropsWithChildren) => {
<>
<Header />
<PanelBody>{children}</PanelBody>
{/* ↓ cloud slot ↓ */}
{/* ↑ cloud slot ↑ */}
</>
);
};

@ -20,9 +20,6 @@ const Layout = ({ children, session }: LayoutProps) => {
</Flexbox>
</Flexbox>
<InitClientDB bottom={60} />
{/* ↓ cloud slot ↓ */}
{/* ↑ cloud slot ↑ */}
</>
);
};

@ -3,17 +3,16 @@ import { Skeleton, Typography, App } from 'antd';
import { createStyles } from 'antd-style';
import { startCase } from 'lodash-es';
import dynamic from 'next/dynamic';
import qs from 'query-string';
import { CSSProperties, memo, useState } from 'react';
import { Center, Flexbox } from 'react-layout-kit';
import urlJoin from 'url-join';
import { useTranslation } from 'react-i18next';
import { StarOutlined } from '@ant-design/icons';
import { DiscoverPlugintem } from '@/types/discover';
import { useUserStore } from '@/store/user';
import request from '@/app/request';
import CardBanner from '../../../components/CardBanner';
import { useCategoryItem } from './useCategory';
import { StarOutlined } from '@ant-design/icons';
import request from '@/app/request';
const Link = dynamic(() => import('next/link'), {
loading: () => <Skeleton.Button size={'small'} style={{ height: 22 }} />,
ssr: false,
@ -81,60 +80,84 @@ interface PluginCardProps
style?: CSSProperties;
variant?: 'default' | 'compact';
}
const getUserId = (s: { user?: { id?: any } }) => s.user?.id
interface ApiResponse {
code: number;
message?: string;
}
const PluginCard = memo<PluginCardProps>(
({ className, showCategory, meta, createdAt, author, variant, style, href,...rest }) => {
({ className, showCategory, meta, createdAt, author, variant, style, href, ...rest }) => {
const { avatar, title, description, tags = [], category } = meta;
const { homepage, identifier, schemaVersion, status, classify, manifest,locale } = {...rest}
const {
homepage,
identifier,
schemaVersion,
status,
classify,
manifest,
locale
} = rest as any; // 临时使用 any 类型,需要后续优化类型定义
const categoryItem = useCategoryItem(category, 12);
const { cx, styles, theme } = useStyles();
const { t } = useTranslation('discover');
const { message } = App.useApp();
const isCompact = variant === 'compact';
const userId = getUserId(useUserStore.getState())
const [val, setVal] = useState('')
const handleCollect = (e) => {
// console.log(e,"8844848")
e.preventDefault()
e.stopPropagation()
const userId = useUserStore(state => state.user?.id);
const [isCollected, setIsCollected] = useState<string>('');
const handleCollect = (e: React.MouseEvent<HTMLDivElement>): void => {
e.preventDefault();
e.stopPropagation();
const params = {
"author": author,
"avatar": avatar,
"category": category,
"createdAt": createdAt,
"description": description,
"homepage": homepage,
"identifier": identifier,
"locale": locale,
"manifest": manifest,
"schemaVersion": schemaVersion,
"tags": tags.join(','),
"title": title,
"userid": userId,
}
request({
author,
avatar,
category,
createdAt,
description,
homepage,
identifier,
locale,
manifest,
schemaVersion,
tags: tags.join(','),
title,
userid: userId,
};
request<ApiResponse>({
data: params,
method: "post",
url: "/flxai/api/robot/appaiplugin",
}).then(response => {
if (response.code === 0) {
message.success(t('collectSuccess'));
setVal("1")
setIsCollected("1");
}
}).catch(error => {
console.error('Error fetching data:', error);
})
}
};
const renderElement = () => {
if (classify !== 'collect') {
if(status === "1" || val === "1") {
return <div className={styles.collectBtn} onClick={(e) => {e.stopPropagation()}}><StarOutlined style={{color: '#FFAD01'}}/></div>
} else {
return <div className={styles.collectBtn} onClick={(e) => handleCollect(e)} style={{background: '#F1F1F1'}}><StarOutlined style={{color: '#D6D6D6'}}/></div>
}
} else {
return;
if (classify === 'collect') {
return null;
}
const isActive = status === "1" || isCollected === "1";
const handleClick = isActive
? (e: React.MouseEvent) => e.stopPropagation()
: handleCollect;
return (
<div
className={styles.collectBtn}
onClick={handleClick}
style={isActive ? undefined : { background: '#F1F1F1' }}
>
<StarOutlined style={{ color: isActive ? '#FFAD01' : '#D6D6D6' }} />
</div>
);
};
return (
<Flexbox className={cx(styles.container, className)} gap={24}>
@ -180,21 +203,24 @@ const PluginCard = memo<PluginCardProps>(
<Paragraph className={styles.desc} ellipsis={{ rows: 2 }}>
{description}
</Paragraph>
<Flexbox gap={6} horizontal style={{ flexWrap: 'wrap',justifyContent: 'space-between' }}>
<div onClick={(e) => {e.stopPropagation()}} style={{width: '85%'}}>
<Flexbox gap={6} horizontal style={{ flexWrap: 'wrap', justifyContent: 'space-between' }}>
<div
onClick={(e) => e.stopPropagation()}
style={{ width: '85%' }}
>
{showCategory && categoryItem ? (
<Tag icon={categoryItem.icon} style={{ margin: '0 5' }}>
<Tag icon={categoryItem.icon} style={{ margin: '0 5px' }}>
{categoryItem.label}
</Tag>
) : (
tags
.slice(0, 4)
.filter(Boolean)
.map((tag: string, index) => {
return (
<Tag key={index} style={{ margin: '0 5' }}>{startCase(tag).trim()}</Tag>
);
})
.map((tag: string, index: number) => (
<Tag key={index} style={{ margin: '0 5px' }}>
{startCase(tag).trim()}
</Tag>
))
)}
</div>
{renderElement()}
@ -205,4 +231,6 @@ const PluginCard = memo<PluginCardProps>(
},
);
PluginCard.displayName = 'PluginCard';
export default PluginCard;

@ -14,75 +14,86 @@ import VirtuosoGridList from '../../../components/VirtuosoGridList';
import Card from './Card';
import request from '@/app/request';
import { useUserStore } from '@/store/user';
export interface ListProps {
category?: string;
items: DiscoverPlugintem[];
mobile?: boolean;
searchKeywords?: string;
}
const getUserId = (s: { user?: { id?: any } }) => s.user?.id
const COLLECT_CATEGORY = "collect";
const API_URL = "/flxai/api/robot/appaiplugin/getAllAiPlugin";
const List = memo<ListProps>(({ category, mobile, searchKeywords, items = [] }) => {
const { t } = useTranslation('discover');
const router = useRouter()
const [stData, setStData] = useState(items)
const router = useRouter();
const [stData, setStData] = useState<DiscoverPlugintem[]>(items);
const recentLength = mobile ? 4 : 8;
const userId = getUserId(useUserStore.getState())
const userId = useUserStore((s) => s.user?.id);
const { all, recent, last } = useMemo(() => {
return {
all: stData,
last: stData.slice(recentLength),
recent: stData.slice(0, recentLength),
};
}, [stData, mobile]);
}, [stData, recentLength]);
useEffect(() => {
const fetchData = async() => {
if(category === "collect") {
const fetchData = async () => {
try {
const res = await request({
method: "get",
params: {
userid: userId
},
url: "/flxai/api/robot/appaiplugin/getAllAiPlugin",
})
setStData(res?.data.map((item)=> {
item.classify = 'collect';
return item
}))
url: API_URL,
});
if (category === COLLECT_CATEGORY) {
setStData(res?.data?.map((item) => ({
...item,
classify: 'collect'
})) || []);
} else {
const res = await request({
method: "get",
params: {
userid: userId
},
url: "/flxai/api/robot/appaiplugin/getAllAiPlugin",
})
const array2Object = {};
const array2Object: Record<string, any> = {};
res?.data?.forEach((item) => {
array2Object[item.identifier] = item;
});
// console.log('chajian22222222222222222222222222222--------------')
setStData(stData.map((item)=> {
setStData(prevData => prevData.map((item) => {
const matchingItem = array2Object[item.identifier];
if (matchingItem) {
matchingItem.status = '1';
return matchingItem
return { ...matchingItem, status: '1' };
} else {
item.status = '0';
return item
return { ...item, status: '0' };
}
}));
}
}))
} catch (error) {
console.error('Error fetching data:', error);
}
};
fetchData();
}, []); // 空数组[]意味着仅在组件挂载时调用一次
}, [category, userId]);
// console.log('chajian999999999999999--------------',stData)
const handleClickCard = (item: DiscoverPlugintem) => {
router.push(urlJoin('/discover/plugin/', item.identifier));
};
const handleClickCard = (item) => {
console.log(item)
router.push(urlJoin('/discover/plugin/', item.identifier))
}
// 可复用的卡片渲染组件
const renderCard = (item: DiscoverPlugintem, showCategory: boolean = false, variant?: string) => (
<div onClick={() => handleClickCard(item)} key={item.identifier}>
<Card
key={item.identifier}
showCategory={showCategory}
variant={variant as any}
href={`/discover/plugin/${item.identifier}`}
{...item}
/>
</div>
);
if (searchKeywords) {
if (!items || items?.length === 0) return <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />;
return (
@ -91,16 +102,7 @@ const List = memo<ListProps>(({ category, mobile, searchKeywords, items = [] })
<VirtuosoGridList
data={all}
initialItemCount={24}
itemContent={(_, item) => (
<div onClick={() => handleClickCard(item)} key={item.identifier}>
<Card
showCategory
variant={'compact'}
{...item}
key={item.identifier}
/>
</div>
)}
itemContent={(_, item) => renderCard(item, true, 'compact')}
style={{
minHeight: '75vh',
}}
@ -111,41 +113,26 @@ const List = memo<ListProps>(({ category, mobile, searchKeywords, items = [] })
return (
<div style={{ marginLeft: '200px'}}>
{category === "collect"?(
{category === COLLECT_CATEGORY ? (
<>
<Title tag={all.length}></Title>
<Grid maxItemWidth={280} rows={4}>
{all?.map((item,index) => (
// <div onClick={() => handleClickCard(item)}>测试接口</div>
<div onClick={() => handleClickCard(item)} key={item.identifier}>
<Card key={item.identifier} showCategory={!category} {...item} />
</div>
))}
{all?.map((item) => renderCard(item, !category))}
</Grid>
</>
):(
) : (
<>
<Title>{t('plugins.recentSubmits')}</Title>
<Title>{t('discover:plugins.recentSubmits')}</Title>
<Grid maxItemWidth={280} rows={4}>
{recent?.map((item) => (
// <Card key={item.identifier} onClick={() => handleClickCard(item)} showCategory={!category} {...item} />
<div onClick={() => handleClickCard(item)} key={item.identifier}>
<Card key={item.identifier} showCategory={!category} {...item} />
</div>
))}
{recent?.map((item) => renderCard(item, !category))}
</Grid>
{last && last?.length > 0 && (
<>
<Title tag={last.length}>{t('plugins.list')}</Title>
<Title tag={last.length}>{t('discover:plugins.list')}</Title>
<VirtuosoGridList
data={last}
initialItemCount={12}
itemContent={(_, item) => (
// <Card key={item.identifier} onClick={() => handleClickCard(item)} showCategory={!category} variant={'compact'} {...item} />
<div onClick={() => handleClickCard(item)} key={item.identifier}>
<Card key={item.identifier} showCategory={!category} {...item} />
</div>
)}
itemContent={(_, item) => renderCard(item, !category)}
style={{
minHeight: '75vh',
}}

@ -1,5 +1,5 @@
import { useMemo, ReactElement } from 'react';
import { Icon } from '@lobehub/ui';
import { useTheme } from 'antd-style';
import {
Gamepad2,
ImagePlay,
@ -12,75 +12,57 @@ import {
Umbrella,
Star
} from 'lucide-react';
import { useTranslation } from 'react-i18next';
import { PluginCategory } from '@/types/discover';
import { ICON_SIZE } from '../../../components/CategoryMenu';
export const useCategory = (fontsize?: number) => {
const theme = useTheme();
interface CategoryItem {
icon: ReactElement;
key: PluginCategory;
label: string;
}
const { t } = useTranslation('discover');
// 常量配置,避免重复创建
const CATEGORY_CONFIGS = [
{ key: PluginCategory.All, color: '#003BFF', icon: LayoutPanelTop, labelKey: 'all' },
{ key: PluginCategory.Collect, color: '#FFAD01', icon: Star, labelKey: 'collect' },
{ key: PluginCategory.MediaGenerate, color: '#FF4D4D', icon: ImagePlay, labelKey: 'media-generate' },
{ key: PluginCategory.WebSearch, color: '#5BD941', icon: ScanSearch, labelKey: 'web-search' },
{ key: PluginCategory.StocksFinance, color: '#FF34AD', icon: Receipt, labelKey: 'stocks-finance' },
{ key: PluginCategory.Tools, color: '#FF9B06', icon: PocketKnife, labelKey: 'tools' },
{ key: PluginCategory.LifeStyle, color: '#0095FF', icon: Umbrella, labelKey: 'life-style' },
{ key: PluginCategory.ScienceEducation, color: '#00B0CB', icon: MicroscopeIcon, labelKey: 'science-education' },
{ key: PluginCategory.Social, color: '#E138FF', icon: TwitterIcon, labelKey: 'social' },
{ key: PluginCategory.GamingEntertainment, color: '#0CD66D', icon: Gamepad2, labelKey: 'gaming-entertainment' },
] as const;
export const useCategory = (fontsize?: number): CategoryItem[] => {
const size = fontsize ? { fontSize: fontsize } : ICON_SIZE;
return [
{
icon: <Icon color="#003BFF" icon={LayoutPanelTop} size={size} />,
key: PluginCategory.All,
label: t('category.plugin.all'),
},
{
icon: <Icon color="#FFAD01" icon={Star} size={size} />,
key: PluginCategory.Collect,
label: t('category.plugin.collect'),
},
{
icon: <Icon color="#FF4D4D" icon={ImagePlay} size={size} />,
key: PluginCategory.MediaGenerate,
label: t('category.plugin.media-generate'),
},
{
icon: <Icon color="#5BD941" icon={ScanSearch} size={size} />,
key: PluginCategory.WebSearch,
label: t('category.plugin.web-search'),
},
{
icon: <Icon color="#FF34AD" icon={Receipt} size={size} />,
key: PluginCategory.StocksFinance,
label: t('category.plugin.stocks-finance'),
},
{
icon: <Icon color="#FF9B06" icon={PocketKnife} size={size} />,
key: PluginCategory.Tools,
label: t('category.plugin.tools'),
},
{
icon: <Icon color="#0095FF" icon={Umbrella} size={size} />,
key: PluginCategory.LifeStyle,
label: t('category.plugin.life-style'),
},
{
icon: <Icon color="#00B0CB" icon={MicroscopeIcon} size={size} />,
key: PluginCategory.ScienceEducation,
label: t('category.plugin.science-education'),
},
{
icon: <Icon color="#E138FF" icon={TwitterIcon} size={size} />,
key: PluginCategory.Social,
label: t('category.plugin.social'),
},
{
icon: <Icon color="#0CD66D" icon={Gamepad2} size={size} />,
key: PluginCategory.GamingEntertainment,
label: t('category.plugin.gaming-entertainment'),
},
];
return useMemo(() =>
CATEGORY_CONFIGS.map(({ key, color, icon: IconComponent, labelKey }) => ({
icon: <Icon color={color} icon={IconComponent} size={size} />,
key,
label: `插件${labelKey === 'all' ? '全部' :
labelKey === 'collect' ? '收藏' :
labelKey === 'media-generate' ? '媒体生成' :
labelKey === 'web-search' ? '网络搜索' :
labelKey === 'stocks-finance' ? '股票金融' :
labelKey === 'tools' ? '工具' :
labelKey === 'life-style' ? '生活方式' :
labelKey === 'science-education' ? '科学教育' :
labelKey === 'social' ? '社交' :
'游戏娱乐'}`,
})),
[size]
);
};
export const useCategoryItem = (key?: PluginCategory, fontsize?: number) => {
export const useCategoryItem = (key?: PluginCategory, fontsize?: number): CategoryItem | undefined => {
const items = useCategory(fontsize);
if (!key) return;
return useMemo(() => {
if (!key) return undefined;
return items.find((item) => item.key === key);
}, [items, key]);
};

@ -1,5 +1,4 @@
import { Col, Form, Input, Modal, Row } from 'antd';
import React from 'react';
import { Form, Input, Modal } from 'antd';
import { createStyles } from 'antd-style';
const { TextArea } = Input;
@ -54,20 +53,12 @@ const KnowledgeCreateForm = (props) => {
return (
<Modal className={styles.modalStyle} destroyOnClose onCancel={() => handleModalVisible(false)} onOk={okHandle} open={open} title="创建知识库" >
<Form className={styles.formStyle} form={form}>
<Row gutter={{ lg: 24, md: 8, xl: 48 }}>
<Col md={24} sm={24}>
<FormItem label="知识库名称" name="name" rules={[{ message: '请输入!', min: 1, required: true}]}>
<FormItem label="知识库名称" name="name" rules={[{ message: '请输入知识库名称!', required: true}]}>
<Input placeholder="请输入" />
</FormItem>
</Col>
</Row>
<Row gutter={{ lg: 24, md: 8, xl: 48 }}>
<Col md={24} sm={24}>
<FormItem label="知识库描述" name="description" rules={[{ message: '请输入!', min: 1 , required: false}]}>
<FormItem label="知识库描述" name="description">
<TextArea maxLength={255} placeholder="请输入" rows={4} />
</FormItem>
</Col>
</Row>
</Form>
</Modal>
);

@ -1,18 +1,14 @@
'use client';
// import { Avatar, Icon } from '@lobehub/ui';
import { Card, Button, List, Image, Flex, Tabs, Table, Space, Upload, Input, Select, App, Popconfirm, Empty,Col, Row } from "antd";
import { Card, Button, List, Image, Flex, Tabs, Table, Space, Upload, Input, App, Empty } from "antd";
import { CloudUploadOutlined} from '@ant-design/icons';
import { createStyles } from 'antd-style';
import { memo, useState, useEffect } from 'react';
import { memo, useState } from 'react';
import { useTranslation } from 'react-i18next';
// import { Flexbox } from 'react-layout-kit';
import KnowledgeCreateForm from './KnowledgeCreateForm'; //新增表单
import KnowledgeUpdateForm from './KnowledgeUpdateForm'; //编辑表单
// import request from '@/app/api/request';
import { Virtuoso } from 'react-virtuoso';
import { useKnowledgeBaseStore } from '@/store/knowledgeBase';
import DragUpload from '@/components/DragUpload';
import { useFileStore } from '@/store/file';
import UploadDock from '@/features/FileManager/UploadDock';
import { useQueryState } from 'nuqs';
@ -21,7 +17,6 @@ import dayjs from 'dayjs';
import relativeTime from 'dayjs/plugin/relativeTime';
import { SortType } from '@/types/files';
import { useRouter } from 'next/navigation';
import { lambdaQuery } from '@/libs/trpc/client';
import SkeletonLoading from './Loading';
import isEqual from 'fast-deep-equal';
const { Search } = Input;
@ -105,13 +100,6 @@ const useStyles = createStyles(({ css }) => ({
color: #fff !important;
}
`,
checkItem: css`
margin-top: 15px;
`,
checkTitle: css`
color: #0044FF;
margin-right: 5px;
`,
dec: css`
color: #666666;
`,
@ -149,11 +137,6 @@ const useStyles = createStyles(({ css }) => ({
checkContent: css`
height: calc(100% - 30px) !important;
`,
formStyle: css`
.ant-form-item-label {
width: 100px !important;
}
`,
iconImg: css`
width: 48px;
height: 48px;
@ -181,38 +164,6 @@ const useStyles = createStyles(({ css }) => ({
listStyle: css`
border: none !important;
`,
modalStyle: css`
.ant-modal-body {
padding: 20px 0;
}
.ant-modal-footer {
.ant-btn:nth-child(1) {
width: 73px;
height: 36px;
border-radius: 4px;
opacity: 1;
background: #EBF4FF;
box-sizing: border-box;
border: 1px solid #B8D8FF;
color: #177FFF;
}
.ant-btn:nth-child(2) {
width: 73px;
height: 36px;
border-radius: 4px;
opacity: 1;
background: #80B9FF;
}
}
`,
previewContainer: css`
width: 100%;
height: 100%;
background: #F7F7F7;
text-align: center;
padding: 30% 0;
`,
searchStyle: css`
.ant-input-affix-wrapper {
border: none;
@ -229,19 +180,6 @@ const useStyles = createStyles(({ css }) => ({
}
}
`,
selectStyle: css`
.ant-select-selector {
border: none !important;
background-color: #F0F3FD !important;
}
.ant-select-selection-item {
color: #4A73FF
}
.ant-select-arrow {
color: #2959FF
}
margin-bottom: 15px;
`,
tabStyle: css`
.ant-tabs-tab {
margin-right: 40px !important;
@ -280,19 +218,6 @@ const useStyles = createStyles(({ css }) => ({
font-size: 18px;
font-weight: 700;
`,
viewButtonStyle: css`
font-size: 16px;
color: #0044FF;
border-color: #BECFFF;
padding: 10px 15px !important;
box-sizing: border-box;
background-color: rgba(255, 255, 255, 0) !important;
&:hover {
border-color: #BECFFF !important;
color: #0044FF !important;
background-color: rgba(255, 255, 255, 0) !important;
}
`,
}));
const KnowledgeList = memo<{ mobile?: boolean }>(() => {
@ -332,20 +257,15 @@ const KnowledgeList = memo<{ mobile?: boolean }>(() => {
clearOnDefault: true,
defaultValue: SortType.Desc,
});
const [useFetchFileManage, pushDockFileList, removeFile, semanticSearch, parseFiles] = useFileStore((s) => [
const [useFetchFileManage, pushDockFileList, removeFile, semanticSearch] = useFileStore((s) => [
s.useFetchFileManage,
s.pushDockFileList,
s.removeFileItem,
s.semanticSearch,
s.parseFilesToChunks,
]);
const isSimilaritySearching = useFileStore((s) => s.isSimilaritySearching);
const dataSource = useFileStore((s) => s.similaritySearchChunks, isEqual);
// useEffect(() => {
// }, []); // 空数组[]意味着仅在组件挂载时调用一次
console.log(knowledgeBaseId,sortType,sorter,'--------44444444444444444------------')
const { data: dataAllFile } = useFetchFileManage({
// category,
knowledgeBaseId,
@ -354,8 +274,6 @@ const KnowledgeList = memo<{ mobile?: boolean }>(() => {
sorter,
...viewConfig,
});
console.log(data,'999999999999999999')
console.log(dataAllFile,'8888888888888888888888')
const columnsAll = [
{
@ -398,21 +316,6 @@ const KnowledgeList = memo<{ mobile?: boolean }>(() => {
</Space>
),
},
// {
// dataIndex: 'tokens',
// key: 'tokens',
// title: 'tokens',
// },
// {
// dataIndex: 'num',
// key: 'num',
// title: '知识库条数',
// },
// {
// dataIndex: 'status',
// key: 'status',
// title: '状态',
// },
{
dataIndex: 'createdAt',
key: 'createdAt',
@ -444,13 +347,11 @@ const KnowledgeList = memo<{ mobile?: boolean }>(() => {
const handleChange = (e) => {
console.log(e.target.value,'--9999222222----')
setTestContent(e.target.value)
}
const handleCheck = () => {
if(testContent != "") semanticSearch(testContent, "");
console.log(testContent,'--635353535355----')
}
const handleAdd = async(fields) => {
@ -504,17 +405,14 @@ const KnowledgeList = memo<{ mobile?: boolean }>(() => {
const onSearch = (val) => {
setQueryData(val)
setTabsVal("1")
console.log(val,'333333-------------------')
}
const handleTabChange = async(val) => {
console.log(val,"5555")
setTabsVal(val)
if(val == 2) {
await setDocFileData(dataAllFile?.filter((item)=> {
return item.fileType != 'text/csv'
}))
console.log(docFileData,'182828282828288')
} else if(val == 3) {
setQuestionFileData(dataAllFile?.filter((item)=> {
return item.fileType == 'text/csv'
@ -529,12 +427,10 @@ const KnowledgeList = memo<{ mobile?: boolean }>(() => {
}
const handleSelectTab = (val) => {
console.log(val)
setTabValue(val)
}
const handleSelectCard = (val) => {
console.log(val,"5555")
setCardValue(val)
}
@ -599,7 +495,6 @@ const KnowledgeList = memo<{ mobile?: boolean }>(() => {
} else {
fileType = 'csv';
}
console.log(file,"77777777777777777")
const isJpgOrPng = fileType.includes(file.name?.match(/(.*)\.(.*)/)[2])
if (!isJpgOrPng) {
message.error(`你只能上传${fileType}文件!`);
@ -650,11 +545,7 @@ const KnowledgeList = memo<{ mobile?: boolean }>(() => {
multiple: true,
name: 'file',
onChange(info) {
console.log(info,"2928282828")
const { status } = info.file;
if (status !== 'uploading') {
console.log(info.file, info.fileList);
}
if (status === 'done') {
message.success(`${info.file.name} file uploaded successfully.`);
} else if (status === 'error') {
@ -662,7 +553,7 @@ const KnowledgeList = memo<{ mobile?: boolean }>(() => {
}
},
onDrop(e) {
console.log('Dropped files', e.dataTransfer.files);
// File drop handler
},
};
if(tabValue === 1) {
@ -719,7 +610,6 @@ const KnowledgeList = memo<{ mobile?: boolean }>(() => {
</div>
)
} else {
console.log(dataSource,'检索测试91919191991199--------------', isSimilaritySearching)
return (
<div style={{height: '100%'}}>
<Flex gap={30} justify='space-between'style={{height: '100%'}}>

@ -1,5 +1,5 @@
import { Col, Form, Input, Modal, Row } from 'antd';
import React from 'react';
import { Form, Input, Modal } from 'antd';
import { useEffect } from 'react';
import { createStyles } from 'antd-style';
const { TextArea } = Input;
@ -42,13 +42,15 @@ const KnowledgeUpdateForm = (props) => {
const { handleUpdate, open, handleUpdateModalVisible, values } = props;
React.useEffect(() => {
useEffect(() => {
if (values) {
form.setFieldsValue({
description: props.values.description,
id: props.values.id,
name: props.values.name,
description: values.description,
id: values.id,
name: values.name,
});
}, []);
}
}, [values, form]);
const handleLocalUpdate = () => {
form.validateFields()
@ -61,20 +63,12 @@ const KnowledgeUpdateForm = (props) => {
return (
<Modal afterClose={() => handleUpdateModalVisible()} className={styles.modalStyle} destroyOnClose onCancel={() => handleUpdateModalVisible(false, values)} onOk={() => handleLocalUpdate()} open={open} title="编辑知识库">
<Form className={styles.formStyle} form={form}>
<Row gutter={{ lg: 24, md: 8, xl: 48 }}>
<Col md={24} sm={24}>
<FormItem label="知识库名称" name="name" rules={[{ message: '请输入!', min: 1, required: true }]}>
<FormItem label="知识库名称" name="name" rules={[{ message: '请输入知识库名称!', required: true }]}>
<Input placeholder="请输入" />
</FormItem>
</Col>
</Row>
<Row gutter={{ lg: 24, md: 8,xl: 48 }}>
<Col md={24} sm={24}>
<FormItem label="知识库描述" name="description" rules={[{ message: '请输入!', min: 1, required: false }]}>
<FormItem label="知识库描述" name="description">
<TextArea maxLength={255} placeholder="请输入" rows={4} />
</FormItem>
</Col>
</Row>
</Form>
</Modal>
);

@ -1,10 +1,6 @@
// import { WelcomeLogo } from '@/components/Branding';
// import StructuredData from '@/components/StructuredData';
import { BRANDING_NAME } from '@/const/branding';
// import { ldModule } from '@/server/ld';
import { metadataModule } from '@/server/metadata';
import { translation } from '@/server/translation';
// import { isMobileDevice } from '@/utils/responsive';
export const generateMetadata = async () => {
const { t } = await translation('metadata');
@ -16,18 +12,9 @@ export const generateMetadata = async () => {
};
const Page = async () => {
// const mobile = isMobileDevice();
// const { t } = await translation('metadata');
// const ld = ldModule.generate({
// description: t('power.description', { appName: BRANDING_NAME }),
// title: t('power.title', { appName: BRANDING_NAME }),
// url: '/power',
// });
return (
<>
{/*<StructuredData ld={ld} />*/}
{/*<WelcomeLogo mobile={mobile} />*/}
<div></div>
</>
);

@ -6,7 +6,6 @@ import { isRtlLang } from 'rtl-detect';
import Analytics from '@/components/Analytics';
import { DEFAULT_LANG } from '@/const/locale';
// import PWAInstall from '@/features/PWAInstall';
import AuthProvider from '@/layout/AuthProvider';
import GlobalProvider from '@/layout/GlobalProvider';
import { Locales } from '@/locales/resources';

Loading…
Cancel
Save