FullTextSearchResult.tsx 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. import { List, Skeleton, Space, Tag, Typography } from "antd";
  2. import { useEffect, useState } from "react";
  3. import { Link } from "react-router-dom";
  4. import { get } from "../../request";
  5. import TocPath, { ITocPathNode } from "../corpus/TocPath";
  6. import { TContentType } from "../discussion/DiscussionCreate";
  7. import Marked from "../general/Marked";
  8. import PaliText from "../template/Wbw/PaliText";
  9. import "./search.css";
  10. const { Title, Text } = Typography;
  11. interface IFtsData {
  12. rank?: number;
  13. highlight?: string;
  14. book: number;
  15. paragraph: number;
  16. content?: string;
  17. content_type?: TContentType;
  18. title?: string;
  19. paliTitle?: string;
  20. path?: ITocPathNode[];
  21. }
  22. interface IFtsResponse {
  23. ok: boolean;
  24. message: string;
  25. data: {
  26. rows: IFtsData[];
  27. count: number;
  28. };
  29. }
  30. interface IFtsItem {
  31. book: number;
  32. paragraph: number;
  33. title?: string;
  34. paliTitle?: string;
  35. content?: string;
  36. path?: ITocPathNode[];
  37. }
  38. export type ISearchView = "pali" | "title" | "page";
  39. interface IWidget {
  40. keyWord?: string;
  41. tags?: string[];
  42. bookId?: string | null;
  43. book?: number;
  44. para?: number;
  45. orderBy?: string | null;
  46. match?: string | null;
  47. keyWord2?: string;
  48. view?: ISearchView;
  49. pageType?: string;
  50. }
  51. const FullTxtSearchResultWidget = ({
  52. keyWord,
  53. tags,
  54. bookId,
  55. book,
  56. para,
  57. orderBy,
  58. match,
  59. keyWord2,
  60. view = "pali",
  61. pageType,
  62. }: IWidget) => {
  63. const [ftsData, setFtsData] = useState<IFtsItem[]>();
  64. const [total, setTotal] = useState<number>();
  65. const [loading, setLoading] = useState(false);
  66. const [currPage, setCurrPage] = useState<number>(1);
  67. useEffect(() => {
  68. let url = `/v2/search?view=${view}&key=${keyWord}`;
  69. if (typeof tags !== "undefined") {
  70. url += `&tags=${tags}`;
  71. }
  72. if (bookId) {
  73. url += `&book=${bookId}`;
  74. }
  75. if (orderBy) {
  76. url += `&orderby=${orderBy}`;
  77. }
  78. if (match) {
  79. url += `&match=${match}`;
  80. }
  81. if (pageType) {
  82. url += `&type=${pageType}`;
  83. }
  84. const offset = (currPage - 1) * 10;
  85. url += `&limit=10&offset=${offset}`;
  86. console.log("fetch url", url);
  87. setLoading(true);
  88. get<IFtsResponse>(url)
  89. .then((json) => {
  90. if (json.ok) {
  91. const result: IFtsItem[] = json.data.rows.map((item) => {
  92. return {
  93. book: item.book,
  94. paragraph: item.paragraph,
  95. title: item.title ? item.title : item.paliTitle,
  96. paliTitle: item.paliTitle,
  97. content: item.highlight
  98. ? item.highlight.replaceAll("** ti ", "**ti ")
  99. : item.content,
  100. path: item.path,
  101. };
  102. });
  103. setFtsData(result);
  104. setTotal(json.data.count);
  105. } else {
  106. console.error(json.message);
  107. }
  108. })
  109. .finally(() => setLoading(false));
  110. }, [bookId, currPage, keyWord, match, orderBy, pageType, tags, view]);
  111. return (
  112. <List
  113. style={{ width: "100%" }}
  114. itemLayout="vertical"
  115. size="small"
  116. dataSource={ftsData}
  117. pagination={{
  118. onChange: (page) => {
  119. console.log(page);
  120. setCurrPage(page);
  121. },
  122. showQuickJumper: true,
  123. showSizeChanger: false,
  124. pageSize: 10,
  125. total: total,
  126. position: "both",
  127. showTotal: (total) => {
  128. return `结果: ${total}`;
  129. },
  130. }}
  131. renderItem={(item) => {
  132. let paragraph: number[];
  133. if (view === "title") {
  134. paragraph = [item.paragraph, item.paragraph + 1, item.paragraph + 2];
  135. } else {
  136. paragraph = [item.paragraph - 1, item.paragraph, item.paragraph + 1];
  137. }
  138. let link: string = "";
  139. switch (view) {
  140. case "pali":
  141. link = `/article/para/${item.book}-${item.paragraph}?book=${item.book}&par=${paragraph}&focus=${item.paragraph}`;
  142. break;
  143. case "title":
  144. link = `/article/chapter/${item.book}-${item.paragraph}`;
  145. break;
  146. case "page":
  147. link = `/article/chapter/${item.book}-${item.paragraph}`;
  148. break;
  149. default:
  150. break;
  151. }
  152. let title = "unnamed";
  153. if (item.paliTitle) {
  154. if (item.paliTitle.length > 0) {
  155. title = item.paliTitle;
  156. }
  157. }
  158. return (
  159. <List.Item>
  160. {loading ? (
  161. <div style={{ width: "100%" }}>
  162. <Skeleton active />
  163. </div>
  164. ) : (
  165. <div>
  166. <div>
  167. <PaliText text={item.path ? item.path[0].title : ""} />
  168. </div>
  169. <div>
  170. <Space style={{ color: "gray", fontSize: "80%" }}>
  171. <TocPath
  172. data={item.path?.slice(1)}
  173. style={{ fontSize: "80%" }}
  174. />
  175. {"/"}
  176. <Tag style={{ fontSize: "80%" }}>{item.paragraph}</Tag>
  177. </Space>
  178. </div>
  179. <Title level={4} style={{ fontWeight: 500 }}>
  180. <Link to={link} target="_blank">
  181. {item.title ? item.title : title}
  182. </Link>
  183. </Title>
  184. <div style={{ display: "none" }}>
  185. <Text type="secondary">{item.paliTitle}</Text>
  186. </div>
  187. <div>
  188. <Marked className="search_content" text={item.content} />
  189. </div>
  190. </div>
  191. )}
  192. </List.Item>
  193. );
  194. }}
  195. />
  196. );
  197. };
  198. export default FullTxtSearchResultWidget;