visuddhinanda 1 month ago
parent
commit
ae6f2aba9e
100 changed files with 1707 additions and 380 deletions
  1. 12 0
      dashboard-v6/backup/components/README.md
  2. 1 1
      dashboard-v6/backup/components/admin/HeadBar.tsx
  3. 1 1
      dashboard-v6/backup/components/admin/LeftSider.tsx
  4. 0 0
      dashboard-v6/backup/components/admin/api/ApiDelayHour.tsx
  5. 0 0
      dashboard-v6/backup/components/admin/api/ApiGauge.tsx
  6. 0 0
      dashboard-v6/backup/components/admin/relation/CaseSelect.tsx
  7. 2 2
      dashboard-v6/backup/components/admin/relation/DataImport.tsx
  8. 3 8
      dashboard-v6/backup/components/admin/relation/GrammarSelect.tsx
  9. 1 1
      dashboard-v6/backup/components/admin/relation/NissayaEndingEdit.tsx
  10. 3 3
      dashboard-v6/backup/components/admin/relation/RelationEdit.tsx
  11. 5 2
      dashboard-v6/backup/components/ai/AiAssistantSelect.tsx
  12. 3 3
      dashboard-v6/backup/components/ai/AiModelCreate.tsx
  13. 3 3
      dashboard-v6/backup/components/ai/AiModelEdit.tsx
  14. 10 10
      dashboard-v6/backup/components/ai/AiModelList.tsx
  15. 5 5
      dashboard-v6/backup/components/ai/AiModelLogList.tsx
  16. 3 23
      dashboard-v6/backup/components/ai/AiTranslate.tsx
  17. 0 0
      dashboard-v6/backup/components/ai/ModelSelector.tsx
  18. 6 3
      dashboard-v6/backup/components/anthology/AnthologyCreate.tsx
  19. 14 12
      dashboard-v6/backup/components/anthology/AnthologyList.tsx
  20. 67 0
      dashboard-v6/backup/components/anthology/AnthologyModal.tsx
  21. 1 1
      dashboard-v6/backup/components/anthology/AnthologySelect.tsx
  22. 8 6
      dashboard-v6/backup/components/anthology/AnthologyTocTree.tsx
  23. 4 6
      dashboard-v6/backup/components/anthology/EditableTocTree.tsx
  24. 4 12
      dashboard-v6/backup/components/anthology/TextBookToc.tsx
  25. 4 4
      dashboard-v6/backup/components/api/Article.ts
  26. 0 0
      dashboard-v6/backup/components/api/Attachments.ts
  27. 1 1
      dashboard-v6/backup/components/api/Auth.ts
  28. 2 2
      dashboard-v6/backup/components/api/Channel.ts
  29. 88 0
      dashboard-v6/backup/components/api/Comment.ts
  30. 7 7
      dashboard-v6/backup/components/api/Corpus.ts
  31. 227 0
      dashboard-v6/backup/components/api/Course.ts
  32. 3 3
      dashboard-v6/backup/components/api/Dict.ts
  33. 0 0
      dashboard-v6/backup/components/api/Exp.ts
  34. 80 0
      dashboard-v6/backup/components/api/Group.ts
  35. 0 0
      dashboard-v6/backup/components/api/Guide.ts
  36. 46 0
      dashboard-v6/backup/components/api/Share.ts
  37. 2 2
      dashboard-v6/backup/components/api/Suggestion.ts
  38. 84 0
      dashboard-v6/backup/components/api/Tag.ts
  39. 4 4
      dashboard-v6/backup/components/api/Term.ts
  40. 45 0
      dashboard-v6/backup/components/api/Transfer.ts
  41. 0 0
      dashboard-v6/backup/components/api/ai.ts
  42. 56 0
      dashboard-v6/backup/components/api/like.ts
  43. 43 0
      dashboard-v6/backup/components/api/notification.ts
  44. 260 0
      dashboard-v6/backup/components/api/task.ts
  45. 0 0
      dashboard-v6/backup/components/api/token.ts
  46. 51 0
      dashboard-v6/backup/components/api/view.ts
  47. 3 3
      dashboard-v6/backup/components/api/webhook.ts
  48. 82 0
      dashboard-v6/backup/components/article/AddToAnthology.tsx
  49. 91 0
      dashboard-v6/backup/components/article/AnchorNav.tsx
  50. 1 1
      dashboard-v6/backup/components/article/AnthologiesAtArticle.tsx
  51. 1 1
      dashboard-v6/backup/components/article/AnthologyCard.tsx
  52. 126 0
      dashboard-v6/backup/components/article/AnthologyDetail.tsx
  53. 5 5
      dashboard-v6/backup/components/article/AnthologyInfoEdit.tsx
  54. 1 1
      dashboard-v6/backup/components/article/AnthologyList.tsx
  55. 3 3
      dashboard-v6/backup/components/article/AnthologyStudioList.tsx
  56. 2 35
      dashboard-v6/backup/components/article/Article.tsx
  57. 2 2
      dashboard-v6/backup/components/article/ArticleCard.tsx
  58. 2 2
      dashboard-v6/backup/components/article/ArticleCardMainMenu.tsx
  59. 7 8
      dashboard-v6/backup/components/article/ArticleCreate.tsx
  60. 4 4
      dashboard-v6/backup/components/article/ArticleDrawer.tsx
  61. 23 12
      dashboard-v6/backup/components/article/ArticleEdit.tsx
  62. 3 3
      dashboard-v6/backup/components/article/ArticleEditDrawer.tsx
  63. 1 1
      dashboard-v6/backup/components/article/ArticleEditTools.tsx
  64. 18 23
      dashboard-v6/backup/components/article/ArticleList.tsx
  65. 0 0
      dashboard-v6/backup/components/article/ArticleListModal.tsx
  66. 10 10
      dashboard-v6/backup/components/article/ArticleListPublic.tsx
  67. 2 2
      dashboard-v6/backup/components/article/ArticlePrevDrawer.tsx
  68. 0 0
      dashboard-v6/backup/components/article/ArticleSkeleton.tsx
  69. 25 24
      dashboard-v6/backup/components/article/ArticleView.tsx
  70. 4 4
      dashboard-v6/backup/components/article/ChapterToc.tsx
  71. 1 3
      dashboard-v6/backup/components/article/EditableTree.tsx
  72. 1 1
      dashboard-v6/backup/components/article/EditableTreeNode.tsx
  73. 2 2
      dashboard-v6/backup/components/article/ExerciseList.tsx
  74. 1 1
      dashboard-v6/backup/components/article/Find.tsx
  75. 0 0
      dashboard-v6/backup/components/article/MainMenu.tsx
  76. 1 1
      dashboard-v6/backup/components/article/ModeSwitch.tsx
  77. 1 1
      dashboard-v6/backup/components/article/Nav.tsx
  78. 7 22
      dashboard-v6/backup/components/article/Navigate.tsx
  79. 2 2
      dashboard-v6/backup/components/article/NavigateButton.tsx
  80. 4 5
      dashboard-v6/backup/components/article/PaliTextToc.tsx
  81. 1 1
      dashboard-v6/backup/components/article/ProTabs.tsx
  82. 2 11
      dashboard-v6/backup/components/article/RightPanel.tsx
  83. 1 1
      dashboard-v6/backup/components/article/RightToolsSwitch.tsx
  84. 1 1
      dashboard-v6/backup/components/article/TermShell.tsx
  85. 2 3
      dashboard-v6/backup/components/article/TocTree.tsx
  86. 4 4
      dashboard-v6/backup/components/article/Token.tsx
  87. 1 1
      dashboard-v6/backup/components/article/TokenModal.tsx
  88. 0 0
      dashboard-v6/backup/components/article/ToolButton.tsx
  89. 2 2
      dashboard-v6/backup/components/article/ToolButtonDiscussion.tsx
  90. 4 4
      dashboard-v6/backup/components/article/ToolButtonNav.tsx
  91. 2 2
      dashboard-v6/backup/components/article/ToolButtonNavMore.tsx
  92. 0 0
      dashboard-v6/backup/components/article/ToolButtonNavSliceTitle.tsx
  93. 2 2
      dashboard-v6/backup/components/article/ToolButtonPr.tsx
  94. 9 9
      dashboard-v6/backup/components/article/ToolButtonSearch.tsx
  95. 1 1
      dashboard-v6/backup/components/article/ToolButtonSetting.tsx
  96. 1 1
      dashboard-v6/backup/components/article/ToolButtonTag.tsx
  97. 3 3
      dashboard-v6/backup/components/article/ToolButtonToc.tsx
  98. 3 25
      dashboard-v6/backup/components/article/TreeText.tsx
  99. 71 0
      dashboard-v6/backup/components/article/TypeAnthology.tsx
  100. 2 2
      dashboard-v6/backup/components/article/TypeArticle.tsx

+ 12 - 0
dashboard-v6/backup/components/README.md

@@ -0,0 +1,12 @@
+# 组件
+
+## 目录
+
+某栏目的专用组件放在以该栏目命名的目录下
+
+- `libray` 前台(阅读)的栏目
+  - `栏目名称` 某栏目的专用组件
+  - `组件1.tsx` libray 下面的栏目的公共组件
+- `studio` 用户后台(课程发布等)
+
+nut目录为练习用途。里面的内容可能会被删除。**线上不要使用**。

+ 1 - 1
dashboard-v6/src/components/admin/HeadBar.tsx → dashboard-v6/backup/components/admin/HeadBar.tsx

@@ -1,4 +1,4 @@
-import { Link } from "react-router-dom";
+import { Link } from "react-router";
 import { Input, Layout, Space } from "antd";
 
 import img_banner from "../../assets/studio/images/wikipali_banner.svg";

+ 1 - 1
dashboard-v6/src/components/admin/LeftSider.tsx → dashboard-v6/backup/components/admin/LeftSider.tsx

@@ -1,4 +1,4 @@
-import { Link } from "react-router-dom";
+import { Link } from "react-router";
 import type { MenuProps } from "antd";
 import { Affix, Layout } from "antd";
 import { Menu } from "antd";

+ 0 - 0
dashboard-v6/src/components/admin/api/ApiDelayHour.tsx → dashboard-v6/backup/components/admin/api/ApiDelayHour.tsx


+ 0 - 0
dashboard-v6/src/components/admin/api/ApiGauge.tsx → dashboard-v6/backup/components/admin/api/ApiGauge.tsx


+ 0 - 0
dashboard-v6/src/components/admin/relation/CaseSelect.tsx → dashboard-v6/backup/components/admin/relation/CaseSelect.tsx


+ 2 - 2
dashboard-v6/src/components/admin/relation/DataImport.tsx → dashboard-v6/backup/components/admin/relation/DataImport.tsx

@@ -2,8 +2,8 @@ import { ModalForm, ProFormUploadDragger } from "@ant-design/pro-components";
 import { Form, message } from "antd";
 
 import { API_HOST, get } from "../../../request";
-import { UploadFile } from "antd/es/upload/interface";
-import { IAttachmentResponse } from "../../api/Attachments";
+import type { UploadFile } from "antd/es/upload/interface";
+import type { IAttachmentResponse } from "../../../api/Attachments";
 import modal from "antd/lib/modal";
 import { useIntl } from "react-intl";
 

+ 3 - 8
dashboard-v6/src/components/admin/relation/GrammarSelect.tsx → dashboard-v6/backup/components/admin/relation/GrammarSelect.tsx

@@ -1,4 +1,5 @@
 import { ProFormSelect } from "@ant-design/pro-components";
+import type { JSX } from "react";
 
 import { useIntl } from "react-intl";
 
@@ -7,15 +8,9 @@ interface IWidget {
   trigger?: JSX.Element;
   id?: string;
   hidden?: boolean;
-  onSuccess?: Function;
+  onSuccess?: () => void;
 }
