ChapterInChannel.tsx 5.2 KB

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