WbwDetail.tsx 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367
  1. import { useEffect, useState } from "react";
  2. import { useIntl } from "react-intl";
  3. import {
  4. Dropdown,
  5. Tabs,
  6. Divider,
  7. Button,
  8. Switch,
  9. Rate,
  10. Space,
  11. Tooltip,
  12. } from "antd";
  13. import {
  14. SaveOutlined,
  15. VerticalAlignBottomOutlined,
  16. VerticalAlignTopOutlined,
  17. } from "@ant-design/icons";
  18. import type { IWbw, IWbwAttachment, IWbwField, TFieldName } from "./WbwWord";
  19. import WbwDetailBasic from "./WbwDetailBasic";
  20. import WbwDetailBookMark from "./WbwDetailBookMark";
  21. import WbwDetailNote from "./WbwDetailNote";
  22. import WbwDetailAdvance from "./WbwDetailAdvance";
  23. import { LockIcon, UnLockIcon } from "../../../assets/icon";
  24. import type { IAttachmentRequest } from "../../../api/Attachments";
  25. import WbwDetailAttachment from "./WbwDetailAttachment";
  26. import { useAppSelector } from "../../../hooks";
  27. import { currentUser } from "../../../reducers/current-user";
  28. import DiscussionButton from "../../discussion/DiscussionButton";
  29. import { courseUser } from "../../../reducers/course-user";
  30. import { tempSet } from "../../../reducers/setting";
  31. import { PopPlacement } from "./WbwPali";
  32. import store from "../../../store";
  33. import TagSelectButton from "../../tag/TagSelectButton";
  34. import type { ITagMapData } from "../../../api/Tag";
  35. interface IWidget {
  36. data: IWbw;
  37. visible?: boolean;
  38. popIsTop?: boolean;
  39. readonly?: boolean;
  40. onClose?: Function;
  41. onSave?: Function;
  42. onAttachmentSelectOpen?: Function;
  43. onPopTopChange?: Function;
  44. onTagCreate?: Function;
  45. }
  46. const WbwDetailWidget = ({
  47. data,
  48. visible = true,
  49. popIsTop = false,
  50. readonly = false,
  51. onClose,
  52. onSave,
  53. onAttachmentSelectOpen,
  54. onPopTopChange,
  55. onTagCreate,
  56. }: IWidget) => {
  57. const intl = useIntl();
  58. const [currWbwData, setCurrWbwData] = useState<IWbw>(
  59. JSON.parse(JSON.stringify(data))
  60. );
  61. const [tabKey, setTabKey] = useState<string>("basic");
  62. const currUser = useAppSelector(currentUser);
  63. useEffect(() => {
  64. console.debug("input data", data);
  65. setCurrWbwData(JSON.parse(JSON.stringify(data)));
  66. }, [data]);
  67. function fieldChanged(field: TFieldName, value: string) {
  68. console.log("field", field, "value", value);
  69. const origin = JSON.parse(JSON.stringify(currWbwData));
  70. switch (field) {
  71. case "note":
  72. origin.note = { value: value, status: 7 };
  73. break;
  74. case "bookMarkColor":
  75. origin.bookMarkColor = { value: parseInt(value), status: 7 };
  76. break;
  77. case "bookMarkText":
  78. origin.bookMarkText = { value: value, status: 7 };
  79. break;
  80. case "word":
  81. origin.word = { value: value, status: 7 };
  82. break;
  83. case "real":
  84. origin.real = { value: value, status: 7 };
  85. break;
  86. case "meaning":
  87. origin.meaning = { value: value, status: 7 };
  88. break;
  89. case "factors":
  90. origin.factors = { value: value, status: 7 };
  91. break;
  92. case "factorMeaning":
  93. origin.factorMeaning = { value: value, status: 7 };
  94. break;
  95. case "parent":
  96. origin.parent = { value: value, status: 7 };
  97. break;
  98. case "parent2":
  99. origin.parent2 = { value: value, status: 7 };
  100. break;
  101. case "grammar2":
  102. origin.grammar2 = { value: value, status: 7 };
  103. break;
  104. case "case":
  105. const arrCase = value.split("#");
  106. origin.case = { value: value, status: 7 };
  107. origin.type = { value: arrCase[0] ? arrCase[0] : "", status: 7 };
  108. origin.grammar = { value: arrCase[1] ? arrCase[1] : "", status: 7 };
  109. break;
  110. case "relation":
  111. origin.relation = { value: value, status: 7 };
  112. break;
  113. case "confidence":
  114. origin.confidence = parseFloat(value);
  115. break;
  116. case "locked":
  117. origin.locked = JSON.parse(value);
  118. break;
  119. case "attachments":
  120. //mData.attachments = value;
  121. break;
  122. default:
  123. break;
  124. }
  125. console.debug("origin", origin);
  126. setCurrWbwData(origin);
  127. }
  128. const userInCourse = useAppSelector(courseUser);
  129. if (userInCourse && userInCourse.role === "student") {
  130. }
  131. return (
  132. <div
  133. className="wbw_detail"
  134. style={{
  135. minWidth: 450,
  136. }}
  137. >
  138. <Tabs
  139. size="small"
  140. type="card"
  141. tabBarExtraContent={
  142. <Space>
  143. <Tooltip
  144. title={popIsTop ? "底端弹窗" : "顶端弹窗"}
  145. getTooltipContainer={(_node: HTMLElement) =>
  146. document.getElementsByClassName("wbw_detail")[0] as HTMLElement
  147. }
  148. >
  149. <Button
  150. type="text"
  151. icon={
  152. popIsTop ? (
  153. <VerticalAlignBottomOutlined />
  154. ) : (
  155. <VerticalAlignTopOutlined />
  156. )
  157. }
  158. onClick={() => {
  159. store.dispatch(
  160. tempSet({
  161. key: PopPlacement,
  162. value: !popIsTop,
  163. })
  164. );
  165. if (typeof onPopTopChange !== "undefined") {
  166. //onPopTopChange(popIsTop);
  167. }
  168. }}
  169. />
  170. </Tooltip>
  171. <TagSelectButton
  172. selectorTitle={data.word.value}
  173. resType="wbw"
  174. resId={data.uid}
  175. onOpen={() => {
  176. if (typeof onClose !== "undefined") {
  177. onClose();
  178. }
  179. }}
  180. onCreate={(tags: ITagMapData[]) => {
  181. if (typeof onTagCreate !== "undefined") {
  182. onTagCreate(tags);
  183. }
  184. }}
  185. />
  186. <DiscussionButton
  187. initCount={data.hasComment ? 1 : 0}
  188. hideCount
  189. resId={data.uid}
  190. resType="wbw"
  191. />
  192. </Space>
  193. }
  194. onChange={(activeKey: string) => {
  195. setTabKey(activeKey);
  196. }}
  197. items={[
  198. {
  199. label: intl.formatMessage({ id: "buttons.basic" }),
  200. key: "basic",
  201. children: (
  202. <WbwDetailBasic
  203. visible={visible && tabKey === "basic"}
  204. data={currWbwData}
  205. readonly={readonly}
  206. onChange={(e: IWbwField) => {
  207. console.debug("WbwDetailBasic onchange", e);
  208. fieldChanged(e.field, e.value);
  209. }}
  210. onRelationAdd={onClose}
  211. />
  212. ),
  213. },
  214. {
  215. label: intl.formatMessage({ id: "buttons.bookmark" }),
  216. key: "bookmark",
  217. children: (
  218. <div style={{ minHeight: 270 }}>
  219. <WbwDetailBookMark
  220. data={data}
  221. onChange={(e: IWbwField) => {
  222. fieldChanged(e.field, e.value);
  223. }}
  224. />
  225. </div>
  226. ),
  227. },
  228. {
  229. label: intl.formatMessage({ id: "buttons.note" }),
  230. key: "note",
  231. children: (
  232. <div style={{ minHeight: 270 }}>
  233. <WbwDetailNote
  234. data={data}
  235. onChange={(e: IWbwField) => {
  236. fieldChanged(e.field, e.value);
  237. }}
  238. />
  239. </div>
  240. ),
  241. },
  242. {
  243. label: intl.formatMessage({ id: "buttons.spell" }),
  244. key: "spell",
  245. children: (
  246. <div style={{ minHeight: 270 }}>
  247. <WbwDetailAdvance
  248. data={currWbwData}
  249. onChange={(e: IWbwField) => {
  250. fieldChanged(e.field, e.value);
  251. }}
  252. />
  253. </div>
  254. ),
  255. },
  256. {
  257. label: intl.formatMessage({ id: "buttons.attachments" }),
  258. key: "attachments",
  259. children: (
  260. <div style={{ minHeight: 270 }}>
  261. <WbwDetailAttachment
  262. data={currWbwData}
  263. onUpload={(fileList: IAttachmentRequest[]) => {
  264. const mData = JSON.parse(JSON.stringify(currWbwData));
  265. mData.attachments = fileList.map((item) => {
  266. return {
  267. id: item.id,
  268. title: item.title,
  269. size: item.size ? item.size : 0,
  270. content_type: item.content_type,
  271. };
  272. });
  273. setCurrWbwData(mData);
  274. }}
  275. onDialogOpen={(open: boolean) => {
  276. if (typeof onAttachmentSelectOpen !== "undefined") {
  277. onAttachmentSelectOpen(open);
  278. }
  279. }}
  280. onChange={(value: IWbwAttachment[]) => {
  281. const mData = JSON.parse(JSON.stringify(currWbwData));
  282. mData.attachments = value;
  283. setCurrWbwData(mData);
  284. //fieldChanged(e.field, e.value);
  285. }}
  286. />
  287. </div>
  288. ),
  289. },
  290. ]}
  291. />
  292. <Divider style={{ margin: "4px 0" }}></Divider>
  293. <div style={{ display: "flex", justifyContent: "space-between" }}>
  294. <Switch
  295. checkedChildren={<LockIcon />}
  296. unCheckedChildren={<UnLockIcon />}
  297. />
  298. <div>
  299. {"信心指数"}
  300. <Rate
  301. defaultValue={data.confidence * 5}
  302. onChange={(value: number) => {
  303. fieldChanged("confidence", (value / 5).toString());
  304. }}
  305. />
  306. </div>
  307. </div>
  308. <Divider style={{ margin: "4px 0" }}></Divider>
  309. <div style={{ display: "flex", justifyContent: "space-between" }}>
  310. <div>
  311. <Button
  312. danger
  313. onClick={() => {
  314. if (typeof onClose !== "undefined") {
  315. onClose();
  316. }
  317. }}
  318. >
  319. {intl.formatMessage({ id: "buttons.cancel" })}
  320. </Button>
  321. </div>
  322. <Dropdown.Button
  323. disabled={readonly}
  324. style={{ width: "unset" }}
  325. type="primary"
  326. menu={{
  327. items: [
  328. {
  329. key: "user-dict-public",
  330. label: intl.formatMessage({ id: "buttons.save.publish" }),
  331. disabled: currUser?.roles?.includes("basic"),
  332. },
  333. {
  334. key: "user-dict-private",
  335. label: intl.formatMessage({ id: "buttons.save.my.dict" }),
  336. },
  337. ],
  338. onClick: (e) => {
  339. if (typeof onSave !== "undefined") {
  340. //保存并发布
  341. if (e.key === "user-dict-public") {
  342. onSave(currWbwData, true, true);
  343. } else {
  344. onSave(currWbwData, true, false);
  345. }
  346. }
  347. },
  348. }}
  349. onClick={() => {
  350. if (typeof onSave !== "undefined") {
  351. onSave(currWbwData, false);
  352. }
  353. }}
  354. >
  355. <SaveOutlined />
  356. {intl.formatMessage({ id: "buttons.save" })}
  357. </Dropdown.Button>
  358. </div>
  359. </div>
  360. );
  361. };
  362. export default WbwDetailWidget;