From eef6d7518eeedaaca221e8acf77a532356b97a60 Mon Sep 17 00:00:00 2001 From: archer <545436317@qq.com> Date: Sat, 10 Jun 2023 14:31:20 +0800 Subject: [PATCH] perf: kb framwork --- client/src/pages/kb/components/Detail.tsx | 205 +------------------ client/src/pages/kb/components/Info.tsx | 227 ++++++++++++++++++++++ 2 files changed, 236 insertions(+), 196 deletions(-) create mode 100644 client/src/pages/kb/components/Info.tsx diff --git a/client/src/pages/kb/components/Detail.tsx b/client/src/pages/kb/components/Detail.tsx index b921b7603..ad946b436 100644 --- a/client/src/pages/kb/components/Detail.tsx +++ b/client/src/pages/kb/components/Detail.tsx @@ -1,58 +1,33 @@ -import React, { useCallback, useState, useRef } from 'react'; +import React, { useRef } from 'react'; import { useRouter } from 'next/router'; -import { - Card, - Box, - Flex, - Button, - Tooltip, - FormControl, - Input, - Tag, - IconButton -} from '@chakra-ui/react'; -import { QuestionOutlineIcon, DeleteIcon } from '@chakra-ui/icons'; +import { Card, Box } 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 { delKbById, putKbById } from '@/api/plugins/kb'; import { KbItemType } from '@/types/plugin'; -import { useSelectFile } from '@/hooks/useSelectFile'; -import { useConfirm } from '@/hooks/useConfirm'; -import { compressImg } from '@/utils/file'; + import DataCard from './DataCard'; import { getErrText } from '@/utils/tools'; -import Avatar from '@/components/Avatar'; +import Info, { type ComponentRef } from './Info'; const Detail = ({ kbId }: { kbId: string }) => { const { toast } = useToast(); const router = useRouter(); - const InputRef = useRef(null); + const BasicInfo = useRef(null); const { setLastKbId, kbDetail, getKbDetail, loadKbList, myKbList } = useUserStore(); - const [btnLoading, setBtnLoading] = useState(false); - const [refresh, setRefresh] = useState(false); - const { getValues, formState, setValue, reset, register, handleSubmit } = useForm({ + const form = useForm({ defaultValues: kbDetail }); - const { openConfirm, ConfirmChild } = useConfirm({ - content: '确认删除该知识库?数据将无法恢复,请确认!' - }); - - const { File, onOpen: onOpenSelectFile } = useSelectFile({ - fileType: '.jpg,.png', - multiple: false - }); + const { reset } = form; useQuery([kbId, myKbList], () => getKbDetail(kbId), { onSuccess(res) { kbId && setLastKbId(kbId); if (res) { reset(res); - if (InputRef.current) { - InputRef.current.value = res.tags; - } + BasicInfo.current?.initInput?.(res.tags); } }, onError(err: any) { @@ -66,176 +41,14 @@ const Detail = ({ kbId }: { kbId: string }) => { } }); - /* 点击删除 */ - const onclickDelKb = useCallback(async () => { - setBtnLoading(true); - try { - await delKbById(kbId); - toast({ - title: '删除成功', - status: 'success' - }); - router.replace(`/kb?kbId=${myKbList.find((item) => item._id !== kbId)?._id || ''}`); - await loadKbList(true); - } catch (err: any) { - toast({ - title: err?.message || '删除失败', - status: 'error' - }); - } - setBtnLoading(false); - }, [setBtnLoading, kbId, toast, router, myKbList, loadKbList]); - - const saveSubmitSuccess = useCallback( - async (data: KbItemType) => { - setBtnLoading(true); - try { - await putKbById({ - id: kbId, - ...data - }); - await getKbDetail(kbId, true); - toast({ - title: '更新成功', - status: 'success' - }); - loadKbList(true); - } catch (err: any) { - toast({ - title: err?.message || '更新失败', - status: 'error' - }); - } - setBtnLoading(false); - }, - [getKbDetail, kbId, loadKbList, toast] - ); - const saveSubmitError = useCallback(() => { - // deep search message - const deepSearch = (obj: any): string => { - if (!obj) return '提交表单错误'; - if (!!obj.message) { - return obj.message; - } - return deepSearch(Object.values(obj)[0]); - }; - toast({ - title: deepSearch(formState.errors), - status: 'error', - duration: 4000, - isClosable: true - }); - }, [formState.errors, toast]); - - const onSelectFile = useCallback( - async (e: File[]) => { - const file = e[0]; - if (!file) return; - try { - const base64 = await compressImg({ - file, - maxW: 100, - maxH: 100 - }); - setValue('avatar', base64); - setRefresh((state) => !state); - } catch (err: any) { - toast({ - title: typeof err === 'string' ? err : '头像选择异常', - status: 'warning' - }); - } - }, - [setRefresh, setValue, toast] - ); - return ( - - - 知识库信息 - - {kbDetail._id && ( - <> - - } - aria-label={''} - variant={'solid'} - colorScheme={'red'} - onClick={openConfirm(onclickDelKb)} - /> - - )} - - - - 头像 - - - - - - - 名称 - - - - - - - - 标签 - - - - - { - setValue('tags', e.target.value); - setRefresh(!refresh); - }} - /> - - {getValues('tags') - .split(' ') - .filter((item) => item) - .map((item, i) => ( - - {item} - - ))} - - - + - - ); }; diff --git a/client/src/pages/kb/components/Info.tsx b/client/src/pages/kb/components/Info.tsx new file mode 100644 index 000000000..a2acf527a --- /dev/null +++ b/client/src/pages/kb/components/Info.tsx @@ -0,0 +1,227 @@ +import React, { + useCallback, + useState, + useRef, + forwardRef, + useImperativeHandle, + ForwardedRef +} from 'react'; +import { useRouter } from 'next/router'; +import { Box, Flex, Button, FormControl, IconButton, Tooltip, Input, Tag } from '@chakra-ui/react'; +import { QuestionOutlineIcon, DeleteIcon } from '@chakra-ui/icons'; +import { delKbById, putKbById } from '@/api/plugins/kb'; +import { useSelectFile } from '@/hooks/useSelectFile'; +import { useToast } from '@/hooks/useToast'; +import { useUserStore } from '@/store/user'; +import { useConfirm } from '@/hooks/useConfirm'; +import { UseFormReturn } from 'react-hook-form'; +import { compressImg } from '@/utils/file'; +import type { KbItemType } from '@/types/plugin'; +import Avatar from '@/components/Avatar'; + +export interface ComponentRef { + initInput: (tags: string) => void; +} + +const Info = ( + { kbId, form }: { kbId: string; form: UseFormReturn }, + ref: ForwardedRef +) => { + const { getValues, formState, setValue, register, handleSubmit } = form; + const InputRef = useRef(null); + + const { toast } = useToast(); + const router = useRouter(); + + const [btnLoading, setBtnLoading] = useState(false); + const [refresh, setRefresh] = useState(false); + + const { openConfirm, ConfirmChild } = useConfirm({ + content: '确认删除该知识库?数据将无法恢复,请确认!' + }); + + const { File, onOpen: onOpenSelectFile } = useSelectFile({ + fileType: '.jpg,.png', + multiple: false + }); + + const { kbDetail, getKbDetail, loadKbList, myKbList } = useUserStore(); + + /* 点击删除 */ + const onclickDelKb = useCallback(async () => { + setBtnLoading(true); + try { + await delKbById(kbId); + toast({ + title: '删除成功', + status: 'success' + }); + router.replace(`/kb?kbId=${myKbList.find((item) => item._id !== kbId)?._id || ''}`); + await loadKbList(true); + } catch (err: any) { + toast({ + title: err?.message || '删除失败', + status: 'error' + }); + } + setBtnLoading(false); + }, [setBtnLoading, kbId, toast, router, myKbList, loadKbList]); + + const saveSubmitSuccess = useCallback( + async (data: KbItemType) => { + setBtnLoading(true); + try { + await putKbById({ + id: kbId, + ...data + }); + await getKbDetail(kbId, true); + toast({ + title: '更新成功', + status: 'success' + }); + loadKbList(true); + } catch (err: any) { + toast({ + title: err?.message || '更新失败', + status: 'error' + }); + } + setBtnLoading(false); + }, + [getKbDetail, kbId, loadKbList, toast] + ); + const saveSubmitError = useCallback(() => { + // deep search message + const deepSearch = (obj: any): string => { + if (!obj) return '提交表单错误'; + if (!!obj.message) { + return obj.message; + } + return deepSearch(Object.values(obj)[0]); + }; + toast({ + title: deepSearch(formState.errors), + status: 'error', + duration: 4000, + isClosable: true + }); + }, [formState.errors, toast]); + + const onSelectFile = useCallback( + async (e: File[]) => { + const file = e[0]; + if (!file) return; + try { + const base64 = await compressImg({ + file, + maxW: 100, + maxH: 100 + }); + setValue('avatar', base64); + setRefresh((state) => !state); + } catch (err: any) { + toast({ + title: typeof err === 'string' ? err : '头像选择异常', + status: 'warning' + }); + } + }, + [setRefresh, setValue, toast] + ); + + useImperativeHandle(ref, () => ({ + initInput: (tags: string) => { + if (InputRef.current) { + InputRef.current.value = tags; + } + } + })); + + return ( + + + + 知识库信息 + + {kbDetail._id && ( + <> + + } + aria-label={''} + variant={'solid'} + colorScheme={'red'} + onClick={openConfirm(onclickDelKb)} + /> + + )} + + + + 头像 + + + + + + + 名称 + + + + + + + + 标签 + + + + + { + setValue('tags', e.target.value); + setRefresh(!refresh); + }} + /> + + {getValues('tags') + .split(' ') + .filter((item) => item) + .map((item, i) => ( + + {item} + + ))} + + + + + + + ); +}; + +export default forwardRef(Info);