diff --git a/client/src/api/plugins/kb.ts b/client/src/api/plugins/kb.ts index 9dd391d5e..bd117556f 100644 --- a/client/src/api/plugins/kb.ts +++ b/client/src/api/plugins/kb.ts @@ -1,5 +1,5 @@ import { GET, POST, PUT, DELETE } from '../request'; -import type { KbItemType } from '@/types/plugin'; +import type { KbItemType, KbListItemType } from '@/types/plugin'; import { RequestPaging } from '@/types/index'; import { TrainingModeEnum } from '@/constants/plugin'; import { @@ -10,6 +10,7 @@ import { Props as SearchTestProps, Response as SearchTestResponse } from '@/pages/api/openapi/kb/searchTest'; +import { Response as KbDataItemType } from '@/pages/api/plugins/kb/data/getDataById'; export type KbUpdateParams = { id: string; @@ -19,7 +20,7 @@ export type KbUpdateParams = { }; /* knowledge base */ -export const getKbList = () => GET(`/plugins/kb/list`); +export const getKbList = () => GET(`/plugins/kb/list`); export const getKbById = (id: string) => GET(`/plugins/kb/detail?id=${id}`); @@ -59,7 +60,7 @@ export const getTrainingData = (data: { kbId: string; init: boolean }) => }>(`/plugins/kb/data/getTrainingData`, data); export const getKbDataItemById = (dataId: string) => - GET(`/plugins/kb/data/getDataById`, { dataId }); + GET(`/plugins/kb/data/getDataById`, { dataId }); /** * 直接push数据 diff --git a/client/src/components/Icon/icons/light/overview.svg b/client/src/components/Icon/icons/light/overview.svg index fcfb64a38..3d107058e 100644 --- a/client/src/components/Icon/icons/light/overview.svg +++ b/client/src/components/Icon/icons/light/overview.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/client/src/components/Layout/navbar.tsx b/client/src/components/Layout/navbar.tsx index 00204c4f0..ccad6dc3d 100644 --- a/client/src/components/Layout/navbar.tsx +++ b/client/src/components/Layout/navbar.tsx @@ -38,8 +38,8 @@ const Navbar = ({ unread }: { unread: number }) => { label: '知识库', icon: 'dbLight', activeIcon: 'dbFill', - link: `/kb`, - activeLink: ['/kb'] + link: `/kb/list`, + activeLink: ['/kb/list', '/kb/detail'] }, { label: '市场', diff --git a/client/src/pages/api/openapi/kb/searchTest.ts b/client/src/pages/api/openapi/kb/searchTest.ts index d2374ac16..fb4bcc9b8 100644 --- a/client/src/pages/api/openapi/kb/searchTest.ts +++ b/client/src/pages/api/openapi/kb/searchTest.ts @@ -7,6 +7,7 @@ import { getVector } from '../plugin/vector'; import type { KbTestItemType } from '@/types/plugin'; export type Props = { + model: string; kbId: string; text: string; }; @@ -14,9 +15,9 @@ export type Response = KbTestItemType['results']; export default withNextCors(async function handler(req: NextApiRequest, res: NextApiResponse) { try { - const { kbId, text } = req.body as Props; + const { kbId, text, model } = req.body as Props; - if (!kbId || !text) { + if (!kbId || !text || !model) { throw new Error('缺少参数'); } @@ -27,7 +28,8 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex throw new Error('缺少用户ID'); } - const vector = await getVector({ + const { vectors } = await getVector({ + model, userId, input: [text] }); @@ -36,9 +38,9 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex `BEGIN; SET LOCAL ivfflat.probes = ${global.systemEnv.pgIvfflatProbe || 10}; select id,q,a,source,(vector <#> '[${ - vector[0] + vectors[0] }]') * -1 AS score from modelData where kb_id='${kbId}' AND user_id='${userId}' order by vector <#> '[${ - vector[0] + vectors[0] }]' limit 12; COMMIT;` ); diff --git a/client/src/pages/api/plugins/kb/data/getDataById.ts b/client/src/pages/api/plugins/kb/data/getDataById.ts index 599ae04e2..a38c026a9 100644 --- a/client/src/pages/api/plugins/kb/data/getDataById.ts +++ b/client/src/pages/api/plugins/kb/data/getDataById.ts @@ -5,6 +5,13 @@ import { authUser } from '@/service/utils/auth'; import { PgClient } from '@/service/pg'; import type { KbDataItemType } from '@/types/plugin'; +export type Response = { + id: string; + q: string; + a: string; + source: string; +}; + export default async function handler(req: NextApiRequest, res: NextApiResponse) { try { let { dataId } = req.query as { diff --git a/client/src/pages/api/plugins/kb/detail.ts b/client/src/pages/api/plugins/kb/detail.ts index a20172108..dd3e2dbaa 100644 --- a/client/src/pages/api/plugins/kb/detail.ts +++ b/client/src/pages/api/plugins/kb/detail.ts @@ -18,10 +18,13 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< await connectToDatabase(); - const data = await KB.findOne({ - _id: id, - userId - }); + const data = await KB.findOne( + { + _id: id, + userId + }, + '_id avatar name userId tags' + ); if (!data) { throw new Error('kb is not exist'); @@ -33,7 +36,6 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< avatar: data.avatar, name: data.name, userId: data.userId, - updateTime: data.updateTime, tags: data.tags.join(' ') } }); diff --git a/client/src/pages/api/plugins/kb/list.ts b/client/src/pages/api/plugins/kb/list.ts index 98a62c225..f2cff7032 100644 --- a/client/src/pages/api/plugins/kb/list.ts +++ b/client/src/pages/api/plugins/kb/list.ts @@ -2,8 +2,7 @@ import type { NextApiRequest, NextApiResponse } from 'next'; import { jsonRes } from '@/service/response'; import { connectToDatabase, KB } from '@/service/mongo'; import { authUser } from '@/service/utils/auth'; -import { PgClient } from '@/service/pg'; -import { KbItemType } from '@/types/plugin'; +import { KbListItemType } from '@/types/plugin'; export default async function handler(req: NextApiRequest, res: NextApiResponse) { try { @@ -12,25 +11,23 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< await connectToDatabase(); - const kbList = await KB.find({ - userId - }).sort({ updateTime: -1 }); + const kbList = await KB.find( + { + userId + }, + '_id avatar name tags' + ).sort({ updateTime: -1 }); const data = await Promise.all( kbList.map(async (item) => ({ _id: item._id, avatar: item.avatar, name: item.name, - userId: item.userId, - updateTime: item.updateTime, - tags: item.tags.join(' '), - totalData: await PgClient.count('modelData', { - where: [['user_id', userId], 'AND', ['kb_id', item._id]] - }) + tags: item.tags })) ); - jsonRes(res, { + jsonRes(res, { data }); } catch (err) { diff --git a/client/src/pages/chat/components/QuoteModal.tsx b/client/src/pages/chat/components/QuoteModal.tsx index 7b65e4f75..1d22f522a 100644 --- a/client/src/pages/chat/components/QuoteModal.tsx +++ b/client/src/pages/chat/components/QuoteModal.tsx @@ -10,7 +10,7 @@ import { useTheme } from '@chakra-ui/react'; import MyIcon from '@/components/Icon'; -import InputDataModal from '@/pages/kb/components/InputDataModal'; +import InputDataModal from '@/pages/kb/detail/components/InputDataModal'; import { getKbDataItemById } from '@/api/plugins/kb'; import { useLoading } from '@/hooks/useLoading'; import { useQuery } from '@tanstack/react-query'; diff --git a/client/src/pages/kb/components/Detail.tsx b/client/src/pages/kb/components/Detail.tsx deleted file mode 100644 index 5012a86ef..000000000 --- a/client/src/pages/kb/components/Detail.tsx +++ /dev/null @@ -1,99 +0,0 @@ -import React, { useRef, useState } from 'react'; -import { useRouter } from 'next/router'; -import { Box, Flex } from '@chakra-ui/react'; -import { useToast } from '@/hooks/useToast'; -import { useForm } from 'react-hook-form'; -import { useQuery } from '@tanstack/react-query'; -import { useUserStore } from '@/store/user'; -import { KbItemType } from '@/types/plugin'; -import { useScreen } from '@/hooks/useScreen'; -import { getErrText } from '@/utils/tools'; -import { type ComponentRef } from './Info'; -import Tabs from '@/components/Tabs'; -import dynamic from 'next/dynamic'; -import DataCard from './DataCard'; - -const Test = dynamic(() => import('./Test'), { - ssr: false -}); -const Info = dynamic(() => import('./Info'), { - ssr: false -}); - -enum TabEnum { - data = 'data', - test = 'test', - info = 'info' -} - -const Detail = ({ kbId }: { kbId: string }) => { - const { toast } = useToast(); - const router = useRouter(); - const { isPc } = useScreen(); - const BasicInfo = useRef(null); - const { setLastKbId, kbDetail, getKbDetail, loadKbList } = useUserStore(); - const [currentTab, setCurrentTab] = useState(TabEnum.data); - - const form = useForm({ - defaultValues: kbDetail - }); - const { reset } = form; - - useQuery( - [kbId], - () => { - setCurrentTab(TabEnum.data); - return getKbDetail(kbId); - }, - { - onSuccess(res) { - if (!res) return; - kbId && setLastKbId(kbId); - reset(res); - BasicInfo.current?.initInput?.(res.tags); - }, - onError(err: any) { - loadKbList(true); - setLastKbId(''); - router.replace(`/kb`); - toast({ - title: getErrText(err, '获取知识库异常'), - status: 'error' - }); - } - } - ); - - return ( - - - setCurrentTab(e)} - /> - - - {currentTab === TabEnum.data && } - {currentTab === TabEnum.test && } - {currentTab === TabEnum.info && } - - - ); -}; - -export default React.memo(Detail); diff --git a/client/src/pages/kb/components/KbList.tsx b/client/src/pages/kb/components/KbList.tsx deleted file mode 100644 index 6127e15e0..000000000 --- a/client/src/pages/kb/components/KbList.tsx +++ /dev/null @@ -1,152 +0,0 @@ -import React, { useCallback, useState, useMemo } from 'react'; -import { Box, Flex, useTheme, Input, IconButton } from '@chakra-ui/react'; -import { AddIcon } from '@chakra-ui/icons'; -import { useRouter } from 'next/router'; -import { postCreateKb } from '@/api/plugins/kb'; -import { useLoading } from '@/hooks/useLoading'; -import { useToast } from '@/hooks/useToast'; -import { useQuery } from '@tanstack/react-query'; -import { useUserStore } from '@/store/user'; -import MyIcon from '@/components/Icon'; -import Avatar from '@/components/Avatar'; -import Tag from '@/components/Tag'; -import MyTooltip from '@/components/MyTooltip'; - -const KbList = ({ kbId }: { kbId: string }) => { - const theme = useTheme(); - const router = useRouter(); - const { toast } = useToast(); - const { Loading, setIsLoading } = useLoading(); - const { myKbList, loadKbList } = useUserStore(); - const [searchText, setSearchText] = useState(''); - - const kbs = useMemo( - () => myKbList.filter((item) => new RegExp(searchText, 'ig').test(item.name + item.tags)), - [myKbList, searchText] - ); - - /* 加载模型 */ - const { isFetching } = useQuery(['loadModels'], () => loadKbList(false)); - - const handleCreateModel = useCallback(async () => { - setIsLoading(true); - try { - const name = `知识库${myKbList.length + 1}`; - const id = await postCreateKb({ name }); - await loadKbList(true); - toast({ - title: '创建成功', - status: 'success' - }); - router.replace(`/kb?kbId=${id}`); - } catch (err: any) { - toast({ - title: typeof err === 'string' ? err : err.message || '出现了意外', - status: 'error' - }); - } - setIsLoading(false); - }, [loadKbList, myKbList.length, router, setIsLoading, toast]); - - return ( - - - - setSearchText(e.target.value)} - /> - {searchText && ( - setSearchText('')} - /> - )} - - - } - aria-label={''} - variant={'base'} - onClick={handleCreateModel} - /> - - - - {kbs.map((item) => ( - { - if (item._id === kbId) return; - router.push(`/kb?kbId=${item._id}`); - }} - > - - - - {item.name} - - {/* tags */} - - {!item.tags ? ( - <>{item.tags || '你还没设置标签~'} - ) : ( - item.tags.split(' ').map((item, i) => ( - - {item} - - )) - )} - - - - ))} - - {!isFetching && myKbList.length === 0 && ( - - - - 知识库空空如也~ - - - )} - - - - ); -}; - -export default KbList; diff --git a/client/src/pages/kb/components/DataCard.tsx b/client/src/pages/kb/detail/components/DataCard.tsx similarity index 98% rename from client/src/pages/kb/components/DataCard.tsx rename to client/src/pages/kb/detail/components/DataCard.tsx index e44ba8e06..0c43f8da6 100644 --- a/client/src/pages/kb/components/DataCard.tsx +++ b/client/src/pages/kb/detail/components/DataCard.tsx @@ -31,8 +31,8 @@ import InputModal, { FormData as InputDataType } from './InputDataModal'; import { debounce } from 'lodash'; import { getErrText } from '@/utils/tools'; -const SelectFileModal = dynamic(() => import('./SelectFileModal')); -const SelectCsvModal = dynamic(() => import('./SelectCsvModal')); +const SelectFileModal = dynamic(() => import('./SelectFileModal'), { ssr: true }); +const SelectCsvModal = dynamic(() => import('./SelectCsvModal'), { ssr: true }); const DataCard = ({ kbId }: { kbId: string }) => { const lastSearch = useRef(''); @@ -140,7 +140,7 @@ const DataCard = ({ kbId }: { kbId: string }) => { }, [kbId]); return ( - + 知识库数据: {total}组 diff --git a/client/src/pages/kb/components/Info.tsx b/client/src/pages/kb/detail/components/Info.tsx similarity index 87% rename from client/src/pages/kb/components/Info.tsx rename to client/src/pages/kb/detail/components/Info.tsx index 2920464ba..f2b879d73 100644 --- a/client/src/pages/kb/components/Info.tsx +++ b/client/src/pages/kb/detail/components/Info.tsx @@ -59,7 +59,7 @@ const Info = ( status: 'success' }); router.replace(`/kb?kbId=${myKbList.find((item) => item._id !== kbId)?._id || ''}`); - await loadKbList(true); + await loadKbList(); } catch (err: any) { toast({ title: err?.message || '删除失败', @@ -82,7 +82,7 @@ const Info = ( title: '更新成功', status: 'success' }); - loadKbList(true); + loadKbList(); } catch (err: any) { toast({ title: err?.message || '更新失败', @@ -136,6 +136,7 @@ const Info = ( useImperativeHandle(ref, () => ({ initInput: (tags: string) => { + console.log(tags); if (InputRef.current) { InputRef.current.value = tags; } @@ -143,7 +144,7 @@ const Info = ( })); return ( - + 知识库头像 @@ -200,31 +201,29 @@ const Info = ( ))} - {kbDetail._id && ( - - - - } - aria-label={''} - variant={'outline'} - size={'sm'} - _hover={{ - color: 'red.600', - borderColor: 'red.600' - }} - onClick={openConfirm(onclickDelKb)} - /> - - )} + + + + } + aria-label={''} + variant={'outline'} + size={'sm'} + _hover={{ + color: 'red.600', + borderColor: 'red.600' + }} + onClick={openConfirm(onclickDelKb)} + /> + diff --git a/client/src/pages/kb/components/InputDataModal.tsx b/client/src/pages/kb/detail/components/InputDataModal.tsx similarity index 100% rename from client/src/pages/kb/components/InputDataModal.tsx rename to client/src/pages/kb/detail/components/InputDataModal.tsx diff --git a/client/src/pages/kb/components/SelectCsvModal.tsx b/client/src/pages/kb/detail/components/SelectCsvModal.tsx similarity index 100% rename from client/src/pages/kb/components/SelectCsvModal.tsx rename to client/src/pages/kb/detail/components/SelectCsvModal.tsx diff --git a/client/src/pages/kb/components/SelectFileModal.tsx b/client/src/pages/kb/detail/components/SelectFileModal.tsx similarity index 100% rename from client/src/pages/kb/components/SelectFileModal.tsx rename to client/src/pages/kb/detail/components/SelectFileModal.tsx diff --git a/client/src/pages/kb/components/Test.tsx b/client/src/pages/kb/detail/components/Test.tsx similarity index 81% rename from client/src/pages/kb/components/Test.tsx rename to client/src/pages/kb/detail/components/Test.tsx index e23becdb7..c7fc7d04d 100644 --- a/client/src/pages/kb/components/Test.tsx +++ b/client/src/pages/kb/detail/components/Test.tsx @@ -11,7 +11,10 @@ import InputDataModal, { type FormData } from './InputDataModal'; import { useGlobalStore } from '@/store/global'; import { getErrText } from '@/utils/tools'; import { useToast } from '@/hooks/useToast'; +import { vectorModelList } from '@/store/static'; import { customAlphabet } from 'nanoid'; +import MyTooltip from '@/components/MyTooltip'; +import { QuestionOutlineIcon } from '@chakra-ui/icons'; const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 12); const Test = () => { @@ -30,7 +33,7 @@ const Test = () => { ); const { mutate, isLoading } = useRequest({ - mutationFn: () => searchText({ kbId, text: inputText.trim() }), + mutationFn: () => searchText({ model: vectorModelList[0].model, kbId, text: inputText.trim() }), onSuccess(res) { const testItem = { id: nanoid(), @@ -40,7 +43,6 @@ const Test = () => { results: res }; pushKbTestItem(testItem); - setInputText(''); setKbTestItem(testItem); }, onError(err) { @@ -59,13 +61,14 @@ const Test = () => { - + 测试文本 @@ -85,13 +88,13 @@ const Test = () => { - + 测试历史 - + 测试文本 时间 @@ -115,26 +118,28 @@ const Test = () => { {item.text} {formatTimeToChatTime(item.time)} - - { - e.stopPropagation(); - delKbTestItemById(item.id); - kbTestItem?.id === item.id && setKbTestItem(undefined); - }} - /> - + + + { + e.stopPropagation(); + delKbTestItemById(item.id); + kbTestItem?.id === item.id && setKbTestItem(undefined); + }} + /> + + ))} - + {!kbTestItem?.results || kbTestItem.results.length === 0 ? ( { 测试结果 - - QA内容可能不是最新 - + + + import('./components/Test'), { + ssr: false +}); + +enum TabEnum { + data = 'data', + test = 'test', + info = 'info' +} + +const Detail = ({ kbId, currentTab }: { kbId: string; currentTab: `${TabEnum}` }) => { + const InfoRef = useRef(null); + const theme = useTheme(); + const { toast } = useToast(); + const router = useRouter(); + const { isPc } = useScreen(); + const { kbDetail, getKbDetail } = useUserStore(); + + const tabList = useMemo( + () => [ + { label: '数据集', id: TabEnum.data, icon: 'overviewLight' }, + { label: '搜索测试', id: TabEnum.test, icon: 'kbTest' }, + { label: '基本信息', id: TabEnum.info, icon: 'settingLight' } + ], + [] + ); + + const setCurrentTab = useCallback( + (tab: `${TabEnum}`) => { + router.replace({ + query: { + kbId, + currentTab: tab + } + }); + }, + [kbId, router] + ); + + const form = useForm({ + defaultValues: kbDetail + }); + + useQuery([kbId], () => getKbDetail(kbId), { + onSuccess(res) { + InfoRef.current?.initInput(res.tags); + form.reset(res); + }, + onError(err: any) { + router.replace(`/kb/list`); + toast({ + title: getErrText(err, '获取知识库异常'), + status: 'error' + }); + } + }); + + return ( + + + {/* pc tab */} + + + + + {kbDetail.name} + + + { + setCurrentTab(e); + }} + /> + router.replace('/kb/list')} + > + } + bg={'white'} + boxShadow={'1px 1px 9px rgba(0,0,0,0.15)'} + h={'28px'} + size={'sm'} + borderRadius={'50%'} + aria-label={''} + /> + 全部知识库 + + + + setCurrentTab(e)} + /> + + + {currentTab === TabEnum.data && } + {currentTab === TabEnum.test && } + {currentTab === TabEnum.info && } + + + + ); +}; + +export async function getServerSideProps(context: any) { + const currentTab = context?.query?.currentTab || TabEnum.data; + const kbId = context?.query?.kbId; + + return { + props: { currentTab, kbId } + }; +} + +export default React.memo(Detail); diff --git a/client/src/pages/kb/index.tsx b/client/src/pages/kb/index.tsx deleted file mode 100644 index 9924c0c7f..000000000 --- a/client/src/pages/kb/index.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import React, { useEffect } from 'react'; -import { Box, Flex } from '@chakra-ui/react'; -import { useGlobalStore } from '@/store/global'; -import { useRouter } from 'next/router'; -import { useUserStore } from '@/store/user'; -import SideBar from '@/components/SideBar'; -import KbList from './components/KbList'; -import KbDetail from './components/Detail'; - -const Kb = () => { - const router = useRouter(); - const { kbId = '' } = router.query as { kbId: string }; - const { isPc } = useGlobalStore(); - const { lastKbId } = useUserStore(); - - // redirect - useEffect(() => { - if (isPc && !kbId && lastKbId) { - router.replace(`/kb?kbId=${lastKbId}`); - } - }, [isPc, kbId, lastKbId, router]); - - return ( - - {/* 模型列表 */} - {(isPc || !kbId) && ( - - - - )} - {!!kbId && ( - - - - )} - - ); -}; - -export default Kb; diff --git a/client/src/pages/kb/list/index.tsx b/client/src/pages/kb/list/index.tsx new file mode 100644 index 000000000..350b36568 --- /dev/null +++ b/client/src/pages/kb/list/index.tsx @@ -0,0 +1,149 @@ +import React, { useCallback } from 'react'; +import { Box, Card, Flex, Grid, useTheme, Button, IconButton } from '@chakra-ui/react'; +import { useRouter } from 'next/router'; +import { useUserStore } from '@/store/user'; +import PageContainer from '@/components/PageContainer'; +import { useConfirm } from '@/hooks/useConfirm'; +import { AddIcon } from '@chakra-ui/icons'; +import { useQuery } from '@tanstack/react-query'; +import { useToast } from '@/hooks/useToast'; +import { delKbById, postCreateKb } from '@/api/plugins/kb'; +import { useRequest } from '@/hooks/useRequest'; +import Avatar from '@/components/Avatar'; +import MyIcon from '@/components/Icon'; +import Tag from '@/components/Tag'; + +const Kb = () => { + const theme = useTheme(); + const router = useRouter(); + const { toast } = useToast(); + const { openConfirm, ConfirmChild } = useConfirm({ + title: '删除提示', + content: '确认删除该知识库?' + }); + const { myKbList, loadKbList, setKbList } = useUserStore(); + + useQuery(['loadKbList'], () => loadKbList()); + + /* 点击删除 */ + const onclickDelKb = useCallback( + async (id: string) => { + try { + delKbById(id); + toast({ + title: '删除成功', + status: 'success' + }); + setKbList(myKbList.filter((item) => item._id !== id)); + } catch (err: any) { + toast({ + title: err?.message || '删除失败', + status: 'error' + }); + } + }, + [toast, setKbList, myKbList] + ); + + /* create a new kb and router to it */ + const { mutate: onclickCreate, isLoading } = useRequest({ + mutationFn: async () => { + const name = `知识库${myKbList.length + 1}`; + const id = await postCreateKb({ name }); + return id; + }, + successToast: '创建成功', + errorToast: '创建知识库出现意外', + onSuccess(id) { + router.push(`/kb/detail?kbId=${id}`); + } + }); + + return ( + + + + 我的知识库 + + + + + {myKbList.map((kb) => ( + + router.push({ + pathname: '/kb/detail', + query: { + kbId: kb._id + } + }) + } + > + + + {kb.name} + } + variant={'base'} + borderRadius={'md'} + aria-label={'delete'} + display={['', 'none']} + _hover={{ + bg: 'red.100' + }} + onClick={(e) => { + e.stopPropagation(); + openConfirm(() => onclickDelKb(kb._id))(); + }} + /> + + + {kb.tags.map((tag, i) => ( + + {tag} + + ))} + + + ))} + + + + ); +}; + +export default Kb; diff --git a/client/src/store/user.ts b/client/src/store/user.ts index 4f8890634..5f8891d2f 100644 --- a/client/src/store/user.ts +++ b/client/src/store/user.ts @@ -7,7 +7,7 @@ import { formatPrice } from '@/utils/user'; import { getTokenLogin } from '@/api/user'; import { defaultApp } from '@/constants/model'; import { AppListItemType } from '@/types/app'; -import { KbItemType } from '@/types/plugin'; +import type { KbItemType, KbListItemType } from '@/types/plugin'; import { getKbList, getKbById } from '@/api/plugins/kb'; import { defaultKbDetail } from '@/constants/kb'; import type { AppSchema } from '@/types/mongoSchema'; @@ -23,10 +23,9 @@ type State = { appDetail: AppSchema; loadAppDetail: (id: string, init?: boolean) => Promise; // kb - lastKbId: string; - setLastKbId: (id: string) => void; - myKbList: KbItemType[]; - loadKbList: (init?: boolean) => Promise; + myKbList: KbListItemType[]; + loadKbList: () => Promise; + setKbList(val: KbListItemType[]): void; kbDetail: KbItemType; getKbDetail: (id: string, init?: boolean) => Promise; }; @@ -79,21 +78,19 @@ export const useUserStore = create()( }); return res; }, - lastKbId: '', - setLastKbId(id: string) { - set((state) => { - state.lastKbId = id; - }); - }, myKbList: [], - async loadKbList(init = false) { - if (get().myKbList.length > 0 && !init) return get().myKbList; + async loadKbList() { const res = await getKbList(); set((state) => { state.myKbList = res; }); return res; }, + setKbList(val: KbListItemType[]) { + set((state) => { + state.myKbList = val; + }); + }, kbDetail: defaultKbDetail, async getKbDetail(id: string, init = false) { if (id === get().kbDetail._id && !init) return get().kbDetail; @@ -109,9 +106,7 @@ export const useUserStore = create()( })), { name: 'userStore', - partialize: (state) => ({ - lastKbId: state.lastKbId - }) + partialize: (state) => ({}) } ) ) diff --git a/client/src/styles/default.scss b/client/src/styles/default.scss new file mode 100644 index 000000000..90250ccf7 --- /dev/null +++ b/client/src/styles/default.scss @@ -0,0 +1,24 @@ +#nprogress .bar { + background: '#1237b3' !important; //自定义颜色 +} + +.textEllipsis { + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; +} +.textEllipsis3 { + display: -webkit-box; + -webkit-line-clamp: 3; + -webkit-box-orient: vertical; + overflow: hidden; + text-overflow: ellipsis; +} +.grecaptcha-badge { + display: none !important; +} +.textlg { + background: linear-gradient(to bottom right, #1237b3 0%, #3370ff 40%, #4e83fd 80%, #85b1ff 100%); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; +} diff --git a/client/src/styles/reactflow.scss b/client/src/styles/reactflow.scss index 0dd301eaf..37df1fac9 100644 --- a/client/src/styles/reactflow.scss +++ b/client/src/styles/reactflow.scss @@ -1,3 +1,6 @@ .react-flow__panel { display: none; } +.react-flow__panel.react-flow__attribution { + display: none !important; +} diff --git a/client/src/styles/reset.scss b/client/src/styles/reset.scss index c796ec935..5360650a2 100644 --- a/client/src/styles/reset.scss +++ b/client/src/styles/reset.scss @@ -1,3 +1,6 @@ +@import './reactflow.scss'; +@import './default.scss'; + body, h1, h2, @@ -71,26 +74,6 @@ textarea::placeholder { #__next { height: 100%; } -#nprogress .bar { - background: '#1237b3' !important; //自定义颜色 -} - -.textEllipsis { - text-overflow: ellipsis; - white-space: nowrap; - overflow: hidden; -} -.grecaptcha-badge { - display: none !important; -} -.react-flow__panel.react-flow__attribution { - display: none !important; -} -.textlg { - background: linear-gradient(to bottom right, #1237b3 0%, #3370ff 40%, #4e83fd 80%, #85b1ff 100%); - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; -} @media (max-width: 900px) { html { diff --git a/client/src/types/plugin.d.ts b/client/src/types/plugin.d.ts index 5e7257233..8a04d1f0a 100644 --- a/client/src/types/plugin.d.ts +++ b/client/src/types/plugin.d.ts @@ -1,5 +1,11 @@ import type { kbSchema } from './mongoSchema'; +export type KbListItemType = { + _id: string; + avatar: string; + name: string; + tags: string[]; +}; /* kb type */ export interface KbItemType extends kbSchema { totalData: number;