feat: improve search

pull/8/head
fantasticit 2022-03-12 10:24:34 +08:00
parent c017d5d351
commit b13dd26d9a
1 changed files with 127 additions and 87 deletions

View File

@ -1,47 +1,102 @@
import React, { useCallback, useEffect, useMemo, useState } from "react"; import React, { useCallback, useEffect, useMemo, useState } from 'react';
import Link from "next/link"; import Link from 'next/link';
import Router from "next/router"; import Router from 'next/router';
import { Typography, Button, Modal, Input } from "@douyinfe/semi-ui"; import { Typography, Button, Modal, Input, Spin } from '@douyinfe/semi-ui';
import { IconSearch } from "components/icons"; import { IconSearch as SemiIconSearch } from '@douyinfe/semi-icons';
import { IDocument } from "@think/domains"; import { IconSearch } from 'components/icons';
import { useRecentDocuments } from "data/document"; import { IDocument } from '@think/domains';
import { useToggle } from "hooks/useToggle"; import { useRecentDocuments } from 'data/document';
import { searchDocument } from "services/document"; import { useToggle } from 'hooks/useToggle';
import { Empty } from "components/empty"; import { useAsyncLoading } from 'hooks/useAsyncLoading';
import { DataRender } from "components/data-render"; import { searchDocument } from 'services/document';
import { LocaleTime } from "components/locale-time"; import { Empty } from 'components/empty';
import { DocumentStar } from "components/document/star"; import { DataRender } from 'components/data-render';
import { IconDocumentFill } from "components/icons/IconDocumentFill"; import { LocaleTime } from 'components/locale-time';
import styles from "./index.module.scss"; import { DocumentStar } from 'components/document/star';
import { IconDocumentFill } from 'components/icons/IconDocumentFill';
import styles from './index.module.scss';
const { Text } = Typography; const { Text } = Typography;
const List: React.FC<{ data: IDocument[] }> = ({ data }) => {
return (
<div className={styles.itemsWrap}>
{data.length ? (
data.map((doc) => {
return (
<div className={styles.itemWrap} key={doc.id}>
<Link
href={{
pathname: '/wiki/[wikiId]/document/[documentId]',
query: {
wikiId: doc.wikiId,
documentId: doc.id,
},
}}
>
<a className={styles.item}>
<div className={styles.leftWrap}>
<IconDocumentFill style={{ marginRight: 12 }} />
<div>
<Text
ellipsis={{ showTooltip: true }}
style={{ width: 180 }}
>
{doc.title}
</Text>
<Text size="small" type="tertiary">
{doc.createUser && doc.createUser.name} {' '}
<LocaleTime date={doc.updatedAt} timeago />
</Text>
</div>
</div>
<div className={styles.rightWrap}>
<DocumentStar documentId={doc.id} />
</div>
</a>
</Link>
</div>
);
})
) : (
<Empty message="暂无搜索结果" />
)}
</div>
);
};
export const Search = () => { export const Search = () => {
const [visible, toggleVisible] = useToggle(false); const [visible, toggleVisible] = useToggle(false);
const { data: recentDocs, loading, error } = useRecentDocuments(); const { data: recentDocs } = useRecentDocuments();
const [keyword, setKeyword] = useState(""); const [searchApi, loading] = useAsyncLoading(searchDocument, 10);
const [keyword, setKeyword] = useState('');
const [error, setError] = useState(null);
const [searchDocs, setSearchDocs] = useState<IDocument[]>([]); const [searchDocs, setSearchDocs] = useState<IDocument[]>([]);
const data = useMemo(
() => (searchDocs.length ? searchDocs : recentDocs),
[searchDocs.length, recentDocs]
);
const search = useCallback(() => { const search = useCallback(() => {
searchDocument(keyword).then((res) => { setError(null);
setSearchDocs(res); searchApi(keyword)
}); .then((res) => {
}, [keyword]); setSearchDocs(res);
})
.catch((err) => {
setError(err);
});
}, [searchApi, keyword]);
useEffect(() => { useEffect(() => {
const fn = () => { const fn = () => {
toggleVisible(false); toggleVisible(false);
}; };
Router.events.on("routeChangeStart", fn);
Router.events.on('routeChangeStart', fn);
return () => { return () => {
Router.events.off("routeChangeStart", fn); Router.events.off('routeChangeStart', fn);
}; };
}, []); }, [toggleVisible]);
return ( return (
<> <>
@ -56,68 +111,53 @@ export const Search = () => {
title="文档搜索" title="文档搜索"
footer={null} footer={null}
onCancel={toggleVisible} onCancel={toggleVisible}
style={{
maxWidth: '96vw',
}}
> >
<div style={{ paddingBottom: 24 }}> <div style={{ paddingBottom: 24 }}>
<Input <div>
placeholder={"搜索文档"} <Input
size="large" placeholder={'搜索文档'}
value={keyword} size="large"
onChange={(val) => setKeyword(val)} value={keyword}
onEnterPress={search} onChange={(val) => {
/> setSearchDocs([]);
<DataRender setKeyword(val);
loading={loading} }}
error={error} onEnterPress={search}
normalContent={() => { suffix={
return ( <SemiIconSearch
<div className={styles.itemsWrap}> onClick={search}
{data.length ? ( style={{ cursor: 'pointer' }}
data.map((doc) => { />
return ( }
<div className={styles.itemWrap}> showClear
<Link />
href={{ </div>
pathname: "/wiki/[wikiId]/document/[documentId]", <div style={{ maxHeight: '70vh', overflow: 'auto' }}>
query: { <DataRender
wikiId: doc.wikiId, loading={loading}
documentId: doc.id, loadingContent={
}, <div
}} style={{
> paddingTop: 30,
<a className={styles.item}> display: 'flex',
<div className={styles.leftWrap}> alignItems: 'center',
<IconDocumentFill style={{ marginRight: 12 }} /> justifyContent: 'center',
<div> }}
<Text >
ellipsis={{ showTooltip: true }} <Spin />
style={{ width: 180 }}
>
{doc.title}
</Text>
<Text size="small" type="tertiary">
{doc.createUser &&
doc.createUser.name} {" "}
<LocaleTime date={doc.updatedAt} timeago />
</Text>
</div>
</div>
<div className={styles.rightWrap}>
<DocumentStar documentId={doc.id} />
</div>
</a>
</Link>
</div>
);
})
) : (
<Empty message="最近访问的文档会出现在此处" />
)}
</div> </div>
); }
}} error={error}
/> normalContent={() => <List data={searchDocs} />}
/>
<div style={{ marginTop: 16 }}>
<Text type="tertiary">访</Text>
<List data={recentDocs} />
</div>
</div>
</div> </div>
</Modal> </Modal>
</> </>