-const GrammarSelectWidget = ({
-  name,
-  trigger = <>{"trigger"}</>,
-  id,
-  hidden = false,
-  onSuccess,
-}: IWidget) => {
+const GrammarSelectWidget = ({ name, hidden = false }: IWidget) => {
   const intl = useIntl();
   const _verb = [
     "n",

+ 1 - 1
dashboard-v6/src/components/admin/relation/NissayaEndingEdit.tsx → dashboard-v6/backup/components/admin/relation/NissayaEndingEdit.tsx

@@ -3,7 +3,7 @@ import { Form, message } from "antd";
 
 import { useState } from "react";
 import { useIntl } from "react-intl";
-import {
+import type {
   INissayaEnding,
   INissayaEndingRequest,
   INissayaEndingResponse,

+ 3 - 3
dashboard-v6/src/components/admin/relation/RelationEdit.tsx → dashboard-v6/backup/components/admin/relation/RelationEdit.tsx

@@ -1,7 +1,7 @@
 import {
   ModalForm,
   ProForm,
-  ProFormInstance,
+  type ProFormInstance,
   ProFormSelect,
   ProFormText,
 } from "@ant-design/pro-components";
@@ -9,7 +9,7 @@ import { Form, message } from "antd";
 
 import { useRef, useState } from "react";
 import { useIntl } from "react-intl";
-import {
+import type {
   IRelation,
   IRelationRequest,
   IRelationResponse,
@@ -74,7 +74,7 @@ const RelationEditWidget = ({
 }: IWidget) => {
   const [title, setTitle] = useState<string | undefined>(id ? "" : "新建");
   const [form] = Form.useForm<IRelation>();
-  const formRef = useRef<ProFormInstance>();
+  const formRef = useRef<ProFormInstance | undefined>(undefined);
   const intl = useIntl();
 
   return (

+ 5 - 2
dashboard-v6/src/components/ai/AiAssistantSelect.tsx → dashboard-v6/backup/components/ai/AiAssistantSelect.tsx

@@ -1,8 +1,11 @@
-import { ProFormSelect, RequestOptionsType } from "@ant-design/pro-components";
+import {
+  ProFormSelect,
+  type RequestOptionsType,
+} from "@ant-design/pro-components";
 import { useIntl } from "react-intl";
 
 import { get } from "../../request";
-import { IUserListResponse } from "../api/Auth";
+import type { IUserListResponse } from "../../api/Auth";
 
 interface IWidget {
   name?: string;

+ 3 - 3
dashboard-v6/src/components/ai/AiModelCreate.tsx → dashboard-v6/backup/components/ai/AiModelCreate.tsx

@@ -1,13 +1,13 @@
 import { useIntl } from "react-intl";
 import {
   ProForm,
-  ProFormInstance,
+  type ProFormInstance,
   ProFormText,
 } from "@ant-design/pro-components";
 import { message } from "antd";
 import { post } from "../../request";
 import { useRef } from "react";
-import { IAiModelRequest, IAiModelResponse } from "../api/ai";
+import type { IAiModelRequest, IAiModelResponse } from "../../api/ai";
 
 interface IWidget {
   studioName?: string;
@@ -15,7 +15,7 @@ interface IWidget {
 }
 const AiModelCreate = ({ studioName, onCreate }: IWidget) => {
   const intl = useIntl();
-  const formRef = useRef<ProFormInstance>();
+  const formRef = useRef<ProFormInstance | undefined>(undefined);
 
   return (
     <ProForm<IAiModelRequest>

+ 3 - 3
dashboard-v6/src/components/ai/AiModelEdit.tsx → dashboard-v6/backup/components/ai/AiModelEdit.tsx

@@ -1,14 +1,14 @@
 import { useIntl } from "react-intl";
 import {
   ProForm,
-  ProFormInstance,
+  type ProFormInstance,
   ProFormText,
   ProFormTextArea,
 } from "@ant-design/pro-components";
 import { message } from "antd";
 import { get, put } from "../../request";
 import { useRef } from "react";
-import { IAiModelRequest, IAiModelResponse } from "../api/ai";
+import type { IAiModelRequest, IAiModelResponse } from "../../api/ai";
 import Publicity from "../studio/Publicity";
 
 interface IWidget {
@@ -18,7 +18,7 @@ interface IWidget {
 }
 const AiModelEdit = ({ studioName, modelId, onChange }: IWidget) => {
   const intl = useIntl();
-  const formRef = useRef<ProFormInstance>();
+  const formRef = useRef<ProFormInstance | undefined>(undefined);
 
   return (
     <ProForm<IAiModelRequest>

+ 10 - 10
dashboard-v6/src/components/ai/AiModelList.tsx → dashboard-v6/backup/components/ai/AiModelList.tsx

@@ -1,7 +1,7 @@
-import { Link } from "react-router-dom";
+import { Link } from "react-router";
 import { useIntl } from "react-intl";
 import { Button, Popover, Tag, Space } from "antd";
-import { ActionType, ProList } from "@ant-design/pro-components";
+import { type ActionType, ProList } from "@ant-design/pro-components";
 import { PlusOutlined } from "@ant-design/icons";
 
 import { get } from "../../request";
@@ -9,7 +9,7 @@ import { get } from "../../request";
 import { useRef, useState } from "react";
 
 import { getSorterUrl } from "../../utils";
-import { IAiModel, IAiModelListResponse } from "../api/ai";
+import type { IAiModel, IAiModelListResponse } from "../../api/ai";
 import AiModelCreate from "./AiModelCreate";
 import PublicityIcon from "../studio/PublicityIcon";
 import ShareModal from "../share/ShareModal";
@@ -24,19 +24,19 @@ const AiModelList = ({ studioName }: IWidget) => {
 
   const [openCreate, setOpenCreate] = useState(false);
 
-  const ref = useRef<ActionType>();
+  const ref = useRef<ActionType | null>(null);
 
   return (
     <>
       <ProList<IAiModel>
         actionRef={ref}
-        onRow={(record) => ({
+        onRow={(_record) => ({
           onClick: () => {},
         })}
         metas={{
           title: {
             dataIndex: "name",
-            render(dom, entity, index, action, schema) {
+            render(_dom, entity, _index, _action, _schema) {
               return (
                 <Space>
                   <PublicityIcon value={entity.privacy} />
@@ -53,22 +53,22 @@ const AiModelList = ({ studioName }: IWidget) => {
             dataIndex: "url",
           },
           subTitle: {
-            render(dom, entity, index, action, schema) {
+            render(_dom, entity, _index, _action, _schema) {
               return <Tag>{entity.model}</Tag>;
             },
           },
           content: {
-            render(dom, entity, index, action, schema) {
+            render(_dom, entity, _index, _action, _schema) {
               return entity.description;
             },
           },
           avatar: {
-            render(dom, entity, index, action, schema) {
+            render(_dom, entity, _index, _action, _schema) {
               return <User {...entity.user} showName={false} />;
             },
           },
           actions: {
-            render(dom, entity, index, action, schema) {
+            render(_dom, entity, _index, _action, _schema) {
               return (
                 <Space>
                   <Link

+ 5 - 5
dashboard-v6/src/components/ai/AiModelLogList.tsx → dashboard-v6/backup/components/ai/AiModelLogList.tsx

@@ -5,7 +5,7 @@ import { useState } from "react";
 
 import { CheckOutlined, WarningOutlined } from "@ant-design/icons";
 
-import { IAiModelLogData, IAiModelLogListResponse } from "../api/ai";
+import type { IAiModelLogData, IAiModelLogListResponse } from "../../api/ai";
 import { get } from "../../request";
 import moment from "moment";
 
@@ -25,7 +25,7 @@ const AiModelLogList = ({ modelId }: IWidget) => {
       metas={{
         title: {},
         subTitle: {
-          render: (dom, entity, index, action, schema) => {
+          render: (_dom, entity, _index, _action, _schema) => {
             return (
               <Space size={0}>
                 <Tag color="blue">{entity.status}</Tag>
@@ -34,7 +34,7 @@ const AiModelLogList = ({ modelId }: IWidget) => {
           },
         },
         description: {
-          render: (dom, entity, index, action, schema) => {
+          render: (_dom, entity, _index, _action, _schema) => {
             const jsonView = (text?: string | null) => {
               return (
                 <div>
@@ -102,7 +102,7 @@ const AiModelLogList = ({ modelId }: IWidget) => {
           },
         },
         avatar: {
-          render(dom, entity, index, action, schema) {
+          render(_dom, entity, _index, _action, _schema) {
             return (
               <>
                 {entity.success ? (
@@ -115,7 +115,7 @@ const AiModelLogList = ({ modelId }: IWidget) => {
           },
         },
         actions: {
-          render: (dom, entity, index, action, schema) => {
+          render: (_dom, entity, _index, _action, _schema) => {
             const date = moment(entity.created_at).toLocaleString();
             return <Text type="secondary">{date}</Text>;
           },

+ 3 - 23
dashboard-v6/src/components/ai/AiTranslate.tsx → dashboard-v6/backup/components/ai/AiTranslate.tsx

@@ -3,8 +3,8 @@ import { useEffect, useState } from "react";
 import { LoadingOutlined } from "@ant-design/icons";
 
 import Marked from "../general/Marked";
-import { get, post } from "../../request";
-import { IAiTranslateRequest, IAiTranslateResponse } from "../api/ai";
+import { get } from "../../request";
+import type { IAiTranslateResponse } from "../../api/ai";
 
 const { Text } = Typography;
 
@@ -16,7 +16,6 @@ interface IAiTranslateWidget {
 }
 
 const AiTranslate = ({
-  origin,
   paragraph,
   autoLoad = false,
   trigger = false,
@@ -33,7 +32,7 @@ const AiTranslate = ({
     if (!autoLoad) {
       return;
     }
-    onTranslatePara();
+    //onTranslatePara();
   }, [paragraph, autoLoad]);
 
   const onTranslatePara = () => {
@@ -52,25 +51,6 @@ const AiTranslate = ({
       .finally(() => setLoading(false));
   };
 
-  const onTranslate = (origin?: string) => {
-    if (typeof origin === "undefined") {
-      return;
-    }
-    const data = { origin: origin };
-    console.info("api request", url, data);
-    setLoading(true);
-    post<IAiTranslateRequest, IAiTranslateResponse>(url, data)
-      .then((json) => {
-        console.debug("api response", json);
-        if (json.ok) {
-          setTranslation(json.data.choices[0].message.content);
-        } else {
-          setError(json.message);
-        }
-      })
-      .finally(() => setLoading(false));
-  };
-
   if (translation) {
     return <Marked text={translation} />;
   } else if (loading) {

+ 0 - 0
dashboard-v6/src/components/ai/ModelSelector.tsx → dashboard-v6/backup/components/ai/ModelSelector.tsx


+ 6 - 3
dashboard-v6/src/components/anthology/AnthologyCreate.tsx → dashboard-v6/backup/components/anthology/AnthologyCreate.tsx

@@ -1,13 +1,16 @@
 import {
   ProForm,
-  ProFormInstance,
+  type ProFormInstance,
   ProFormText,
 } from "@ant-design/pro-components";
 import { useIntl } from "react-intl";
 import { message } from "antd";
 
 import LangSelect from "../general/LangSelect";
-import { IAnthologyCreateRequest, IAnthologyResponse } from "../api/Article";
+import type {
+  IAnthologyCreateRequest,
+  IAnthologyResponse,
+} from "../../api/Article";
 import { post } from "../../request";
 import { useRef } from "react";
 
@@ -23,7 +26,7 @@ interface IWidget {
 }
 const AnthologyCreateWidget = ({ studio, onSuccess }: IWidget) => {
   const intl = useIntl();
-  const formRef = useRef<ProFormInstance>();
+  const formRef = useRef<ProFormInstance | undefined>(undefined);
 
   return (
     <ProForm<IFormData>

+ 14 - 12
dashboard-v6/src/components/anthology/AnthologyList.tsx → dashboard-v6/backup/components/anthology/AnthologyList.tsx

@@ -1,6 +1,6 @@
-import { ActionType, ProTable } from "@ant-design/pro-components";
+import { type ActionType, ProTable } from "@ant-design/pro-components";
 import { useIntl } from "react-intl";
-import { Link } from "react-router-dom";
+import { Link } from "react-router";
 import { message, Modal, Typography } from "antd";
 import { PlusOutlined } from "@ant-design/icons";
 import { Button, Dropdown, Popover } from "antd";
@@ -12,14 +12,17 @@ import {
 } from "@ant-design/icons";
 
 import AnthologyCreate from "./AnthologyCreate";
-import { IAnthologyListResponse, IDeleteResponse } from "../api/Article";
+import type {
+  IAnthologyListResponse,
+  IDeleteResponse,
+} from "../../api/Article";
 import { delete_, get } from "../../request";
 import { PublicityValueEnum } from "../studio/table";
 import { useEffect, useRef, useState } from "react";
 import Share, { EResType } from "../share/Share";
 
-import StudioName, { IStudio } from "../auth/Studio";
-import { IResNumberResponse, renderBadge } from "../channel/ChannelTable";
+import StudioName, { type IStudio } from "../auth/Studio";
+import { type IResNumberResponse, renderBadge } from "../channel/ChannelTable";
 import { fullUrl, getSorterUrl } from "../../utils";
 
 const { Text } = Typography;
@@ -40,12 +43,11 @@ interface IWidget {
   showCol?: string[];
   showCreate?: boolean;
   showOption?: boolean;
-  onTitleClick?: Function;
+  onTitleClick?: (id: string) => void;
 }
 const AnthologyListWidget = ({
   title,
   studioName,
-  showCol,
   showCreate = true,
   showOption = true,
   onTitleClick,
@@ -125,7 +127,7 @@ const AnthologyListWidget = ({
     setIsModalOpen(false);
   };
 
-  const ref = useRef<ActionType>();
+  const ref = useRef<ActionType | null>(null);
   return (
     <>
       <ProTable<IItem>
@@ -149,7 +151,7 @@ const AnthologyListWidget = ({
             key: "title",
             tooltip: "过长会自动收缩",
             ellipsis: true,
-            render: (text, row, index, action) => {
+            render: (_text, row, index) => {
               return (
                 <div key={index}>
                   <div>
@@ -174,7 +176,7 @@ const AnthologyListWidget = ({
             }),
             dataIndex: "studio",
             key: "studio",
-            render: (text, row, index, action) => {
+            render: (_text, row) => {
               return <StudioName data={row.studio} />;
             },
           },
@@ -216,7 +218,7 @@ const AnthologyListWidget = ({
             width: 120,
             hideInTable: !showOption,
             valueType: "option",
-            render: (text, row, index, action) => [
+            render: (_text, row, index) => [
               <Dropdown.Button
                 key={index}
                 type="link"
@@ -378,7 +380,7 @@ const AnthologyListWidget = ({
       />
 
       <Modal
-        destroyOnClose={true}
+        destroyOnHidden={true}
         width={700}
         title={intl.formatMessage({ id: "labels.collaboration" })}
         open={isModalOpen}

+ 67 - 0
dashboard-v6/backup/components/anthology/AnthologyModal.tsx

@@ -0,0 +1,67 @@
+import { useState } from "react";
+import { Modal } from "antd";
+import AnthologyList from "./AnthologyList";
+
+interface IWidget {
+  studioName?: string;
+  trigger?: React.ReactNode;
+  open?: boolean;
+  onClose?: (closed: boolean) => void;
+  onSelect?: (selected: string) => void;
+  onCancel?: () => void;
+}
+const AnthologyModalWidget = ({
+  studioName,
+  trigger,
+  open,
+  onClose,
+  onSelect,
+}: IWidget) => {
+  const [innerOpen, setInnerOpen] = useState(false);
+
+  const isModalOpen = open ?? innerOpen;
+
+  const openModal = () => {
+    if (open === undefined) {
+      setInnerOpen(true);
+    } else {
+      onClose?.(true);
+    }
+  };
+
+  const closeModal = () => {
+    onClose?.(false);
+    if (open === undefined) {
+      setInnerOpen(false);
+    }
+  };
+
+  return (
+    <>
+      <span role="button" tabIndex={0} onClick={openModal}>
+        {trigger}
+      </span>
+
+      <Modal
+        width="80%"
+        title="加入文集"
+        open={isModalOpen}
+        onOk={closeModal}
+        onCancel={closeModal}
+      >
+        <AnthologyList
+          title="选择文集"
+          studioName={studioName}
+          showCreate={false}
+          showOption={false}
+          onTitleClick={(id) => {
+            onSelect?.(id);
+            closeModal();
+          }}
+        />
+      </Modal>
+    </>
+  );
+};
+
+export default AnthologyModalWidget;

+ 1 - 1
dashboard-v6/src/components/anthology/AnthologySelect.tsx → dashboard-v6/backup/components/anthology/AnthologySelect.tsx

@@ -1,7 +1,7 @@
 import { Select } from "antd";
 import { useEffect, useState } from "react";
 import { get } from "../../request";
-import { IAnthologyListResponse } from "../api/Article";
+import type { IAnthologyListResponse } from "../../api/Article";
 
 interface IOptions {
   value: string;

+ 8 - 6
dashboard-v6/src/components/anthology/AnthologyTocTree.tsx → dashboard-v6/backup/components/anthology/AnthologyTocTree.tsx

@@ -1,21 +1,23 @@
 import { useEffect, useState } from "react";
 
 import { get } from "../../request";
-import { IArticleMapListResponse } from "../api/Article";
-import { ListNodeData } from "../article/EditableTree";
+import type { IArticleMapListResponse } from "../../api/Article";
+import type { ListNodeData } from "../article/EditableTree";
 import TocTree from "../article/TocTree";
 
 interface IWidget {
   anthologyId?: string;
   channels?: string[];
-  onSelect?: Function;
-  onClick?: Function;
-  onArticleSelect?: Function;
+  onClick?: (
+    anthologyId: string,
+    id: string,
+    target: "_blank" | "self"
+  ) => void;
+  onArticleSelect?: (anthologyId: string, keys: string[]) => void;
 }
 const AnthologyTocTreeWidget = ({
   anthologyId,
   channels,
-  onSelect,
   onClick,
   onArticleSelect,
 }: IWidget) => {

+ 4 - 6
dashboard-v6/src/components/anthology/EditableTocTree.tsx → dashboard-v6/backup/components/anthology/EditableTocTree.tsx

@@ -5,7 +5,7 @@ import { FolderOpenOutlined } from "@ant-design/icons";
 import { get as getUiLang } from "../../locales";
 
 import { get, post, put } from "../../request";
-import {
+import type {
   IAnthologyDataResponse,
   IArticleCreateRequest,
   IArticleDataResponse,
@@ -14,11 +14,11 @@ import {
   IArticleMapRequest,
   IArticleMapUpdateRequest,
   IArticleResponse,
-} from "../api/Article";
+} from "../../api/Article";
 import ArticleListModal from "../article/ArticleListModal";
 import EditableTree, {
-  ListNodeData,
-  TreeNodeData,
+  type ListNodeData,
+  type TreeNodeData,
 } from "../article/EditableTree";
 import ArticleDrawer from "../article/ArticleDrawer";
 import { fullUrl, randomString } from "../../utils";
@@ -28,14 +28,12 @@ interface IWidget {
   studioName?: string;
   myStudioName?: string;
   anthology?: IAnthologyDataResponse;
-  onSelect?: Function;
 }
 const EditableTocTreeWidget = ({
   anthologyId,
   anthology,
   studioName,
   myStudioName,
-  onSelect,
 }: IWidget) => {
   const [tocData, setTocData] = useState<ListNodeData[]>([]);
   const [addArticle, setAddArticle] = useState<TreeNodeData>();

+ 4 - 12
dashboard-v6/src/components/anthology/TextBookToc.tsx → dashboard-v6/backup/components/anthology/TextBookToc.tsx

@@ -1,22 +1,14 @@
 import { useEffect, useState } from "react";
 import AnthologyTocTree from "./AnthologyTocTree";
 import { get } from "../../request";
-import { ICourseResponse } from "../api/Course";
+import type { ICourseResponse } from "../../api/Course";
 
 interface IWidget {
   courseId?: string | null;
   channels?: string[];
-  onSelect?: Function;
-  onClick?: Function;
-  onArticleSelect?: Function;
+  onClick?: (article: string, target: string) => void;
 }
-const TextBookTocWidget = ({
-  courseId,
-  channels,
-  onSelect,
-  onClick,
-  onArticleSelect,
-}: IWidget) => {
+const TextBookTocWidget = ({ courseId, channels, onClick }: IWidget) => {
   const [anthologyId, setAnthologyId] = useState<string>();
 
   useEffect(() => {
@@ -37,7 +29,7 @@ const TextBookTocWidget = ({
     <AnthologyTocTree
       anthologyId={anthologyId}
       channels={channels}
-      onClick={(anthology: string, article: string, target: string) => {
+      onClick={(_anthology: string, article: string, target: string) => {
         console.debug("AnthologyTocTree onClick", article);
         if (typeof onClick !== "undefined") {
           onClick(article, target);

+ 4 - 4
dashboard-v6/src/components/api/Article.ts → dashboard-v6/backup/components/api/Article.ts

@@ -1,7 +1,7 @@
-import { IStudio } from "../auth/Studio";
-import { IUser } from "../auth/User";
-import { IChannel } from "../channel/Channel";
-import { ITocPathNode } from "../corpus/TocPath";
+import type { IStudio } from "../auth/Studio"
+import type { IUser } from "../auth/User"
+import type { IChannel } from "../channel/Channel"
+import type { ITocPathNode } from "../corpus/TocPath"
 import type { IStudioApiResponse, TRole } from "./Auth";
 
 export interface IArticleListApiResponse {

+ 0 - 0
dashboard-v6/src/components/api/Attachments.ts → dashboard-v6/backup/components/api/Attachments.ts


+ 1 - 1
dashboard-v6/src/components/api/Auth.ts → dashboard-v6/backup/components/api/Auth.ts

@@ -1,4 +1,4 @@
-import { IUser } from "../auth/User";
+import type { IUser } from "../auth/User"
 
 export type TRole =
   | "owner"

+ 2 - 2
dashboard-v6/src/components/api/Channel.ts → dashboard-v6/backup/components/api/Channel.ts

@@ -1,5 +1,5 @@
-import { IStudio } from "../auth/Studio";
-import { TRole } from "./Auth";
+import type { IStudio } from "../auth/Studio"
+import type { TRole } from "./Auth"
 export type TChannelType =
   | "translation"
   | "nissaya"

+ 88 - 0
dashboard-v6/backup/components/api/Comment.ts

@@ -0,0 +1,88 @@
+import type { IUser } from "../auth/User"
+import type { TDiscussionType } from "../discussion/Discussion"
+import type { TContentType } from "../discussion/DiscussionCreate"
+import type { TResType } from "../discussion/DiscussionListCard"
+import type { ITagMapData } from "./Tag"
+
+export interface ICommentRequest {
+  id?: string;
+  res_id?: string;
+  res_type?: string;
+  type?: TDiscussionType;
+  title?: string;
+  content?: string;
+  content_type?: TContentType;
+  parent?: string;
+  topicId?: string;
+  tpl_id?: string;
+  status?: "active" | "close";
+  editor?: IUser;
+  created_at?: string;
+  updated_at?: string;
+}
+
+export interface ICommentApiData {
+  id: string;
+  res_id: string;
+  res_type: TResType;
+  type: TDiscussionType;
+  title?: string;
+  content?: string;
+  content_type?: TContentType;
+  html?: string;
+  summary?: string;
+  parent?: string;
+  tpl_id?: string;
+  status?: "active" | "close";
+  children_count?: number;
+  editor: IUser;
+  created_at?: string;
+  updated_at?: string;
+}
+
+export interface ICommentResponse {
+  ok: boolean;
+  message: string;
+  data: ICommentApiData;
+}
+
+export interface ICommentListResponse {
+  ok: boolean;
+  message: string;
+  data: {
+    rows: ICommentApiData[];
+    count: number;
+    active: number;
+    close: number;
+    can_create: boolean;
+    can_reply: boolean;
+  };
+}
+export interface ICommentAnchorResponse {
+  ok: boolean;
+  message: string;
+  data: string;
+}
+
+export interface IDiscussionCountRequest {
+  course_id?: string | null;
+  sentences: string[][];
+}
+
+export interface IDiscussionCountWbw {
+  book_id: number;
+  paragraph: number;
+  wid: number;
+}
+export interface IDiscussionCountData {
+  id: string;
+  res_id: string;
+  type: string;
+  editor_uid: string;
+  wbw?: IDiscussionCountWbw;
+}
+export interface IDiscussionCountResponse {
+  ok: boolean;
+  message: string;
+  data: { discussions: IDiscussionCountData[]; tags: ITagMapData[] };
+}

+ 7 - 7
dashboard-v6/src/components/api/Corpus.ts → dashboard-v6/backup/components/api/Corpus.ts

@@ -1,10 +1,10 @@
-import { IStudio } from "../auth/Studio";
-import { IUser } from "../auth/User";
-import { IChannel } from "../channel/Channel";
-import { TContentType } from "../discussion/DiscussionCreate";
-import { ISuggestionCount, IWidgetSentEditInner } from "../template/SentEdit";
-import { TChannelType } from "./Channel";
-import { TagNode } from "./Tag";
+import type { IStudio } from "../auth/Studio"
+import type { IUser } from "../auth/User"
+import type { IChannel } from "../channel/Channel"
+import type { TContentType } from "../discussion/DiscussionCreate"
+import type { ISuggestionCount, IWidgetSentEditInner } from "../template/SentEdit"
+import type { TChannelType } from "./Channel"
+import type { TagNode } from "./Tag"
 
 export interface IApiPaliChapterList {
   id: string;

+ 227 - 0
dashboard-v6/backup/components/api/Course.ts

@@ -0,0 +1,227 @@
+import type { IStudio } from "../auth/Studio"
+import type { IUser } from "../auth/User"
+import type { IChannel } from "../channel/Channel"
+import type { _TRole } from "./Auth"
+
+export interface ICourseListApiResponse {
+  article: string;
+  title: string;
+  level: string;
+  children: number;
+}
+
+export interface ICourseDataRequest {
+  id?: string; //课程ID
+  title: string; //标题
+  subtitle?: string; //副标题
+  summary?: string; //副标题
+  content?: string | null;
+  sign_up_message?: string | null;
+  cover?: string; //封面图片文件名
+  teacher_id?: string; //UserID
+  publicity: number; //类型-公开/内部
+  anthology_id?: string; //文集ID
+  channel_id?: string; //标准答案channel
+  start_at?: string; //课程开始时间
+  end_at?: string; //课程结束时间
+  sign_up_start_at: string | null; //报名开始时间
+  sign_up_end_at: string | null; //报名结束时间
+  join: string;
+  request_exp: string;
+  number: number;
+}
+export type TCourseRole =
+  | "owner"
+  | "teacher"
+  | "manager"
+  | "assistant"
+  | "student";
+export type TCourseJoinMode = "invite" | "manual" | "open";
+export type TCourseExpRequest = "none" | "begin-end" | "daily";
+
+export interface IMember {
+  role: TCourseRole;
+  status: TCourseMemberStatus;
+}
+export interface ICourseDataResponse {
+  id: string; //课程ID
+  title: string; //标题
+  subtitle: string; //副标题
+  summary?: string; //副标题
+  sign_up_message?: string | null; //报名弹窗消息
+  teacher?: IUser; //UserID
+  course_count?: number; //课程数
+  publicity: number; //类型-公开/内部
+  anthology_id?: string; //文集ID
+  anthology_title?: string; //文集标题
+  anthology_owner?: IStudio; //文集拥有者
+  channel_id: string; //标准答案ID
+  channel_name?: string; //文集标题
+  channel_owner?: IStudio; //文集拥有者
+  studio?: IStudio; //课程拥有者
+  start_at: string; //课程开始时间
+  end_at: string; //课程结束时间
+  sign_up_start_at: string; //报名开始时间
+  sign_up_end_at: string; //报名结束时间
+  content: string; //简介
+  cover: string; //封面图片文件名
+  cover_url?: string[]; //封面图片文件名
+  member_count: number;
+  join: TCourseJoinMode; //报名方式
+  request_exp: TCourseExpRequest;
+  my_status?: TCourseMemberStatus;
+  my_status_id?: string;
+  count_progressing?: number;
+  number: number;
+  members?: IMember[];
+  my_role?: TCourseRole;
+  created_at: string; //创建时间
+  updated_at: string; //修改时间
+}
+export interface ICourseResponse {
+  ok: boolean;
+  message: string;
+  data: ICourseDataResponse;
+}
+export interface ICourseListResponse {
+  ok: boolean;
+  message: string;
+  data: {
+    rows: ICourseDataResponse[];
+    count: number;
+  };
+}
+
+export interface ICourseCreateRequest {
+  title: string;
+  lang: string;
+  studio: string;
+}
+
+export interface IAnthologyCreateRequest {
+  title: string;
+  lang: string;
+  studio: string;
+}
+export interface ICourseNumberResponse {
+  ok: boolean;
+  message: string;
+  data: {
+    create: number;
+    teach: number;
+    study: number;
+  };
+}
+
+export type TCourseMemberStatus =
+  | "none" /*无*/
+  | "normal" /*开放课程直接加入*/
+  | "joined" /*开放课程已经加入*/
+  | "applied" /**学生已经报名 管理员尚未审核 */
+  | "canceled" /**学生取消报名 */
+  | "agreed" /**学生/助教已经接受邀请 */
+  | "disagreed" /**学生/助教已经拒绝邀请 */
+  | "left" /**学生自己退出 */
+  | "invited" /**管理员已经邀请学生加入 */
+  | "revoked" /**管理员撤销邀请 */
+  | "accepted" /**已经被管理员录取 */
+  | "rejected" /**报名已经被管理员拒绝 */
+  | "blocked"; /**被管理员清退 */
+
+export type TCourseMemberAction =
+  | "join" /*加入自学课程*/
+  | "apply" /**学生报名 */
+  | "cancel" /**学生取消报名 */
+  | "agree" /**学生/助教接受邀请 */
+  | "disagree" /**学生/助教拒绝邀请 */
+  | "leave" /**学生/助教自己退出 */
+  | "invite" /**管理员邀请学生加入 */
+  | "revoke" /**管理员撤销邀请 */
+  | "accept" /**管理员录取 */
+  | "reject" /**管理员拒绝 */
+  | "block"; /**管理员清退 */
+
+interface IActionMap {
+  action: TCourseMemberAction;
+  status: TCourseMemberStatus;
+}
+export const actionMap = (action: TCourseMemberAction) => {
+  const data: IActionMap[] = [
+    { action: "join", status: "joined" },
+    { action: "apply", status: "applied" },
+    { action: "cancel", status: "canceled" },
+    { action: "agree", status: "agreed" },
+    { action: "disagree", status: "disagreed" },
+    { action: "leave", status: "left" },
+    { action: "invite", status: "invited" },
+    { action: "revoke", status: "revoked" },
+    { action: "accept", status: "accepted" },
+    { action: "reject", status: "rejected" },
+    { action: "block", status: "blocked" },
+  ];
+
+  const current = data.find((value) => value.action === action);
+  return current?.status;
+};
+
+export interface ICourseMemberData {
+  id?: string;
+  user_id: string;
+  course_id: string;
+  course?: ICourseDataResponse;
+  channel_id?: string;
+  channel?: IChannel;
+  role?: TCourseRole;
+  operating?: "invite" | "sign_up";
+  user?: IUser;
+  editor?: IUser;
+  status?: TCourseMemberStatus;
+  created_at?: string;
+  updated_at?: string;
+}
+export interface ICourseMemberResponse {
+  ok: boolean;
+  message: string;
+  data: ICourseMemberData;
+}
+export interface ICourseMemberListResponse {
+  ok: boolean;
+  message: string;
+  data: {
+    rows: ICourseMemberData[];
+    role: TCourseRole;
+    count: number;
+  };
+}
+
+export interface ICourseMemberDeleteResponse {
+  ok: boolean;
+  message: string;
+  data: boolean;
+}
+
+export interface ICourseUser {
+  role: TCourseRole;
+  channel_id?: string | null;
+}
+export interface ICourseCurrUserResponse {
+  ok: boolean;
+  message: string;
+  data: ICourseUser;
+}
+
+export interface IExerciseListData {
+  user: IUser;
+  wbw: number;
+  translation: number;
+  question: number;
+  html: string;
+}
+export interface ICourseExerciseResponse {
+  ok: boolean;
+  message: string;
+  data: {
+    rows: IExerciseListData[];
+    count: number;
+  };
+}

+ 3 - 3
dashboard-v6/src/components/api/Dict.ts → dashboard-v6/backup/components/api/Dict.ts

@@ -1,6 +1,6 @@
-import { IStudio } from "../auth/Studio";
-import { IUser } from "../auth/User";
-import { ICaseListData } from "../dict/CaseList";
+import type { IStudio } from "../auth/Studio"
+import type { IUser } from "../auth/User"
+import type { ICaseListData } from "../dict/CaseList"
 
 export interface IDictRequest {
   id?: number;

+ 0 - 0
dashboard-v6/src/components/api/Exp.ts → dashboard-v6/backup/components/api/Exp.ts


+ 80 - 0
dashboard-v6/backup/components/api/Group.ts

@@ -0,0 +1,80 @@
+import type { IStudio } from "../auth/Studio"
+import type { IUser } from "../auth/User"
+import type { TRole } from "./Auth"
+
+export interface IGroupRequest {
+  id?: string;
+  name: string;
+  description?: string;
+  studio_name?: string;
+}
+
+export interface IGroupDataRequest {
+  uid: string;
+  name: string;
+  description: string;
+  owner: string;
+  studio: IStudio;
+  role: TRole;
+  created_at: string;
+  updated_at: string;
+}
+
+export interface IGroupResponse {
+  ok: boolean;
+  message: string;
+  data: IGroupDataRequest;
+}
+export interface IGroupListResponse {
+  ok: boolean;
+  message: string;
+  data: {
+    rows: IGroupDataRequest[];
+    count: number;
+  };
+}
+export interface IGroupMemberRequest {
+  id?: number;
+  user_id: string;
+  group_id: string;
+  power?: number;
+  level?: number;
+  status?: number;
+}
+export interface IGroupMemberData {
+  id?: number;
+  user_id: string;
+  group_id: string;
+  group: IStudio;
+  power?: number;
+  level?: number;
+  status?: number;
+  user: IUser;
+  created_at?: string;
+  updated_at?: string;
+}
+export interface IGroupMemberResponse {
+  ok: boolean;
+  message: string;
+  data: IGroupMemberData;
+}
+export interface IGroupMemberListResponse {
+  ok: boolean;
+  message: string;
+  data: {
+    rows: IGroupMemberData[];
+    role: TRole;
+    count: number;
+  };
+}
+
+export interface IGroupMemberDeleteResponse {
+  ok: boolean;
+  message: string;
+  data: boolean;
+}
+export interface IDeleteResponse {
+  ok: boolean;
+  message: string;
+  data: number;
+}

+ 0 - 0
dashboard-v6/src/components/api/Guide.ts → dashboard-v6/backup/components/api/Guide.ts


+ 46 - 0
dashboard-v6/backup/components/api/Share.ts

@@ -0,0 +1,46 @@
+import type { IUser } from "../auth/User"
+import type { IGroup } from "../group/Group"
+import type { TRole } from "./Auth"
+
+export interface IShareRequest {
+  res_id: string;
+  res_type: number;
+  role: TRole;
+  user_id: string[];
+  user_type: string;
+}
+export interface IShareUpdateRequest {
+  role: TRole;
+}
+export interface IShareData {
+  id?: string;
+  res_id: string;
+  res_type: string;
+  power?: number;
+  res_name: string;
+  user?: IUser;
+  group?: IGroup;
+  owner?: IUser;
+  role?: TRole;
+  created_at?: string;
+  updated_at?: string;
+}
+export interface IShareResponse {
+  ok: boolean;
+  message: string;
+  data: IShareData;
+}
+export interface IShareListResponse {
+  ok: boolean;
+  message: string;
+  data: {
+    rows: IShareData[];
+    role: TRole;
+    count: number;
+  };
+}
+export interface IShareDeleteResponse {
+  ok: boolean;
+  message: string;
+  data: number;
+}

+ 2 - 2
dashboard-v6/src/components/api/Suggestion.ts → dashboard-v6/backup/components/api/Suggestion.ts

@@ -1,5 +1,5 @@
-import { IUser } from "../auth/User";
-import { IChannelApiData } from "./Channel";
+import type { IUser } from "../auth/User"
+import type { IChannelApiData } from "./Channel"
 
 export interface ISuggestionData {
   id: string;

+ 84 - 0
dashboard-v6/backup/components/api/Tag.ts

@@ -0,0 +1,84 @@
+import type { IStudio } from "../auth/Studio"
+import type { IUser } from "../auth/User"
+
+export interface TagNode {
+  id: string;
+  name: string;
+  description?: string;
+}
+
+export interface ITagRequest {
+  id?: string;
+  name?: string;
+  description?: string | null;
+  color?: number;
+  studio?: string;
+  created_at?: string;
+  updated_at?: string;
+}
+export interface ITag {
+  id?: string;
+  name?: string;
+  description?: string | null;
+  color?: number;
+  owner?: IStudio;
+  created_at?: string;
+  updated_at?: string;
+}
+export interface ITagData {
+  id: string;
+  name: string;
+  description?: string | null;
+  color: number;
+  owner?: IStudio;
+  created_at: string;
+  updated_at: string;
+}
+
+export interface ITagResponse {
+  ok: boolean;
+  message: string;
+  data: ITagData;
+}
+
+export interface ITagResponseList {
+  ok: boolean;
+  message: string;
+  data: { rows: ITagData[]; count: number };
+}
+
+export interface ITagMapRequest {
+  id?: string;
+  table_name?: string;
+  anchor_id?: string;
+  tag_id?: string;
+  studio?: string;
+  course?: string;
+}
+
+export interface ITagMapData {
+  id: string;
+  table_name: string;
+  anchor_id: string;
+  tag_id: string;
+  name?: string | null;
+  color?: number | null;
+  description?: string | null;
+  title?: string;
+  editor?: IUser;
+  owner?: IStudio;
+  created_at?: string;
+  updated_at?: string;
+}
+
+export interface ITagMapResponse {
+  ok: boolean;
+  message: string;
+  data: ITagMapData;
+}
+
+export interface ITagMapResponseList {
+  ok: boolean;
+  message: string;
+  data: { rows: ITagMapData[]; count: number };
+}

+ 4 - 4
dashboard-v6/src/components/api/Term.ts → dashboard-v6/backup/components/api/Term.ts

@@ -1,7 +1,7 @@
-import { IStudio } from "../auth/Studio";
-import { IUser } from "../auth/User";
-import { IChannel } from "../channel/Channel";
-import { TRole } from "./Auth";
+import type { IStudio } from "../auth/Studio"
+import type { IUser } from "../auth/User"
+import type { IChannel } from "../channel/Channel"
+import type { TRole } from "./Auth"
 
 export interface ITermDataRequest {
   id?: string;

+ 45 - 0
dashboard-v6/backup/components/api/Transfer.ts

@@ -0,0 +1,45 @@
+import type { IStudio } from "../auth/Studio"
+import type { IUser } from "../auth/User"
+import type { IChannel } from "../channel/Channel"
+import type { TResType } from "../discussion/DiscussionListCard"
+
+export type ITransferStatus = "transferred" | "accept" | "refuse" | "cancel";
+export interface ITransferRequest {
+  res_type?: TResType;
+  res_id?: string[];
+  new_owner?: string;
+  status?: ITransferStatus;
+}
+export interface ITransferResponseData {
+  id: string;
+  origin_owner: IStudio;
+  res_type: TResType;
+  res_id: string;
+  channel?: IChannel;
+  transferor: IUser;
+  new_owner: IStudio;
+  status: ITransferStatus;
+  editor?: IUser | null;
+  created_at: string;
+  updated_at: string;
+}
+export interface ITransferCreateResponse {
+  ok: boolean;
+  message: string;
+  data: number;
+}
+export interface ITransferResponse {
+  ok: boolean;
+  message: string;
+  data: ITransferResponseData;
+}
+export interface ITransferResponseList {
+  ok: boolean;
+  message: string;
+  data: {
+    rows: ITransferResponseData[];
+    count: number;
+    out: number;
+    in: number;
+  };
+}

+ 0 - 0
dashboard-v6/src/components/api/ai.ts → dashboard-v6/backup/components/api/ai.ts


+ 56 - 0
dashboard-v6/backup/components/api/like.ts

@@ -0,0 +1,56 @@
+import type { IUser } from "../auth/User"
+
+export type TLikeType = "like" | "dislike" | "favorite" | "watch";
+export interface ILikeData {
+  id: string;
+  type: TLikeType;
+  target_id: string;
+  target_type?: string;
+  user: IUser;
+  context?: string | null;
+  selected?: boolean;
+  my_id?: string;
+  count?: number;
+  updated_at?: string;
+  created_at?: string;
+}
+export interface ILikeCount {
+  type: TLikeType;
+  selected?: boolean;
+  my_id?: string;
+  count?: number;
+  user: IUser;
+}
+
+export interface ILikeListResponse {
+  ok: boolean;
+  message: string;
+  data: {
+    rows: ILikeData[];
+    count: number;
+  };
+}
+
+export interface ILikeResponse {
+  ok: boolean;
+  message: string;
+  data: ILikeData;
+}
+
+export interface ILikeCountListResponse {
+  ok: boolean;
+  message: string;
+  data: ILikeCount[];
+}
+export interface ILikeCountResponse {
+  ok: boolean;
+  message: string;
+  data: ILikeCount;
+}
+
+export interface ILikeRequest {
+  type: TLikeType;
+  target_id: string;
+  target_type: string;
+  user_id?: string;
+}

+ 43 - 0
dashboard-v6/backup/components/api/notification.ts

@@ -0,0 +1,43 @@
+import type { IUser } from "../auth/User"
+import type { IChannel } from "../channel/Channel"
+
+export interface INotificationPutResponse {
+  ok: boolean;
+  data: {
+    unread: number;
+  };
+  message: string;
+}
+
+export interface INotificationListResponse {
+  ok: boolean;
+  data: INotificationListData;
+  message: string;
+}
+
+export interface INotificationListData {
+  rows: INotificationData[];
+  count: number;
+  unread: number;
+}
+
+interface INotificationData {
+  id: string;
+  from: IUser;
+  to: IUser;
+  channel: IChannel;
+  url?: string;
+  title?: string;
+  book_title?: string;
+  content: string;
+  content_type: string;
+  res_type: string;
+  res_id: string;
+  status: string;
+  deleted_at?: string;
+  created_at: string;
+  updated_at: string;
+}
+export interface INotificationRequest {
+  status: string;
+}

+ 260 - 0
dashboard-v6/backup/components/api/task.ts

@@ -0,0 +1,260 @@
+/**
+ *             $table->text('description',512)->nullable();
+            $table->jsonb('assignees')->index()->nullable();
+            $table->jsonb('roles')->index()->nullable();
+            $table->uuid('executor')->index()->nullable();
+            $table->uuid('executor_relation_task')->index()->nullable();
+            $table->uuid('parent')->index()->nullable();
+            $table->jsonb('pre_task')->index()->nullable();
+            $table->uuid('owner')->index();
+            $table->uuid('editor')->index();
+            $table->string('status',32)->index()->default('pending');
+            $table->timestamps();
+ */
+
+import type { IStudio } from "../auth/Studio"
+import type { IUser } from "../auth/User"
+import type { TPrivacy } from "./ai"
+
+export type TTaskStatus =
+  | "pending"
+  | "published"
+  | "running"
+  | "done"
+  | "restarted"
+  | "requested_restart"
+  | "closed"
+  | "canceled"
+  | "expired"
+  | "queue"
+  | "stop"
+  | "quit"
+  | "pause";
+export const StatusButtons: TTaskStatus[] = [
+  "pending",
+  "published",
+  "running",
+  "done",
+  "restarted",
+  "requested_restart",
+  "quit",
+];
+export type TTaskType = "instance" | "workflow" | "group";
+
+export interface IProject {
+  id: string;
+  sn: number;
+  title: string;
+  description: string | null;
+  weight: number;
+}
+
+export type TTaskCategory =
+  | "translate"
+  | "suggest"
+  | "vocabulary"
+  | "team"
+  | "review"
+  | "proofread";
+export const ATaskCategory: TTaskCategory[] = [
+  "translate",
+  "suggest",
+  "vocabulary",
+  "team",
+  "review",
+  "proofread",
+];
+export interface ITaskData {
+  id: string;
+  title: string;
+  description?: string | null;
+  category?: TTaskCategory | null;
+  progress?: number;
+  html?: string | null;
+  type: TTaskType;
+  order?: number;
+  assignees?: IUser[] | null;
+  assignees_id?: string[] | null;
+  parent?: ITaskData | null;
+  parent_id?: string | null;
+  roles?: string[] | null;
+  executor?: IUser | null;
+  executor_id?: string | null;
+  executor_relation_task?: ITaskData | null;
+  executor_relation_task_id?: string | null;
+  pre_task?: ITaskData[] | null;
+  pre_task_id?: string | null;
+  next_task?: ITaskData[] | null;
+  next_task_id?: string | null;
+  is_milestone: boolean;
+  project?: IProject | null;
+  project_id?: string | null;
+  owner?: IStudio;
+  owner_id?: string | null;
+  editor?: IUser;
+  editor_id?: string | null;
+  status?: TTaskStatus;
+  created_at?: string;
+  updated_at?: string;
+  started_at?: string | null;
+  finished_at?: string | null;
+  children?: ITaskData[];
+}
+
+export interface ITaskUpdateRequest {
+  id: string;
+  studio_name: string;
+  title?: string;
+  description?: string | null;
+  category?: TTaskCategory | null;
+  type?: TTaskType;
+  assignees_id?: string[] | null;
+  parent_id?: string | null;
+  project_id?: string | null;
+  roles?: string[] | null;
+  executor_id?: string | null;
+  executor_relation_task_id?: string | null;
+  pre_task_id?: string | null;
+  next_task_id?: string | null;
+  is_milestone?: boolean;
+  status?: string;
+}
+
+export interface ITaskListResponse {
+  ok: boolean;
+  message: string;
+  data: {
+    rows: ITaskData[];
+    count: number;
+  };
+}
+
+export interface ITaskCreateRequest {
+  title: string;
+  studio: string;
+  type: TTaskType;
+}
+
+export interface ITaskResponse {
+  ok: boolean;
+  message: string;
+  data: ITaskData;
+}
+
+/**
+ *            $table->uuid('id')->primary()->default(DB::raw('uuid_generate_v1mc()'));
+            $table->string('title',512)->index();
+            $table->boolean('is_template')->index()->default(false);
+            $table->text('description')->nullable();
+            $table->jsonb('executors')->index()->nullable();
+            $table->uuid('parent')->index()->nullable();
+            $table->jsonb('milestone')->index()->nullable();
+            $table->uuid('owner')->index();
+            $table->uuid('editor')->index();
+            $table->jsonb('status')->index();
+            $table->timestamps();
+ */
+
+export interface IProjectData {
+  id: string;
+  title: string;
+  type: TProjectType;
+  weight: number;
+  description: string | null;
+  parent?: IProjectData | null;
+  parent_id?: string | null;
+  path?: IProjectData[] | null;
+  executors?: IUser[] | null;
+  milestone?: IMilestoneInProject[] | null;
+  owner: IStudio;
+  editor: IUser;
+  status: ITaskStatusInProject[];
+  privacy: TPrivacy;
+  created_at: string;
+  updated_at: string;
+  deleted_at?: string | null;
+  started_at?: string | null;
+  finished_at?: string | null;
+  children?: IProjectData[];
+}
+
+export interface IProjectUpdateRequest {
+  id?: string;
+  studio_name?: string;
+  title: string;
+  type: TProjectType;
+  privacy?: TPrivacy;
+  weight?: number;
+  description?: string | null;
+  parent_id?: string | null;
+  res_id?: string;
+}
+
+export interface IProjectListResponse {
+  data: { rows: IProjectData[]; count: number };
+  message: string;
+  ok: boolean;
+}
+export interface IProjectResponse {
+  data: IProjectData;
+  message: string;
+  ok: boolean;
+}
+export type TProjectType = "instance" | "workflow" | "endpoint";
+export interface IProjectCreateRequest {
+  title: string;
+  type: TProjectType;
+  studio_name: string;
+}
+
+export interface IMilestoneData {
+  id: string;
+  title: string;
+}
+
+export interface IMilestoneCount {
+  value: number;
+  total: number;
+}
+export interface IMilestoneInProject {
+  milestone: IMilestoneData;
+  projects: IMilestoneCount;
+  chars: IMilestoneCount;
+}
+
+export interface ITaskStatusInProject {
+  status: string;
+  count: number;
+  percent: number;
+}
+
+export interface ITaskGroupInsertRequest {
+  data: ITaskGroupInsertData[];
+}
+export interface ITaskGroupInsertData {
+  project_id: string;
+  tasks: ITaskData[];
+}
+
+export interface ITaskGroupResponse {
+  ok: boolean;
+  message: string;
+  data: { taskCount: number; taskRelationCount: number };
+}
+export interface IProjectTreeInsertRequest {
+  studio_name: string;
+  parent_id?: string | null;
+  title?: string;
+  data: IProjectUpdateRequest[];
+}
+
+export interface IProjectTreeData {
+  id: string;
+  resId?: string;
+  isLeaf: boolean;
+}
+export interface IProjectTreeResponse {
+  ok: boolean;
+  message: string;
+  data: { rows: IProjectTreeData[]; count: number };
+}

+ 0 - 0
dashboard-v6/src/components/api/token.ts → dashboard-v6/backup/components/api/token.ts


+ 51 - 0
dashboard-v6/backup/components/api/view.ts

@@ -0,0 +1,51 @@
+import type { ArticleType } from "../article/Article"
+
+export interface IViewRequest {
+  target_type: ArticleType;
+  book: number;
+  para: number;
+  channel: string;
+  mode: string;
+}
+export interface IMetaChapter {
+  book: number;
+  para: number;
+  channel: string;
+  mode: string;
+}
+export interface IViewData {
+  id: string;
+  target_id: string;
+  target_type: ArticleType;
+  updated_at: string;
+  title: string;
+  org_title: string;
+  meta: string;
+}
+export interface IViewStoreResponse {
+  ok: boolean;
+  message: string;
+  data: number;
+}
+export interface IViewResponse {
+  ok: boolean;
+  message: string;
+  data: IViewData;
+}
+export interface IViewListResponse {
+  ok: boolean;
+  message: string;
+  data: {
+    rows: IViewData[];
+    count: number;
+  };
+}
+
+export interface IView {
+  id: string;
+  title: string;
+  subtitle: string;
+  type: ArticleType;
+  updatedAt: string;
+  meta: IMetaChapter;
+}

+ 3 - 3
dashboard-v6/src/components/api/webhook.ts → dashboard-v6/backup/components/api/webhook.ts

@@ -1,6 +1,6 @@
-import { IUser } from "../auth/User";
-import { TResType } from "../discussion/DiscussionListCard";
-import { IWebhookEvent } from "../webhook/WebhookTpl";
+import type { IUser } from "../auth/User"
+import type { TResType } from "../discussion/DiscussionListCard"
+import type { IWebhookEvent } from "../webhook/WebhookTpl"
 
 export type TReceiverType = "wechat" | "dingtalk";
 

+ 82 - 0
dashboard-v6/backup/components/article/AddToAnthology.tsx

@@ -0,0 +1,82 @@
+import { message } from "antd";
+import React, { useState } from "react";
+import { post } from "../../request";
+import AnthologyModal from "../anthology/AnthologyModal";
+import type {
+  IArticleMapAddRequest,
+  IArticleMapAddResponse,
+} from "../../api/Article";
+import { useAppSelector } from "../../hooks";
+import { currentUser } from "../../reducers/current-user";
+
+interface IWidget {
+  trigger?: React.ReactNode;
+  studioName?: string;
+  articleIds?: string[];
+  open?: boolean; // 外部控制
+  onClose?: (open: boolean) => void;
+  onFinally?: () => void;
+}
+
+const AddToAnthologyWidget = ({
+  trigger,
+  studioName,
+  open,
+  articleIds,
+  onClose,
+  onFinally,
+}: IWidget) => {
+  const user = useAppSelector(currentUser);
+
+  /** 是否受控 */
+  const isControlled = open !== undefined;
+
+  /** 内部状态(仅非受控使用) */
+  const [innerOpen, setInnerOpen] = useState(false);
+
+  /** 最终状态来源 */
+  const isOpen = isControlled ? open : innerOpen;
+
+  /** 状态修改统一入口 */
+  const setOpen = (next: boolean) => {
+    if (!isControlled) {
+      setInnerOpen(next);
+    }
+    onClose?.(next);
+  };
+
+  /** 选择文集 */
+  const handleSelect = (id: string) => {
+    if (!articleIds) return;
+
+    post<IArticleMapAddRequest, IArticleMapAddResponse>("/v2/article-map", {
+      anthology_id: id,
+      article_id: articleIds,
+      operation: "add",
+    })
+      .then((json) => {
+        if (json.ok) {
+          message.success(json.data);
+          setOpen(false); // 成功后关闭
+        } else {
+          message.error(json.message);
+        }
+      })
+      .catch(console.error)
+      .finally(() => {
+        onFinally?.();
+      });
+  };
+
+  return (
+    <AnthologyModal
+      studioName={studioName ?? user?.realName}
+      trigger={trigger && <span onClick={() => setOpen(true)}>{trigger}</span>}
+      open={isOpen}
+      onClose={setOpen}
+      onSelect={handleSelect}
+    />
+  );
+};
+
+export default AddToAnthologyWidget;

+ 91 - 0
dashboard-v6/backup/components/article/AnchorNav.tsx

@@ -0,0 +1,91 @@
+import { Anchor } from "antd";
+import { useEffect, useState, useRef } from "react";
+import { convertToPlain } from "../../utils";
+
+const { Link } = Anchor;
+
+interface HeadingNode {
+  key: string;
+  label: string;
+  level: number;
+  children?: HeadingNode[];
+}
+
+interface Props {
+  open?: boolean;
+  containerSelector?: string; // 可指定扫描范围
+}
+
+/** 构建树结构 */
+function buildTree(list: HeadingNode[]): HeadingNode[] {
+  const root: HeadingNode = { key: "root", label: "", level: 0, children: [] };
+  const stack = [root];
+
+  for (const node of list) {
+    while (stack.length && stack[stack.length - 1].level >= node.level) {
+      stack.pop();
+    }
+
+    const parent = stack[stack.length - 1];
+    parent.children ??= [];
+    parent.children.push(node);
+
+    stack.push(node);
+  }
+
+  return root.children ?? [];
+}
+
+/** 递归渲染 */
+function renderLinks(nodes: HeadingNode[]): React.ReactNode {
+  return nodes.map((node) => (
+    <Link key={node.key} href={node.key} title={node.label}>
+      {node.children && renderLinks(node.children)}
+    </Link>
+  ));
+}
+
+const AnchorNavWidget = ({ open = false, containerSelector }: Props) => {
+  const [tree, setTree] = useState<HeadingNode[]>([]);
+  const containerRef = useRef<HTMLElement | null>(null);
+
+  /** 获取容器 */
+  useEffect(() => {
+    containerRef.current = containerSelector
+      ? document.querySelector(containerSelector)
+      : document.body;
+  }, [containerSelector]);
+
+  /** 扫描 heading */
+  useEffect(() => {
+    if (!open || !containerRef.current) return;
+
+    const headings = Array.from(
+      containerRef.current.querySelectorAll("h1,h2,h3,h4,h5,h6")
+    );
+
+    const list: HeadingNode[] = headings
+      .map((el) => {
+        if (!el.id) return null;
+
+        return {
+          key: `#${el.id}`,
+          label: convertToPlain(el.innerHTML).slice(0, 30),
+          level: Number(el.tagName[1]),
+        };
+      })
+      .filter(Boolean) as HeadingNode[];
+
+    setTree(buildTree(list));
+  }, [open]);
+
+  if (!open || tree.length === 0) return null;
+
+  return (
+    <div className="article_anchor paper_zh">
+      <Anchor offsetTop={50}>{renderLinks(tree)}</Anchor>
+    </div>
+  );
+};
+
+export default AnchorNavWidget;

+ 1 - 1
dashboard-v6/src/components/article/AnthologiesAtArticle.tsx → dashboard-v6/backup/components/article/AnthologiesAtArticle.tsx

@@ -1,7 +1,7 @@
 import { Space, Typography, message } from "antd";
 import { useEffect, useState } from "react";
 import { get } from "../../request";
-import { IArticleMapListResponse } from "../api/Article";
+import type { IArticleMapListResponse } from "../../api/Article";
 
 const { Link, Paragraph } = Typography;
 interface IList {

+ 1 - 1
dashboard-v6/src/components/article/AnthologyCard.tsx → dashboard-v6/backup/components/article/AnthologyCard.tsx

@@ -1,4 +1,4 @@
-import { Link } from "react-router-dom";
+import { Link } from "react-router";
 import { Row, Col } from "antd";
 import { Card } from "antd";
 import { Typography } from "antd";

+ 126 - 0
dashboard-v6/backup/components/article/AnthologyDetail.tsx

@@ -0,0 +1,126 @@
+import { useState, useEffect } from "react";
+import { Space, Typography, message } from "antd";
+import { get } from "../../request";
+import type {
+  IAnthologyDataResponse,
+  IAnthologyResponse,
+} from "../../api/Article";
+import type { IAnthologyData } from "./AnthologyCard";
+import StudioName from "../auth/Studio";
+import TimeShow from "../general/TimeShow";
+import Marked from "../general/Marked";
+import AnthologyTocTree from "../anthology/AnthologyTocTree";
+import { useIntl } from "react-intl";
+
+const { Title, Text, Paragraph } = Typography;
+
+interface Props {
+  aid?: string;
+  channels?: string[];
+  visible?: boolean;
+  onArticleClick?: (anthologyId: string, id: string, target: string) => void;
+  onTitle?: (title: string) => void;
+  onLoading?: (loading: boolean) => void;
+  onError?: (error: unknown, message?: string) => void;
+}
+
+const AnthologyDetailWidget = ({
+  aid,
+  channels,
+  visible = true,
+  onArticleClick,
+  onLoading,
+  onTitle,
+  onError,
+}: Props) => {
+  const [data, setData] = useState<IAnthologyData>();
+  const intl = useIntl();
+
+  useEffect(() => {
+    if (!aid) return;
+
+    let active = true;
+
+    const fetchData = async () => {
+      try {
+        onLoading?.(true);
+
+        const res = await get<IAnthologyResponse>(`/v2/anthology/${aid}`);
+
+        if (!active) return;
+
+        if (!res.ok) {
+          message.error(res.message);
+          onError?.(res.data, res.message);
+          return;
+        }
+
+        const item: IAnthologyDataResponse = res.data;
+
+        const parsed: IAnthologyData = {
+          id: item.uid,
+          title: item.title,
+          subTitle: item.subtitle,
+          summary: item.summary,
+          articles: [],
+          studio: item.studio,
+          created_at: item.created_at,
+          updated_at: item.updated_at,
+        };
+
+        setData(parsed);
+        onTitle?.(item.title);
+      } catch (err) {
+        if (active) {
+          console.error(err);
+          onError?.(err);
+        }
+      } finally {
+        if (active && onLoading) {
+          onLoading(false);
+        }
+      }
+    };
+
+    fetchData();
+
+    return () => {
+      active = false;
+    };
+  }, [aid, onError, onLoading, onTitle]);
+
+  if (!visible || !data) return null;
+
+  return (
+    <div style={{ padding: 12 }}>
+      <Title level={4}>{data.title}</Title>
+
+      <Text type="secondary">{data.subTitle}</Text>
+
+      <Paragraph>
+        <Space>
+          <StudioName data={data.studio} />
+          <TimeShow updatedAt={data.updated_at} />
+        </Space>
+      </Paragraph>
+
+      <Paragraph>
+        <Marked text={data.summary} />
+      </Paragraph>
+
+      <Title level={5}>
+        {intl.formatMessage({ id: "labels.table-of-content" })}
+      </Title>
+
+      <AnthologyTocTree
+        anthologyId={aid}
+        channels={channels}
+        onClick={(anthologyId, id, target) =>
+          onArticleClick?.(anthologyId, id, target)
+        }
+      />
+    </div>
+  );
+};
+
+export default AnthologyDetailWidget;

+ 5 - 5
dashboard-v6/src/components/article/AnthologyInfoEdit.tsx → dashboard-v6/backup/components/article/AnthologyInfoEdit.tsx

@@ -4,21 +4,21 @@ import {
   ProForm,
   ProFormSelect,
   ProFormText,
-  RequestOptionsType,
+  type RequestOptionsType,
 } from "@ant-design/pro-components";
 import MDEditor from "@uiw/react-md-editor";
 
 import { get, put } from "../../request";
-import {
+import type {
   IAnthologyDataRequest,
   IAnthologyDataResponse,
   IAnthologyResponse,
-} from "../api/Article";
+} from "../../api/Article";
 import LangSelect from "../general/LangSelect";
 import PublicitySelect from "../studio/PublicitySelect";
 import { useState } from "react";
-import { DefaultOptionType } from "antd/lib/select";
-import { IApiResponseChannelList } from "../api/Channel";
+import type { DefaultOptionType } from "antd/lib/select";
+import type { IApiResponseChannelList } from "../../api/Channel";
 import { useAppSelector } from "../../hooks";
 import { currentUser } from "../../reducers/current-user";
 

+ 1 - 1
dashboard-v6/src/components/article/AnthologyList.tsx → dashboard-v6/backup/components/article/AnthologyList.tsx

@@ -2,7 +2,7 @@ import { useState, useEffect } from "react";
 import { List } from "antd";
 
 import { get } from "../../request";
-import type { IAnthologyListResponse } from "../api/Article";
+import type { IAnthologyListResponse } from "../../api/Article";
 import AnthologyCard from "./AnthologyCard";
 import type { IAnthologyData } from "./AnthologyCard";
 

+ 3 - 3
dashboard-v6/src/components/article/AnthologyStudioList.tsx → dashboard-v6/backup/components/article/AnthologyStudioList.tsx

@@ -1,10 +1,10 @@
-import { Link } from "react-router-dom";
+import { Link } from "react-router";
 import { useState, useEffect } from "react";
 import { List, Space, Card } from "antd";
 
 import StudioName from "../auth/Studio";
-import type { IAnthologyStudioListApiResponse } from "../api/Article";
-import type { IStudioApiResponse } from "../api/Auth";
+import type { IAnthologyStudioListApiResponse } from "../../api/Article";
+import type { IStudioApiResponse } from "../../api/Auth";
 import { get } from "../../request";
 
 interface IAnthologyStudioData {

+ 2 - 35
dashboard-v6/src/components/article/Article.tsx → dashboard-v6/backup/components/article/Article.tsx

@@ -1,4 +1,4 @@
-import { IArticleDataResponse } from "../api/Article";
+import type { IArticleDataResponse } from "../../api/Article";
 import TypeArticle from "./TypeArticle";
 import TypeAnthology from "./TypeAnthology";
 import TypeTerm from "./TypeTerm";
@@ -6,7 +6,7 @@ import TypePali from "./TypePali";
 import "./article.css";
 import TypePage from "./TypePage";
 import TypeCSPara from "./TypeCSPara";
-import { ISearchParams } from "../../pages/library/article/show";
+import type { ISearchParams } from "../../pages/library/article/show";
 import TypeCourse from "./TypeCourse";
 import { useEffect, useState } from "react";
 import { fullUrl } from "../../utils";
@@ -14,39 +14,6 @@ import TypeSeries from "./TypeSeries";
 import DiscussionCount from "../discussion/DiscussionCount";
 import TypeTask from "./TypeTask";
 
-export type ArticleMode = "read" | "edit" | "wbw" | "auto";
-export type ArticleType =
-  | "anthology"
-  | "article"
-  | "series"
-  | "chapter"
-  | "para"
-  | "cs-para"
-  | "sent"
-  | "sim"
-  | "page"
-  | "textbook"
-  | "exercise"
-  | "exercise-list"
-  | "sent-original"
-  | "sent-commentary"
-  | "sent-nissaya"
-  | "sent-translation"
-  | "term"
-  | "task";
-/**
- * 每种article type 对应的路由参数
- * article/id?anthology=id&channel=id1,id2&mode=ArticleMode
- * chapter/book-para?channel=id1,id2&mode=ArticleMode
- * para/book?par=para1,para2&channel=id1,id2&mode=ArticleMode
- * cs-para/book-para?channel=id1,id2&mode=ArticleMode
- * sent/id?channel=id1,id2&mode=ArticleMode
- * sim/id?channel=id1,id2&mode=ArticleMode
- * textbook/articleId?course=id&mode=ArticleMode
- * exercise/articleId?course=id&exercise=id&username=name&mode=ArticleMode
- * exercise-list/articleId?course=id&exercise=id&mode=ArticleMode
- * sent-original/id
- */
 interface IWidget {
   type?: ArticleType;
   articleId?: string;

+ 2 - 2
dashboard-v6/src/components/article/ArticleCard.tsx → dashboard-v6/backup/components/article/ArticleCard.tsx

@@ -1,9 +1,9 @@
-import { useNavigate } from "react-router-dom";
+import { useNavigate } from "react-router";
 import { Button, Card, Dropdown, Space } from "antd";
 import { MoreOutlined, ReloadOutlined } from "@ant-design/icons";
 import type { MenuProps } from "antd";
 
-import { IWidgetArticleData } from "./ArticleView";
+import type { IWidgetArticleData } from "./ArticleView"
 import ArticleCardMainMenu from "./ArticleCardMainMenu";
 import ModeSwitch from "./ModeSwitch";
 

+ 2 - 2
dashboard-v6/src/components/article/ArticleCardMainMenu.tsx → dashboard-v6/backup/components/article/ArticleCardMainMenu.tsx

@@ -10,7 +10,7 @@ interface IWidget {
   type?: string;
   articleId?: string;
 }
-const ArticleCardMainMenuWidget = ({ type, articleId }: IWidget) => {
+const ArticleCardMainMenuWidget = ({ articleId }: IWidget) => {
   const intl = useIntl();
   const id = articleId?.split("_");
   let tocWidget = <></>;
@@ -66,7 +66,7 @@ const ArticleCardMainMenuWidget = ({ type, articleId }: IWidget) => {
   return (
     <Popover
       placement="bottomLeft"
-      arrowPointAtCenter
+      arrow={{ pointAtCenter: true }}
       content={mainMenuContent}
       trigger="click"
     >

+ 7 - 8
dashboard-v6/src/components/article/ArticleCreate.tsx → dashboard-v6/backup/components/article/ArticleCreate.tsx

@@ -1,19 +1,19 @@
 import { useIntl } from "react-intl";
 import {
   ProForm,
-  ProFormInstance,
+  type ProFormInstance,
   ProFormSelect,
   ProFormText,
 } from "@ant-design/pro-components";
 import { Alert, Space, message } from "antd";
 
 import { get, post } from "../../request";
-import {
+import type {
   IAnthologyListResponse,
   IArticleCreateRequest,
   IArticleDataResponse,
   IArticleResponse,
-} from "../api/Article";
+} from "../../api/Article";
 import LangSelect from "../general/LangSelect";
 import { useEffect, useRef, useState } from "react";
 
@@ -30,17 +30,16 @@ interface IWidget {
   anthologyId?: string;
   parentId?: string | null;
   compact?: boolean;
-  onSuccess?: Function;
+  onSuccess?: (data: IArticleDataResponse) => void;
 }
 const ArticleCreateWidget = ({
   studio,
-  anthologyId,
   parentId,
   compact = true,
   onSuccess,
 }: IWidget) => {
   const intl = useIntl();
-  const formRef = useRef<ProFormInstance>();
+  const formRef = useRef<ProFormInstance | undefined>(undefined);
   const [parent, setParent] = useState<IArticleDataResponse>();
   console.log("parentId", parentId);
   useEffect(() => {
@@ -56,10 +55,10 @@ const ArticleCreateWidget = ({
   }, []);
 
   return (
-    <Space direction="vertical">
+    <Space orientation="vertical">
       {parentId ? (
         <Alert
-          message={`从文章 ${parent?.title} 创建子文章`}
+          title={`从文章 ${parent?.title} 创建子文章`}
           type="info"
           closable
         />

+ 4 - 4
dashboard-v6/src/components/article/ArticleDrawer.tsx → dashboard-v6/backup/components/article/ArticleDrawer.tsx

@@ -1,9 +1,9 @@
 import { Button, Drawer, Space, Typography } from "antd";
 import React, { useEffect, useState } from "react";
-import { Link } from "react-router-dom";
+import { Link } from "react-router";
 
-import Article, { ArticleMode, ArticleType } from "./Article";
-import { IArticleDataResponse } from "../api/Article";
+import Article, { type ArticleMode, ArticleType } from "./Article";
+import type { IArticleDataResponse } from "../../api/Article";
 const { Text } = Typography;
 
 interface IWidget {
@@ -86,7 +86,7 @@ const ArticleDrawerWidget = ({
         placement="right"
         onClose={onDrawerClose}
         open={openDrawer}
-        destroyOnClose={true}
+        destroyOnHidden={true}
         extra={
           <Space>
             <Button>

+ 23 - 12
dashboard-v6/src/components/article/ArticleEdit.tsx → dashboard-v6/backup/components/article/ArticleEdit.tsx

@@ -3,7 +3,7 @@ import { useRef, useState } from "react";
 import { useIntl } from "react-intl";
 import {
   ProForm,
-  ProFormInstance,
+  type ProFormInstance,
   ProFormSwitch,
   ProFormText,
   ProFormTextArea,
@@ -12,13 +12,17 @@ import {
 import { Alert, Button, Form, message, Result } from "antd";
 
 import { get, put } from "../../request";
-import { IArticleDataRequest, IArticleResponse } from "../api/Article";
+import type {
+  IArticleDataRequest,
+  IArticleDataResponse,
+  IArticleResponse,
+} from "../../api/Article";
 import LangSelect from "../general/LangSelect";
 import PublicitySelect from "../studio/PublicitySelect";
 
 import MDEditor from "@uiw/react-md-editor";
 import ArticlePrevDrawer from "./ArticlePrevDrawer";
-import { IStudio } from "../auth/Studio";
+import type { IStudio } from "../auth/Studio";
 import ArticleEditTools from "./ArticleEditTools";
 import { useAppSelector } from "../../hooks";
 import { currentUser } from "../../reducers/current-user";
@@ -41,11 +45,15 @@ interface IWidget {
   articleId?: string;
   anthologyId?: string;
   resetButton?: "reset" | "cancel";
-  onReady?: Function;
-  onLoad?: Function;
-  onChange?: Function;
-  onCancel?: Function;
-  onSubmit?: Function;
+  onReady?: (
+    title: string,
+    readonly: boolean,
+    studioName?: string,
+    parentId?: string
+  ) => void;
+  onChange?: (data: IArticleDataResponse) => void;
+  onCancel?: () => void;
+  onSubmit?: (data: IArticleDataResponse) => void;
 }
 
 const ArticleEditWidget = ({
@@ -54,7 +62,6 @@ const ArticleEditWidget = ({
   anthologyId,
   resetButton = "reset",
   onReady,
-  onLoad,
   onChange,
   onCancel,
   onSubmit,
@@ -64,7 +71,7 @@ const ArticleEditWidget = ({
   const [readonly, setReadonly] = useState(false);
   const [content, setContent] = useState<string>();
   const [owner, setOwner] = useState<IStudio>();
-  const formRef = useRef<ProFormInstance>();
+  const formRef = useRef<ProFormInstance | undefined>(undefined);
   const [title, setTitle] = useState<string>();
   const user = useAppSelector(currentUser);
 
@@ -101,7 +108,7 @@ const ArticleEditWidget = ({
         formRef={formRef}
         submitter={{
           // 完全自定义整个区域
-          render: (props, doms) => {
+          render: (props) => {
             console.log(props);
             return [
               <Button
@@ -269,7 +276,11 @@ const ArticleEditWidget = ({
         >
           <MDEditor
             className="pcd_md_editor paper_zh"
-            onChange={(value) => setContent(value)}
+            onChange={(value: unknown) => {
+              if (typeof value === "string") {
+                setContent(value);
+              }
+            }}
             height={450}
             minHeight={200}
             style={{ width: "100%" }}

+ 3 - 3
dashboard-v6/src/components/article/ArticleEditDrawer.tsx → dashboard-v6/backup/components/article/ArticleEditDrawer.tsx

@@ -1,6 +1,6 @@
 import { Drawer } from "antd";
 import React, { useEffect, useState } from "react";
-import { IArticleDataResponse } from "../api/Article";
+import type { IArticleDataResponse } from "../../api/Article";
 
 import ArticleEdit from "./ArticleEdit";
 
@@ -24,7 +24,7 @@ const ArticleEditDrawerWidget = ({
   const [openDrawer, setOpenDrawer] = useState(open);
   const [title, setTitle] = useState("loading");
   const [readonly, setReadonly] = useState(false);
-  const [studioName, setStudioName] = useState<string>();
+  const [_studioName, setStudioName] = useState<string>();
 
   useEffect(() => setOpenDrawer(open), [open]);
   const showDrawer = () => {
@@ -50,7 +50,7 @@ const ArticleEditDrawerWidget = ({
         placement="right"
         onClose={onDrawerClose}
         open={openDrawer}
-        destroyOnClose={true}
+        destroyOnHidden={true}
       >
         <ArticleEdit
           anthologyId={anthologyId}

+ 1 - 1
dashboard-v6/src/components/article/ArticleEditTools.tsx → dashboard-v6/backup/components/article/ArticleEditTools.tsx

@@ -1,4 +1,4 @@
-import { Link } from "react-router-dom";
+import { Link } from "react-router";
 import { useIntl } from "react-intl";
 import { TeamOutlined } from "@ant-design/icons";
 import { Button, Space } from "antd";

+ 18 - 23
dashboard-v6/src/components/article/ArticleList.tsx → dashboard-v6/backup/components/article/ArticleList.tsx

@@ -1,4 +1,4 @@
-import { Link } from "react-router-dom";
+import { Link } from "react-router";
 import { useIntl } from "react-intl";
 import {
   Button,
@@ -11,7 +11,7 @@ import {
   Table,
   Badge,
 } from "antd";
-import { ActionType, ProTable } from "@ant-design/pro-components";
+import { type ActionType, ProTable } from "@ant-design/pro-components";
 import {
   PlusOutlined,
   DeleteOutlined,
@@ -23,15 +23,15 @@ import {
 
 import ArticleCreate from "./ArticleCreate";
 import { delete_, get } from "../../request";
-import { IArticleListResponse, IDeleteResponse } from "../api/Article";
+import type { IArticleListResponse, IDeleteResponse } from "../../api/Article";
 import { PublicityValueEnum } from "../studio/table";
 import { useEffect, useRef, useState } from "react";
 import { ArticleTplModal } from "../template/Builder/ArticleTpl";
 import Share, { EResType } from "../share/Share";
 import AddToAnthology from "./AddToAnthology";
 import AnthologySelect from "../anthology/AnthologySelect";
-import StudioName, { IStudio } from "../auth/Studio";
-import { IUser } from "../auth/User";
+import StudioName, { type IStudio } from "../auth/Studio";
+import type { IUser } from "../auth/User";
 import { getSorterUrl } from "../../utils";
 import TransferCreate from "../transfer/TransferCreate";
 import { TransferOutLinedIcon } from "../../assets/icon";
@@ -79,7 +79,11 @@ interface IWidget {
   studioName?: string;
   editable?: boolean;
   multiple?: boolean;
-  onSelect?: Function;
+  onSelect?: (
+    id: string,
+    title: string,
+    event: React.MouseEvent<HTMLElement, MouseEvent>
+  ) => void;
 }
 const ArticleListWidget = ({
   studioName,
@@ -146,7 +150,7 @@ const ArticleListWidget = ({
       },
     });
   };
-  const ref = useRef<ActionType>();
+  const ref = useRef<ActionType | null>(null);
 
   const [isModalOpen, setIsModalOpen] = useState(false);
   const [shareResId, setShareResId] = useState<string>("");
@@ -187,7 +191,7 @@ const ArticleListWidget = ({
             key: "title",
             tooltip: "过长会自动收缩",
             ellipsis: true,
-            render: (text, row, index, action) => {
+            render: (_text, row) => {
               return (
                 <>
                   <div key={1}>
@@ -223,7 +227,7 @@ const ArticleListWidget = ({
             }),
             dataIndex: "subtitle",
             key: "subtitle",
-            render: (text, row, index, action) => {
+            render: (_text, row) => {
               return (
                 <Space>
                   {row.anthologyTitle}
@@ -272,7 +276,7 @@ const ArticleListWidget = ({
             width: 120,
             valueType: "option",
             hideInTable: !editable,
-            render: (text, row, index, action) => {
+            render: (_text, row, index) => {
               return [
                 <Dropdown.Button
                   trigger={["click", "contextMenu"]}
@@ -368,11 +372,7 @@ const ArticleListWidget = ({
               }
             : undefined
         }
-        tableAlertRender={({
-          selectedRowKeys,
-          selectedRows,
-          onCleanSelected,
-        }) => (
+        tableAlertRender={({ selectedRowKeys, onCleanSelected }) => (
           <Space size={24}>
             <span>
               {intl.formatMessage({ id: "buttons.selected" })}
@@ -383,12 +383,7 @@ const ArticleListWidget = ({
             </span>
           </Space>
         )}
-        tableAlertOptionRender={({
-          intl,
-          selectedRowKeys,
-          selectedRows,
-          onCleanSelected,
-        }) => {
+        tableAlertOptionRender={({ selectedRowKeys, onCleanSelected }) => {
           return (
             <Space>
               <Button
@@ -413,7 +408,7 @@ const ArticleListWidget = ({
             </Space>
           );
         }}
-        request={async (params = {}, sorter, filter) => {
+        request={async (params = {}, sorter) => {
           let url = `/v2/article?view=studio&view2=${activeKey}&name=${studioName}`;
           const offset =
             ((params.current ? params.current : 1) - 1) *
@@ -533,7 +528,7 @@ const ArticleListWidget = ({
       />
 
       <Modal
-        destroyOnClose={true}
+        destroyOnHidden={true}
         width={700}
         title={intl.formatMessage({ id: "labels.collaboration" })}
         open={isModalOpen}

+ 0 - 0
dashboard-v6/src/components/article/ArticleListModal.tsx → dashboard-v6/backup/components/article/ArticleListModal.tsx


+ 10 - 10
dashboard-v6/src/components/article/ArticleListPublic.tsx → dashboard-v6/backup/components/article/ArticleListPublic.tsx

@@ -1,13 +1,13 @@
-import { Link } from "react-router-dom";
+import { Link } from "react-router";
 import { useRef } from "react";
 import { Space } from "antd";
-import { ActionType, ProList } from "@ant-design/pro-components";
+import { type ActionType, ProList } from "@ant-design/pro-components";
 
 import { get } from "../../request";
-import { IArticleListResponse } from "../api/Article";
+import type { IArticleListResponse } from "../../api/Article";
 
-import { IStudio } from "../auth/Studio";
-import { IUser } from "../auth/User";
+import type { IStudio } from "../auth/Studio";
+import type { IUser } from "../auth/User";
 import TimeShow from "../general/TimeShow";
 
 interface DataItem {
@@ -29,8 +29,8 @@ interface IWidget {
   search?: string;
   studioName?: string;
 }
-const ArticleListWidget = ({ search, studioName }: IWidget) => {
-  const ref = useRef<ActionType>();
+const ArticleListWidget = ({ studioName }: IWidget) => {
+  const ref = useRef<ActionType | null>(null);
 
   return (
     <>
@@ -39,7 +39,7 @@ const ArticleListWidget = ({ search, studioName }: IWidget) => {
         actionRef={ref}
         metas={{
           title: {
-            render: (text, row, index, action) => {
+            render: (_text, row) => {
               return <Link to={`/article/article/${row.id}`}>{row.title}</Link>;
             },
           },
@@ -47,7 +47,7 @@ const ArticleListWidget = ({ search, studioName }: IWidget) => {
             dataIndex: "summary",
           },
           subTitle: {
-            render: (text, row, index, action) => {
+            render: (_text, row) => {
               return (
                 <Space>
                   {row.editor?.nickName}
@@ -61,7 +61,7 @@ const ArticleListWidget = ({ search, studioName }: IWidget) => {
             },
           },
         }}
-        request={async (params = {}, sorter, filter) => {
+        request={async (params = {}) => {
           let url = `/v2/article?view=public`;
           const offset =
             ((params.current ? params.current : 1) - 1) *

+ 2 - 2
dashboard-v6/src/components/article/ArticlePrevDrawer.tsx → dashboard-v6/backup/components/article/ArticlePrevDrawer.tsx

@@ -1,7 +1,7 @@
 import { Drawer, Typography } from "antd";
 import React, { useEffect, useState } from "react";
 import { put } from "../../request";
-import { IArticleDataResponse, IArticleResponse } from "../api/Article";
+import type { IArticleDataResponse, IArticleResponse } from "../../api/Article";
 import ArticleView from "./ArticleView";
 
 const { Paragraph } = Typography;
@@ -63,7 +63,7 @@ const ArticlePrevDrawerWidget = ({
         placement="right"
         onClose={onClose}
         open={open}
-        destroyOnClose={true}
+        destroyOnHidden={true}
       >
         <Paragraph type="danger">{errorMsg}</Paragraph>
         {articleData ? (

+ 0 - 0
dashboard-v6/src/components/article/ArticleSkeleton.tsx → dashboard-v6/backup/components/article/ArticleSkeleton.tsx


+ 25 - 24
dashboard-v6/src/components/article/ArticleView.tsx → dashboard-v6/backup/components/article/ArticleView.tsx

@@ -1,11 +1,11 @@
 import { Typography, Divider, Skeleton, Space } from "antd";
 
 import MdView from "../template/MdView";
-import TocPath, { ITocPathNode } from "../corpus/TocPath";
+import TocPath, { type ITocPathNode } from "../corpus/TocPath";
 import PaliChapterChannelList from "../corpus/PaliChapterChannelList";
-import { ArticleMode, ArticleType } from "./Article";
+import type { ArticleMode, ArticleType } from "./Article";
 import VisibleObserver from "../general/VisibleObserver";
-import { IStudio } from "../auth/Studio";
+import type { IStudio } from "../auth/Studio";
 
 const { Paragraph, Title, Text } = Typography;
 export interface IFirstAnthology {
@@ -31,27 +31,24 @@ export interface IWidgetArticleData {
   remains?: boolean;
   anthology?: IFirstAnthology;
   hideTitle?: boolean;
-  onEnd?: Function;
-  onPathChange?: Function;
+  onEnd?: () => void;
+  onPathChange?: (
+    node: ITocPathNode,
+    e: React.MouseEvent<HTMLSpanElement | HTMLAnchorElement, MouseEvent>
+  ) => void;
 }
 
 const ArticleViewWidget = ({
-  id,
   title = "",
   subTitle,
   summary,
   content,
   html = [],
   path = [],
-  created_at,
-  updated_at,
-  owner,
   channels,
   type,
   articleId,
-  anthology,
   hideTitle,
-  mode = "read",
   onEnd,
   remains,
   onPathChange,
@@ -60,29 +57,33 @@ const ArticleViewWidget = ({
 
   let currChannelList = <></>;
   switch (type) {
-    case "chapter":
+    case "chapter": {
       const chapterProps = articleId?.split("-");
-      if (typeof chapterProps === "object" && chapterProps.length > 0) {
-        currChannelList = (
-          <PaliChapterChannelList
-            para={{
-              book: parseInt(chapterProps[0]),
-              para: parseInt(chapterProps[1]),
-            }}
-            channelId={channels}
-            openTarget="_self"
-          />
-        );
+      if (Array.isArray(chapterProps) && chapterProps.length >= 2) {
+        const book = Number(chapterProps[0]);
+        const para = Number(chapterProps[1]);
+
+        if (!Number.isNaN(book) && !Number.isNaN(para)) {
+          currChannelList = (
+            <PaliChapterChannelList
+              para={{ book, para }}
+              channelId={channels}
+              openTarget="_self"
+            />
+          );
+        }
       }
 
       break;
+    }
 
     default:
       break;
   }
+
   return (
     <>
-      <Space direction="vertical">
+      <Space orientation="vertical">
         {hideTitle ? (
           <></>
         ) : (

+ 4 - 4
dashboard-v6/src/components/article/ChapterToc.tsx → dashboard-v6/backup/components/article/ChapterToc.tsx

@@ -1,9 +1,9 @@
-import { Key } from "antd/lib/table/interface";
+import type { Key } from "antd/lib/table/interface";
 import { useState, useEffect } from "react";
 
 import { get } from "../../request";
-import { IChapterToc, IChapterTocListResponse } from "../api/Corpus";
-import { ListNodeData } from "./EditableTree";
+import type { IChapterToc, IChapterTocListResponse } from "../../api/Corpus";
+import type { ListNodeData } from "./EditableTree";
 import TocTree from "./TocTree";
 import { Skeleton } from "antd";
 
@@ -34,7 +34,7 @@ const ChapterTocWidget = ({
           (value) => value.level <= maxLevel
         );
         onData && onData(chapters);
-        const toc = chapters.map((item, id) => {
+        const toc = chapters.map((item, _id) => {
           return {
             key: `${item.book}-${item.paragraph}`,
             title: item.text,

+ 1 - 3
dashboard-v6/src/components/article/EditableTree.tsx → dashboard-v6/backup/components/article/EditableTree.tsx

@@ -2,7 +2,7 @@ import React, { useState } from "react";
 import { useEffect } from "react";
 import { message, Modal, Tree } from "antd";
 import type { DataNode, TreeProps } from "antd/es/tree";
-import { Key } from "antd/lib/table/interface";
+import type { Key } from "antd/lib/table/interface";
 import { DeleteOutlined, SaveOutlined } from "@ant-design/icons";
 import { FileAddOutlined, LinkOutlined } from "@ant-design/icons";
 
@@ -146,7 +146,6 @@ interface IWidget {
   onSave?: Function;
   onAddFile?: Function;
   onAppend?: Function;
-
   onTitleClick?: Function;
 }
 const EditableTreeWidget = ({
@@ -157,7 +156,6 @@ const EditableTreeWidget = ({
   onChange,
   onSelect,
   onSave,
-  onAddFile,
   onAppend,
   onTitleClick,
 }: IWidget) => {

+ 1 - 1
dashboard-v6/src/components/article/EditableTreeNode.tsx → dashboard-v6/backup/components/article/EditableTreeNode.tsx

@@ -2,7 +2,7 @@ import { Button, message, Space, Typography } from "antd";
 import { useState } from "react";
 import { PlusOutlined } from "@ant-design/icons";
 
-import { TreeNodeData } from "./EditableTree";
+import type { TreeNodeData } from "./EditableTree"
 const { Text } = Typography;
 
 interface IWidget {

+ 2 - 2
dashboard-v6/src/components/article/ExerciseList.tsx → dashboard-v6/backup/components/article/ExerciseList.tsx

@@ -1,9 +1,9 @@
 import { useEffect, useState } from "react";
 import { Collapse, Space, Tag } from "antd";
 
-import { ICourseExerciseResponse } from "../api/Course";
+import type { ICourseExerciseResponse } from "../../api/Course";
 import { get } from "../../request";
-import { IUser } from "../auth/User";
+import type { IUser } from "../auth/User";
 import MdView from "../template/MdView";
 
 const { Panel } = Collapse;

+ 1 - 1
dashboard-v6/src/components/article/Find.tsx → dashboard-v6/backup/components/article/Find.tsx

@@ -15,7 +15,7 @@ const FindWidget = () => {
   };
   return (
     <div>
-      <Space direction="vertical">
+      <Space orientation="vertical">
         <Search
           placeholder="input search text"
           allowClear

+ 0 - 0
dashboard-v6/src/components/article/MainMenu.tsx → dashboard-v6/backup/components/article/MainMenu.tsx


+ 1 - 1
dashboard-v6/src/components/article/ModeSwitch.tsx → dashboard-v6/backup/components/article/ModeSwitch.tsx

@@ -1,7 +1,7 @@
 import { Segmented } from "antd";
 import { useEffect, useState } from "react";
 import { useIntl } from "react-intl";
-import { IChannel } from "../channel/Channel";
+import type { IChannel } from "../channel/Channel"
 import ChannelPicker from "../channel/ChannelPicker";
 
 interface IWidget {

+ 1 - 1
dashboard-v6/src/components/article/Nav.tsx → dashboard-v6/backup/components/article/Nav.tsx

@@ -3,7 +3,7 @@ import { Space, Select } from "antd";
 const NavWidget = () => {
   return (
     <div>
-      <Space direction="vertical">
+      <Space orientation="vertical">
         <Select
           defaultValue="current"
           style={{ width: "100%" }}

+ 7 - 22
dashboard-v6/src/components/article/Navigate.tsx → dashboard-v6/backup/components/article/Navigate.tsx

@@ -1,28 +1,10 @@
-import { Button, Space, Typography } from "antd";
 import { useEffect, useState } from "react";
-import { DoubleRightOutlined, DoubleLeftOutlined } from "@ant-design/icons";
 
 import { get } from "../../request";
-import { ArticleType } from "./Article";
+import type { ArticleType } from "./Article";
 import React from "react";
 import NavigateButton from "./NavigateButton";
-import { ITocPathNode } from "../corpus/TocPath";
-
-const { Paragraph, Text } = Typography;
-
-const EllipsisMiddle: React.FC<{
-  suffixCount: number;
-  maxWidth: number;
-  children?: string;
-}> = ({ suffixCount, maxWidth = 500, children = "" }) => {
-  const start = children.slice(0, children.length - suffixCount).trim();
-  const suffix = children.slice(-suffixCount).trim();
-  return (
-    <Text style={{ maxWidth: maxWidth }} ellipsis={{ suffix }}>
-      {start}
-    </Text>
-  );
-};
+import type { ITocPathNode } from "../corpus/TocPath";
 
 interface INavButton {
   title: string;
@@ -42,8 +24,11 @@ interface IWidget {
   type?: ArticleType;
   articleId?: string;
   path?: ITocPathNode[];
-  onChange?: Function;
-  onPathChange?: Function;
+  onChange?: (
+    event: React.MouseEvent<HTMLElement, MouseEvent>,
+    id: string
+  ) => void;
+  onPathChange?: (key: string) => void;
 }
 const NavigateWidget = ({
   type,

+ 2 - 2
dashboard-v6/src/components/article/NavigateButton.tsx → dashboard-v6/backup/components/article/NavigateButton.tsx

@@ -2,7 +2,7 @@ import { Button, Dropdown, Modal, Space, Typography } from "antd";
 import { DoubleRightOutlined, DoubleLeftOutlined } from "@ant-design/icons";
 import { FolderOutlined } from "@ant-design/icons";
 
-import { ITocPathNode } from "../corpus/TocPath";
+import type { ITocPathNode } from "../corpus/TocPath"
 
 const { Paragraph, Text } = Typography;
 
@@ -83,7 +83,7 @@ const NavigateButtonWidget = ({
           placement="top"
           trigger={["hover"]}
           menu={{
-            items: path?.map((item, id) => {
+            items: path?.map((item, _id) => {
               return { label: item.title, key: item.key ?? item.title };
             }),
             onClick: (e) => {

+ 4 - 5
dashboard-v6/src/components/article/PaliTextToc.tsx → dashboard-v6/backup/components/article/PaliTextToc.tsx

@@ -1,9 +1,9 @@
-import { Key } from "antd/lib/table/interface";
+import type { Key } from "antd/lib/table/interface";
 import { useState, useEffect } from "react";
 
 import { get } from "../../request";
-import { IPaliTocListResponse } from "../api/Corpus";
-import { ListNodeData } from "./EditableTree";
+import type { IPaliTocListResponse } from "../../api/Corpus";
+import type { ListNodeData } from "./EditableTree";
 import TocTree from "./TocTree";
 import { Skeleton } from "antd";
 
@@ -19,7 +19,6 @@ const PaliTextTocWidget = ({
   book,
   para,
   series,
-  channel,
   onSelect,
   onClick,
 }: IWidget) => {
@@ -39,7 +38,7 @@ const PaliTextTocWidget = ({
     get<IPaliTocListResponse>(url)
       .then((json) => {
         console.info("api response", json);
-        const toc = json.data.rows.map((item, id) => {
+        const toc = json.data.rows.map((item, _id) => {
           return {
             key: `${item.book}-${item.paragraph}`,
             title: item.toc,

+ 1 - 1
dashboard-v6/src/components/article/ProTabs.tsx → dashboard-v6/backup/components/article/ProTabs.tsx

@@ -117,7 +117,7 @@ const ProTabsWidget = () => {
             setValue2(e.target.value);
           }}
         >
-          <Space direction="vertical">
+          <Space orientation="vertical">
             <Radio
               value="setting"
               onClick={() => {

+ 2 - 11
dashboard-v6/src/components/article/RightPanel.tsx → dashboard-v6/backup/components/article/RightPanel.tsx

@@ -3,9 +3,9 @@ import { useEffect, useState } from "react";
 import { CloseOutlined } from "@ant-design/icons";
 import { FullscreenOutlined, FullscreenExitOutlined } from "@ant-design/icons";
 
-import { IChannel } from "../channel/Channel";
+import type { IChannel } from "../channel/Channel";
 import DictComponent from "../dict/DictComponent";
-import { ArticleType } from "./Article";
+import type { ArticleType } from "./Article";
 import { useAppSelector } from "../../hooks";
 import { openPanel, rightPanel } from "../../reducers/right-panel";
 import store from "../../store";
@@ -16,15 +16,6 @@ import SuggestionBox from "../template/SentEdit/SuggestionBox";
 import ChannelMy from "../channel/ChannelMy";
 import GrammarBook from "../term/GrammarBook";
 
-export type TPanelName =
-  | "dict"
-  | "channel"
-  | "discussion"
-  | "suggestion"
-  | "grammar"
-  | "close"
-  | "open";
-
 interface IWidget {
   curr?: TPanelName;
   type: ArticleType;

+ 1 - 1
dashboard-v6/src/components/article/RightToolsSwitch.tsx → dashboard-v6/backup/components/article/RightToolsSwitch.tsx

@@ -3,7 +3,7 @@ import { useEffect, useState } from "react";
 import { useIntl } from "react-intl";
 import { useAppSelector } from "../../hooks";
 import { rightPanel } from "../../reducers/right-panel";
-import { TPanelName } from "./RightPanel";
+import type { TPanelName } from "./RightPanel"
 
 interface IWidget {
   initMode?: string;

+ 1 - 1
dashboard-v6/src/components/article/TermShell.tsx → dashboard-v6/backup/components/article/TermShell.tsx

@@ -3,7 +3,7 @@ import { useEffect, useState } from "react";
 import { useAppSelector } from "../../hooks";
 import { message } from "../../reducers/command";
 
-import TermEdit, { ITerm } from "../term/TermEdit";
+import TermEdit, { type ITerm } from "../term/TermEdit";
 
 const TermShellWidget = () => {
   const [termProps, setTermProps] = useState<ITerm>();

+ 2 - 3
dashboard-v6/src/components/article/TocTree.tsx → dashboard-v6/backup/components/article/TocTree.tsx

@@ -3,9 +3,9 @@ import { useEffect, useState } from "react";
 
 import type { ListNodeData } from "./EditableTree";
 import PaliText from "../template/Wbw/PaliText";
-import { Key } from "antd/lib/table/interface";
+import type { Key } from "antd/lib/table/interface";
 import { randomString } from "../../utils";
-import { DataNode, EventDataNode } from "antd/es/tree";
+import type { DataNode, EventDataNode } from "antd/es/tree";
 
 const { Text } = Typography;
 
@@ -204,7 +204,6 @@ const TocTreeWidget = ({
         resolve();
         return;
       }
-      const url = onLoad(key);
 
       setTimeout(() => {
         setTree((origin) => {

+ 4 - 4
dashboard-v6/src/components/article/Token.tsx → dashboard-v6/backup/components/article/Token.tsx

@@ -1,17 +1,17 @@
 import { Button, message, Segmented, Typography } from "antd";
-import { SegmentedValue } from "antd/lib/segmented";
+import type { SegmentedValue } from "antd/lib/segmented";
 import { useEffect, useState } from "react";
 import { CopyOutlined } from "@ant-design/icons";
 
 import { useIntl } from "react-intl";
-import { ArticleType } from "./Article";
+import type { ArticleType } from "./Article";
 import { post } from "../../request";
-import {
+import type {
   IPayload,
   ITokenCreate,
   ITokenCreateResponse,
   TPower,
-} from "../api/token";
+} from "../../api/token";
 const { Text } = Typography;
 
 interface IWidget {

+ 1 - 1
dashboard-v6/src/components/article/TokenModal.tsx → dashboard-v6/backup/components/article/TokenModal.tsx

@@ -1,6 +1,6 @@
 import { useEffect, useState } from "react";
 import { Modal } from "antd";
-import { ArticleType } from "./Article";
+import type { ArticleType } from "./Article"
 import Token from "./Token";
 
 interface IWidget {

+ 0 - 0
dashboard-v6/src/components/article/ToolButton.tsx → dashboard-v6/backup/components/article/ToolButton.tsx


+ 2 - 2
dashboard-v6/src/components/article/ToolButtonDiscussion.tsx → dashboard-v6/backup/components/article/ToolButtonDiscussion.tsx

@@ -5,7 +5,7 @@ import { ReloadOutlined } from "@ant-design/icons";
 
 import ToolButton from "./ToolButton";
 import { post } from "../../request";
-import { IUser } from "../auth/User";
+import type { IUser } from "../auth/User"
 import { CommentOutlinedIcon } from "../../assets/icon";
 
 interface IPrTreeData {
@@ -53,7 +53,7 @@ interface IWidget {
   type?: string;
   articleId?: string;
 }
-const ToolButtonDiscussionWidget = ({ type, articleId }: IWidget) => {
+const ToolButtonDiscussionWidget = ({ type, articleId }: IWidget) => { // eslint-disable-line
   const [treeData, setTreeData] = useState<DataNode[]>([]);
   const [loading, setLoading] = useState(false);
   const intl = useIntl();

+ 4 - 4
dashboard-v6/src/components/article/ToolButtonNav.tsx → dashboard-v6/backup/components/article/ToolButtonNav.tsx

@@ -1,5 +1,5 @@
 import { useEffect, useState } from "react";
-import { useNavigate } from "react-router-dom";
+import { useNavigate } from "react-router";
 import { Button, message, Space, Tag, Tree, Typography } from "antd";
 import { CompassOutlined, ReloadOutlined } from "@ant-design/icons";
 
@@ -27,7 +27,7 @@ interface IWidget {
   type?: string;
   articleId?: string;
 }
-const ToolButtonNavWidget = ({ type, articleId }: IWidget) => {
+const ToolButtonNavWidget = ({ type, articleId }: IWidget) => { // eslint-disable-line
   const [treeData, setTreeData] = useState<DataNode[]>([]);
   const [slice, setSlice] = useState<number>(1);
   const navigate = useNavigate();
@@ -103,7 +103,7 @@ const ToolButtonNavWidget = ({ type, articleId }: IWidget) => {
 
     const mSlice: DataNode[] = new Array(currSliceId + 1)
       .fill(1)
-      .map((item, index) => {
+      .map((_item, index) => {
         let sliceStrLen = 0;
         const sliceChildren: string[] = [];
         const newTree: DataNode[] = paraSlice
@@ -144,7 +144,7 @@ const ToolButtonNavWidget = ({ type, articleId }: IWidget) => {
               }
               onMenuClick={(key: string) => {
                 if (sliceChildren.length > 0) {
-                  const [book, para] = sliceChildren[0].split("-");
+                  const [book, _para] = sliceChildren[0].split("-");
                   const paraList = sliceChildren.map(
                     (item) => item.split("-")[1]
                   );

+ 2 - 2
dashboard-v6/src/components/article/ToolButtonNavMore.tsx → dashboard-v6/backup/components/article/ToolButtonNavMore.tsx

@@ -1,11 +1,11 @@
 import { SettingOutlined } from "@ant-design/icons";
-import { Button, Dropdown, MenuProps } from "antd";
+import { Button, Dropdown, type MenuProps } from "antd"
 
 interface IWidget {
   onSliceChange?: Function;
 }
 const CaseFormulaWidget = ({ onSliceChange }: IWidget) => {
-  const sliceOption = new Array(8).fill(1).map((item, index) => {
+  const sliceOption = new Array(8).fill(1).map((_item, index) => {
     return { key: index + 2, label: index + 2 };
   });
   const items: MenuProps["items"] = [

+ 0 - 0
dashboard-v6/src/components/article/ToolButtonNavSliceTitle.tsx → dashboard-v6/backup/components/article/ToolButtonNavSliceTitle.tsx


+ 2 - 2
dashboard-v6/src/components/article/ToolButtonPr.tsx → dashboard-v6/backup/components/article/ToolButtonPr.tsx

@@ -6,7 +6,7 @@ import { ReloadOutlined } from "@ant-design/icons";
 import { HandOutlinedIcon } from "../../assets/icon";
 import ToolButton from "./ToolButton";
 import { post } from "../../request";
-import { IUser } from "../auth/User";
+import type { IUser } from "../auth/User"
 
 interface IPrTreeData {
   book: number;
@@ -51,7 +51,7 @@ interface IWidget {
   type?: string;
   articleId?: string;
 }
-const ToolButtonPrWidget = ({ type, articleId }: IWidget) => {
+const ToolButtonPrWidget = ({ type, articleId }: IWidget) => { // eslint-disable-line
   const [treeData, setTreeData] = useState<DataNode[]>([]);
   const [loading, setLoading] = useState(false);
   const intl = useIntl();

+ 9 - 9
dashboard-v6/src/components/article/ToolButtonSearch.tsx → dashboard-v6/backup/components/article/ToolButtonSearch.tsx

@@ -1,12 +1,12 @@
 import { SearchOutlined } from "@ant-design/icons";
 
 import ToolButton from "./ToolButton";
-import { Input, Tree, TreeDataNode } from "antd";
-import { useSearchParams } from "react-router-dom";
-import { ArticleType } from "./Article";
+import { Input, Tree, type TreeDataNode } from "antd";
+import { useSearchParams } from "react-router";
+import type { ArticleType } from "./Article";
 import { get } from "../../request";
-import { IArticleFtsListResponse } from "../api/Article";
-import { Key } from "antd/lib/table/interface";
+import type { IArticleFtsListResponse } from "../../api/Article";
+import type { Key } from "antd/lib/table/interface";
 import { useState } from "react";
 
 const { Search } = Input;
@@ -23,15 +23,15 @@ const ToolButtonSearchWidget = ({
   anthologyId,
   channels,
 }: IWidget) => {
-  const [searchParams, setSearchParams] = useSearchParams();
-  const [treeNode, setTreeNode] = useState<TreeDataNode[]>();
+  const [_searchParams, _setSearchParams] = useSearchParams();
+  const [treeNode, _setTreeNode] = useState<TreeDataNode[]>();
   const content = (
     <>
       <Search
         placeholder="搜索本章节"
         onSearch={(
           value: string,
-          event?:
+          _event?:
             | React.ChangeEvent<HTMLInputElement>
             | React.MouseEvent<HTMLElement, MouseEvent>
             | React.KeyboardEvent<HTMLInputElement>
@@ -50,7 +50,7 @@ const ToolButtonSearchWidget = ({
         }}
         style={{ width: "100%" }}
       />
-      <Tree onSelect={(selectedKeys: Key[]) => {}} treeData={treeNode} />
+      <Tree onSelect={(_selectedKeys: Key[]) => {}} treeData={treeNode} />
     </>
   );
 

+ 1 - 1
dashboard-v6/src/components/article/ToolButtonSetting.tsx → dashboard-v6/backup/components/article/ToolButtonSetting.tsx

@@ -8,7 +8,7 @@ interface IWidget {
   type?: string;
   articleId?: string;
 }
-const ToolButtonSettingWidget = ({ type, articleId }: IWidget) => {
+const ToolButtonSettingWidget = ({ type, articleId }: IWidget) => { // eslint-disable-line
   const intl = useIntl();
   return (
     <ToolButton

+ 1 - 1
dashboard-v6/src/components/article/ToolButtonTag.tsx → dashboard-v6/backup/components/article/ToolButtonTag.tsx

@@ -6,7 +6,7 @@ interface IWidget {
   type?: string;
   articleId?: string;
 }
-const ToolButtonTagWidget = ({ type, articleId }: IWidget) => {
+const ToolButtonTagWidget = ({ articleId }: IWidget) => {
   const id = articleId?.split("_");
   let tocWidget = <></>;
   if (id && id.length > 0) {

+ 3 - 3
dashboard-v6/src/components/article/ToolButtonToc.tsx → dashboard-v6/backup/components/article/ToolButtonToc.tsx

@@ -1,7 +1,7 @@
 import { MenuOutlined } from "@ant-design/icons";
-import { Key } from "antd/lib/table/interface";
+import type { Key } from "antd/lib/table/interface"
 import AnthologyTocTree from "../anthology/AnthologyTocTree";
-import { ArticleType } from "./Article";
+import type { ArticleType } from "./Article"
 
 import PaliTextToc from "./PaliTextToc";
 import ToolButton from "./ToolButton";
@@ -50,7 +50,7 @@ const ToolButtonTocWidget = ({
         <AnthologyTocTree
           anthologyId={anthologyId}
           channels={channels}
-          onClick={(anthology: string, article: string, target: string) => {
+          onClick={(_anthology: string, article: string, target: string) => {
             if (typeof onSelect !== "undefined") {
               onSelect(article, target);
             }

+ 3 - 25
dashboard-v6/src/components/article/TreeText.tsx → dashboard-v6/backup/components/article/TreeText.tsx

@@ -9,11 +9,11 @@ import {
   TranslationOutlined,
 } from "@ant-design/icons";
 import { get } from "../../request";
-import {
+import type {
   IPaliListResponse,
   IPaliParagraphResponse,
   ISentenceListResponse,
-} from "../api/Corpus";
+} from "../../api/Corpus";
 
 // 定义节点类型
 type NodeType =
@@ -50,7 +50,7 @@ interface TreeNode extends TreeDataNode {
 }
 
 // 定义API响应接口
-interface ApiResponse extends BaseNodeData {}
+type ApiResponse = BaseNodeData;
 
 // 定义组件状态接口
 interface IWidget {
@@ -303,29 +303,7 @@ const TreeTextComponent = ({ type, rootId, channelsId }: IWidget) => {
     setLoadingKeys((prev) => [...prev, key]);
 
     try {
-      let apiUrl = "";
       const id = key.split("_").pop() || "";
-
-      switch (type) {
-        case "book":
-          apiUrl = `/v2/book/${id}`;
-          break;
-        case "chapter":
-          apiUrl = `/v2/chapter/${id}`;
-          break;
-        case "paragraph":
-          apiUrl = `/v2/paragraph/${id}`;
-          break;
-        case "sentence":
-          apiUrl = `/v2/sentence/${id}`;
-          break;
-        case "similar":
-          apiUrl = `/v2/similar/${id}`;
-          break;
-        default:
-          return;
-      }
-
       const data = await mockApiCall(type, id);
 
       // 更新树数据

+ 71 - 0
dashboard-v6/backup/components/article/TypeAnthology.tsx

@@ -0,0 +1,71 @@
+import type { ArticleMode, ArticleType } from "./Article";
+import AnthologyDetail from "./AnthologyDetail";
+import "./article.css";
+import { useState, useMemo } from "react";
+import ErrorResult from "../general/ErrorResult";
+import ArticleSkeleton from "./ArticleSkeleton";
+
+interface IWidget {
+  type?: ArticleType;
+  articleId?: string;
+  mode?: ArticleMode | null;
+  channelId?: string | null;
+  onArticleChange?: (
+    type: string,
+    articleId: string,
+    target: string,
+    extra?: { anthologyId?: string }
+  ) => void;
+  onFinal?: () => void;
+  onLoad?: () => void;
+  onTitle?: (title: string) => void;
+}
+
+const TypeAnthologyWidget = ({
+  channelId,
+  articleId,
+  onArticleChange,
+  onTitle,
+}: IWidget) => {
+  const [loading, setLoading] = useState(false);
+  const [errorCode, setErrorCode] = useState<number | null>(null);
+
+  /** ✅ 避免每次 render 都 split */
+  const channels = useMemo(
+    () => (channelId ? channelId.split("_") : undefined),
+    [channelId]
+  );
+
+  return (
+    <div>
+      {loading && <ArticleSkeleton />}
+
+      {!loading && errorCode && <ErrorResult code={errorCode} />}
+
+      {!errorCode && (
+        <AnthologyDetail
+          visible={!loading}
+          channels={channels}
+          aid={articleId}
+          onArticleClick={(anthologyId, articleId, target) => {
+            onArticleChange?.("article", articleId, target, {
+              anthologyId,
+            });
+          }}
+          onLoading={setLoading}
+          onError={(error: unknown) => {
+            console.error(error);
+            //TODO get real error code
+            setErrorCode(404);
+            //setErrorCode(message); //old code
+          }}
+          onTitle={(value) => {
+            onTitle?.(value);
+          }}
+        />
+      )}
+    </div>
+  );
+};
+
+export default TypeAnthologyWidget;

+ 2 - 2
dashboard-v6/src/components/article/TypeArticle.tsx → dashboard-v6/backup/components/article/TypeArticle.tsx

@@ -1,8 +1,8 @@
 import { useState } from "react";
 import { Modal } from "antd";
 import { ExclamationCircleOutlined } from "@ant-design/icons";
-import { IArticleDataResponse } from "../api/Article";
-import { ArticleMode, ArticleType } from "./Article";
+import type { IArticleDataResponse } from "../../api/Article";
+import type { ArticleMode, ArticleType } from "./Article";
 import TypeArticleReader from "./TypeArticleReader";
 import ArticleEdit from "./ArticleEdit";
 

Some files were not shown because too many files changed in this diff