Article.tsx 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. import { useEffect, useState } from "react";
  2. import { Divider, message, Tag } from "antd";
  3. import { modeChange } from "../../reducers/article-mode";
  4. import { get } from "../../request";
  5. import store from "../../store";
  6. import { IArticleDataResponse, IArticleResponse } from "../api/Article";
  7. import ArticleView from "./ArticleView";
  8. import { ICourseCurrUserResponse } from "../api/Course";
  9. import { ICourseUser, signIn } from "../../reducers/course-user";
  10. import { ITextbook, refresh } from "../../reducers/current-course";
  11. import ExerciseList from "./ExerciseList";
  12. import ExerciseAnswer from "../course/ExerciseAnswer";
  13. import "./article.css";
  14. import CommentListCard from "../comment/CommentListCard";
  15. import TocTree from "./TocTree";
  16. import PaliText from "../template/Wbw/PaliText";
  17. import ArticleSkeleton from "./ArticleSkeleton";
  18. export type ArticleMode = "read" | "edit" | "wbw";
  19. export type ArticleType =
  20. | "article"
  21. | "chapter"
  22. | "paragraph"
  23. | "cs-para"
  24. | "sent"
  25. | "sim"
  26. | "page"
  27. | "textbook"
  28. | "exercise"
  29. | "exercise-list"
  30. | "corpus_sent/original"
  31. | "corpus_sent/commentary"
  32. | "corpus_sent/nissaya"
  33. | "corpus_sent/translation";
  34. interface IWidgetArticle {
  35. type?: ArticleType;
  36. articleId?: string;
  37. mode?: ArticleMode;
  38. active?: boolean;
  39. onArticleChange?: Function;
  40. onFinal?: Function;
  41. }
  42. const Widget = ({
  43. type,
  44. articleId,
  45. mode = "read",
  46. active = false,
  47. onArticleChange,
  48. onFinal,
  49. }: IWidgetArticle) => {
  50. const [articleData, setArticleData] = useState<IArticleDataResponse>();
  51. const [articleMode, setArticleMode] = useState<ArticleMode>(mode);
  52. const [extra, setExtra] = useState(<></>);
  53. const [showSkeleton, setShowSkeleton] = useState(true);
  54. let channels: string[] = [];
  55. if (typeof articleId !== "undefined") {
  56. const aId = articleId.split("_");
  57. if (aId.length > 1) {
  58. channels = aId.slice(1);
  59. }
  60. }
  61. useEffect(() => {
  62. /**
  63. * 由课本进入查询当前用户的权限和channel
  64. */
  65. if (
  66. type === "textbook" ||
  67. type === "exercise" ||
  68. type === "exercise-list"
  69. ) {
  70. if (typeof articleId !== "undefined") {
  71. const id = articleId.split("_");
  72. get<ICourseCurrUserResponse>(`/v2/course-curr?course_id=${id[0]}`).then(
  73. (response) => {
  74. console.log("course user", response);
  75. if (response.ok) {
  76. const it: ICourseUser = {
  77. channelId: response.data.channel_id,
  78. role: response.data.role,
  79. };
  80. store.dispatch(signIn(it));
  81. /**
  82. * redux发布课程信息
  83. */
  84. const ic: ITextbook = {
  85. courseId: id[0],
  86. articleId: id[1],
  87. };
  88. store.dispatch(refresh(ic));
  89. }
  90. }
  91. );
  92. }
  93. }
  94. }, [articleId, type]);
  95. useEffect(() => {
  96. console.log("mode", mode, articleMode);
  97. if (!active) {
  98. return;
  99. }
  100. setArticleMode(mode);
  101. //发布mode变更
  102. store.dispatch(modeChange(mode));
  103. if (mode !== articleMode && mode !== "read" && articleMode !== "read") {
  104. console.log("set mode", mode, articleMode);
  105. return;
  106. }
  107. if (typeof type !== "undefined" && typeof articleId !== "undefined") {
  108. let url = "";
  109. switch (type) {
  110. case "article":
  111. const aIds = articleId.split("_");
  112. url = `/v2/article/${aIds[0]}?mode=${mode}`;
  113. if (aIds.length > 1) {
  114. const channels = aIds.slice(1);
  115. url += "&channel=" + channels.join();
  116. }
  117. break;
  118. case "textbook":
  119. /**
  120. * 从课本进入
  121. * id两部分组成
  122. * 课程id_文章id
  123. */
  124. const id = articleId.split("_");
  125. if (id.length < 2) {
  126. message.error("文章id期待2个,实际只给了一个");
  127. return;
  128. }
  129. url = `/v2/article/${id[1]}?mode=${mode}&view=textbook&course=${id[0]}`;
  130. break;
  131. case "exercise":
  132. /**
  133. * 从练习进入
  134. * id 由4部分组成
  135. * 课程id_文章id_练习id_username
  136. */
  137. const exerciseId = articleId.split("_");
  138. if (exerciseId.length < 3) {
  139. message.error("练习id期待3个");
  140. return;
  141. }
  142. console.log("exe", exerciseId);
  143. url = `/v2/article/${exerciseId[1]}?mode=${mode}&course=${exerciseId[0]}&exercise=${exerciseId[2]}&user=${exerciseId[3]}`;
  144. setExtra(
  145. <ExerciseAnswer
  146. courseId={exerciseId[0]}
  147. articleId={exerciseId[1]}
  148. exerciseId={exerciseId[2]}
  149. />
  150. );
  151. break;
  152. case "exercise-list":
  153. /**
  154. * 从练习进入
  155. * id 由3部分组成
  156. * 课程id_文章id_练习id
  157. */
  158. const exerciseListId = articleId.split("_");
  159. if (exerciseListId.length < 3) {
  160. message.error("练习id期待3个");
  161. return;
  162. }
  163. url = `/v2/article/${exerciseListId[1]}?mode=${mode}&course=${exerciseListId[0]}&exercise=${exerciseListId[2]}`;
  164. //url = `/v2/article/${exerciseListId[1]}?mode=${mode}&course=${exerciseListId[0]}&exercise=${exerciseListId[2]}&list=true`;
  165. setExtra(
  166. <ExerciseList
  167. courseId={exerciseListId[0]}
  168. articleId={exerciseListId[1]}
  169. exerciseId={exerciseListId[2]}
  170. />
  171. );
  172. break;
  173. default:
  174. const aid = articleId.split("_");
  175. url = `/v2/corpus/${type}/${articleId}/${mode}?mode=${mode}`;
  176. if (aid.length > 0) {
  177. const channels = aid.slice(1).join();
  178. url += `&channels=${channels}`;
  179. }
  180. break;
  181. }
  182. console.log("url", url);
  183. setShowSkeleton(true);
  184. get<IArticleResponse>(url).then((json) => {
  185. console.log("article", json);
  186. if (json.ok) {
  187. setArticleData(json.data);
  188. setShowSkeleton(false);
  189. setExtra(
  190. <TocTree
  191. treeData={json.data.toc?.map((item) => {
  192. const strTitle = item.title ? item.title : item.pali_title;
  193. const progress = item.progress?.map((item, id) => (
  194. <Tag key={id}>{Math.round(item * 100)}</Tag>
  195. ));
  196. return {
  197. key: `${item.book}-${item.paragraph}`,
  198. title: (
  199. <>
  200. <PaliText text={strTitle} />
  201. {progress}
  202. </>
  203. ),
  204. level: item.level,
  205. };
  206. })}
  207. onSelect={(keys: string[]) => {
  208. console.log(keys);
  209. if (typeof onArticleChange !== "undefined" && keys.length > 0) {
  210. const aid = articleId.split("_");
  211. const channels =
  212. aid.length > 1 ? "_" + aid.slice(1).join("_") : undefined;
  213. onArticleChange(keys[0] + channels);
  214. }
  215. }}
  216. />
  217. );
  218. } else {
  219. message.error(json.message);
  220. }
  221. });
  222. }
  223. }, [active, type, articleId, mode, articleMode]);
  224. return (
  225. <div>
  226. {showSkeleton ? (
  227. <ArticleSkeleton />
  228. ) : (
  229. <ArticleView
  230. id={articleData?.uid}
  231. title={articleData?.title}
  232. subTitle={articleData?.subtitle}
  233. summary={articleData?.summary}
  234. content={articleData ? articleData.content : ""}
  235. html={articleData?.html}
  236. path={articleData?.path}
  237. created_at={articleData?.created_at}
  238. updated_at={articleData?.updated_at}
  239. channels={channels}
  240. type={type}
  241. articleId={articleId}
  242. />
  243. )}
  244. {extra}
  245. <Divider />
  246. <CommentListCard resId={articleData?.uid} resType="article" />
  247. </div>
  248. );
  249. };
  250. export default Widget;