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 Link from "next/link";
import Router from "next/router";
import { Typography, Button, Modal, Input } from "@douyinfe/semi-ui";
import { IconSearch } from "components/icons";
import { IDocument } from "@think/domains";
import { useRecentDocuments } from "data/document";
import { useToggle } from "hooks/useToggle";
import { searchDocument } from "services/document";
import { Empty } from "components/empty";
import { DataRender } from "components/data-render";
import { LocaleTime } from "components/locale-time";
import { DocumentStar } from "components/document/star";
import { IconDocumentFill } from "components/icons/IconDocumentFill";
import styles from "./index.module.scss";
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import Link from 'next/link';
import Router from 'next/router';
import { Typography, Button, Modal, Input, Spin } from '@douyinfe/semi-ui';
import { IconSearch as SemiIconSearch } from '@douyinfe/semi-icons';
import { IconSearch } from 'components/icons';
import { IDocument } from '@think/domains';
import { useRecentDocuments } from 'data/document';
import { useToggle } from 'hooks/useToggle';
import { useAsyncLoading } from 'hooks/useAsyncLoading';
import { searchDocument } from 'services/document';
import { Empty } from 'components/empty';
import { DataRender } from 'components/data-render';
import { LocaleTime } from 'components/locale-time';
import { DocumentStar } from 'components/document/star';
import { IconDocumentFill } from 'components/icons/IconDocumentFill';
import styles from './index.module.scss';
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 = () => {
const [visible, toggleVisible] = useToggle(false);
const { data: recentDocs, loading, error } = useRecentDocuments();
const [keyword, setKeyword] = useState("");
const { data: recentDocs } = useRecentDocuments();
const [searchApi, loading] = useAsyncLoading(searchDocument, 10);
const [keyword, setKeyword] = useState('');
const [error, setError] = useState(null);
const [searchDocs, setSearchDocs] = useState<IDocument[]>([]);
const data = useMemo(
() => (searchDocs.length ? searchDocs : recentDocs),
[searchDocs.length, recentDocs]
);
const search = useCallback(() => {
searchDocument(keyword).then((res) => {
setSearchDocs(res);
});
}, [keyword]);
setError(null);
searchApi(keyword)
.then((res) => {
setSearchDocs(res);
})
.catch((err) => {
setError(err);
});
}, [searchApi, keyword]);
useEffect(() => {
const fn = () => {
toggleVisible(false);
};
Router.events.on("routeChangeStart", fn);
Router.events.on('routeChangeStart', fn);
return () => {
Router.events.off("routeChangeStart", fn);
Router.events.off('routeChangeStart', fn);
};
}, []);
}, [toggleVisible]);
return (
<>
@ -56,68 +111,53 @@ export const Search = () => {
title="文档搜索"
footer={null}
onCancel={toggleVisible}
style={{
maxWidth: '96vw',
}}
>
<div style={{ paddingBottom: 24 }}>
<Input
placeholder={"搜索文档"}
size="large"
value={keyword}
onChange={(val) => setKeyword(val)}
onEnterPress={search}
/>
<DataRender
loading={loading}
error={error}
normalContent={() => {
return (
<div className={styles.itemsWrap}>
{data.length ? (
data.map((doc) => {
return (
<div className={styles.itemWrap}>
<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>
<Input
placeholder={'搜索文档'}
size="large"
value={keyword}
onChange={(val) => {
setSearchDocs([]);
setKeyword(val);
}}
onEnterPress={search}
suffix={
<SemiIconSearch
onClick={search}
style={{ cursor: 'pointer' }}
/>
}
showClear
/>
</div>
<div style={{ maxHeight: '70vh', overflow: 'auto' }}>
<DataRender
loading={loading}
loadingContent={
<div
style={{
paddingTop: 30,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
>
<Spin />
</div>
);
}}
/>
}
error={error}
normalContent={() => <List data={searchDocs} />}
/>
<div style={{ marginTop: 16 }}>
<Text type="tertiary">访</Text>
<List data={recentDocs} />
</div>
</div>
</div>
</Modal>
</>