ChapterInChannel.tsx 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. import { Button, Col, List, Modal, Row, Space, Tabs } from "antd";
  2. import { Typography } from "antd";
  3. import { LikeOutlined, EyeOutlined } from "@ant-design/icons";
  4. import { TinyLine } from "@ant-design/plots";
  5. import { IChannelApiData } from "../api/Channel";
  6. import ChannelListItem from "../channel/ChannelListItem";
  7. import TimeShow from "../general/TimeShow";
  8. import { useIntl } from "react-intl";
  9. import { Link, useSearchParams } from "react-router-dom";
  10. import { IStudio } from "../auth/Studio";
  11. import { useState } from "react";
  12. import { ProgressOutlinedIcon } from "../../assets/icon";
  13. import { ArticleMode } from "../article/Article";
  14. const { Text } = Typography;
  15. export interface IChapterChannelData {
  16. channel: IChannelApiData;
  17. studio: IStudio;
  18. progress: number;
  19. progressLine?: number[];
  20. hit: number;
  21. like: number;
  22. updatedAt: string;
  23. }
  24. interface IWidgetChapterInChannel {
  25. data: IChapterChannelData[];
  26. book: number;
  27. para: number;
  28. channelId?: string[];
  29. openTarget?: React.HTMLAttributeAnchorTarget;
  30. }
  31. const ChapterInChannelWidget = ({
  32. data,
  33. book,
  34. para,
  35. channelId,
  36. openTarget = "_blank",
  37. }: IWidgetChapterInChannel) => {
  38. const intl = useIntl(); //i18n
  39. const [searchParams, setSearchParams] = useSearchParams();
  40. const [open, setOpen] = useState(false);
  41. const ChannelList = (channels: IChapterChannelData[]): JSX.Element => {
  42. return channels.length ? (
  43. <List
  44. style={{ maxWidth: 500 }}
  45. itemLayout="vertical"
  46. size="small"
  47. dataSource={channels}
  48. pagination={
  49. channelId
  50. ? undefined
  51. : {
  52. showQuickJumper: false,
  53. showSizeChanger: false,
  54. pageSize: 5,
  55. total: channels.length,
  56. position: "bottom",
  57. showTotal: (total) => {
  58. return `结果: ${total}`;
  59. },
  60. }
  61. }
  62. renderItem={(item, id) => {
  63. let url = `/article/chapter/${book}-${para}`;
  64. const currMode = searchParams.get("mode");
  65. url += currMode ? `?mode=${currMode}` : "?mode=read";
  66. url += item.channel.id ? `&channel=${item.channel.id}` : "";
  67. return (
  68. <List.Item key={id}>
  69. <Row>
  70. <Col span={12}>
  71. <Link to={url}>
  72. <ChannelListItem
  73. channel={item.channel}
  74. studio={item.studio}
  75. />
  76. </Link>
  77. </Col>
  78. <Col span={12}>
  79. {item.progressLine ? (
  80. <TinyLine
  81. height={32}
  82. width={150}
  83. autoFit={false}
  84. data={item.progressLine}
  85. smooth={true}
  86. />
  87. ) : (
  88. <></>
  89. )}
  90. </Col>
  91. </Row>
  92. <div style={{ display: "flex", justifyContent: "space-between" }}>
  93. <Text type="secondary">
  94. <Space style={{ paddingLeft: "2em" }}>
  95. <EyeOutlined />
  96. {item.hit} | <LikeOutlined />
  97. {item.like} |
  98. <TimeShow updatedAt={item.updatedAt} /> |
  99. <ProgressOutlinedIcon />
  100. {`${item.progress}%`}
  101. </Space>
  102. </Text>
  103. </div>
  104. </List.Item>
  105. );
  106. }}
  107. />
  108. ) : (
  109. <></>
  110. );
  111. };
  112. const handleCancel = () => {
  113. setOpen(false);
  114. };
  115. if (typeof channelId !== "undefined") {
  116. const channelList = ChannelList(
  117. data.filter((item) => channelId.includes(item.channel.id))
  118. );
  119. return (
  120. <div>
  121. <div>{channelList}</div>
  122. <div>
  123. <Button
  124. type="link"
  125. onClick={() => {
  126. setOpen(true);
  127. }}
  128. >
  129. {intl.formatMessage({ id: "buttons.more" })}
  130. </Button>
  131. </div>
  132. <Modal
  133. title="版本选择"
  134. open={open}
  135. onCancel={handleCancel}
  136. onOk={handleCancel}
  137. >
  138. <div>{ChannelList(data)}</div>
  139. </Modal>
  140. </div>
  141. );
  142. } else {
  143. return (
  144. <Tabs
  145. items={[
  146. {
  147. label: intl.formatMessage({ id: "channel.type.translation.label" }),
  148. key: "translation",
  149. children: ChannelList(
  150. data.filter((item) => item.channel.type === "translation")
  151. ),
  152. },
  153. {
  154. label: intl.formatMessage({ id: "channel.type.nissaya.label" }),
  155. key: "nissaya",
  156. children: ChannelList(
  157. data.filter((item) => item.channel.type === "nissaya")
  158. ),
  159. },
  160. {
  161. label: intl.formatMessage({ id: "channel.type.commentary.label" }),
  162. key: "commentary",
  163. children: ChannelList(
  164. data.filter((item) => item.channel.type === "commentary")
  165. ),
  166. },
  167. {
  168. label: intl.formatMessage({ id: "channel.type.original.label" }),
  169. key: "original",
  170. children: ChannelList(
  171. data.filter((item) => item.channel.type === "original")
  172. ),
  173. },
  174. ]}
  175. />
  176. );
  177. }
  178. };
  179. export default ChapterInChannelWidget;