Browse Source

在外部组件和内部组件 import 之间加空行

visuddhinanda 3 years ago
parent
commit
4369bba4bd
89 changed files with 1392 additions and 1322 deletions
  1. 52 51
      dashboard/src/components/article/AnthologyList.tsx
  2. 1 0
      dashboard/src/components/article/Article.tsx
  3. 5 4
      dashboard/src/components/article/ArticleCard.tsx
  4. 1 0
      dashboard/src/components/article/ArticleCardMainMenu.tsx
  5. 1 1
      dashboard/src/components/article/ArticleCreate.tsx
  6. 1 0
      dashboard/src/components/article/ArticleTabs.tsx
  7. 1 1
      dashboard/src/components/article/Find.tsx
  8. 1 0
      dashboard/src/components/article/PaliTextToc.tsx
  9. 1 0
      dashboard/src/components/article/TermShell.tsx
  10. 1 0
      dashboard/src/components/article/TocTree.tsx
  11. 13 13
      dashboard/src/components/auth/ToStudio.tsx
  12. 11 10
      dashboard/src/components/auth/User.tsx
  13. 1 0
      dashboard/src/components/auth/setting/SettingArticle.tsx
  14. 2 1
      dashboard/src/components/auth/setting/SettingItem.tsx
  15. 1 0
      dashboard/src/components/auth/setting/default.ts
  16. 58 54
      dashboard/src/components/blog/TopArticleCard.tsx
  17. 41 21
      dashboard/src/components/blog/TopArticles.tsx
  18. 1 1
      dashboard/src/components/channel/ChannelCreate.tsx
  19. 2 1
      dashboard/src/components/channel/ChannelList.tsx
  20. 1 0
      dashboard/src/components/channel/ChannelListItem.tsx
  21. 1 0
      dashboard/src/components/channel/ChannelPicker.tsx
  22. 42 42
      dashboard/src/components/channel/ChannelTypeSelect.tsx
  23. 69 69
      dashboard/src/components/corpus/BookTree.tsx
  24. 2 0
      dashboard/src/components/corpus/BookTreeList.tsx
  25. 1 0
      dashboard/src/components/corpus/ChapterCard.tsx
  26. 20 19
      dashboard/src/components/corpus/ChapterFilter.tsx
  27. 1 0
      dashboard/src/components/corpus/ChapterInChannel.tsx
  28. 3 2
      dashboard/src/components/corpus/ChapterList.tsx
  29. 50 49
      dashboard/src/components/corpus/ChapterTagList.tsx
  30. 51 50
      dashboard/src/components/corpus/PaliChapterCard.tsx
  31. 1 0
      dashboard/src/components/corpus/PaliChapterChannelList.tsx
  32. 3 2
      dashboard/src/components/corpus/PaliChapterHead.tsx
  33. 27 26
      dashboard/src/components/corpus/PaliChapterList.tsx
  34. 1 0
      dashboard/src/components/corpus/PaliChapterListByPara.tsx
  35. 1 0
      dashboard/src/components/corpus/PaliChapterListByTag.tsx
  36. 18 18
      dashboard/src/components/dict/Confidence.tsx
  37. 2 2
      dashboard/src/components/dict/DictComponent.tsx
  38. 28 28
      dashboard/src/components/dict/DictContent.tsx
  39. 29 32
      dashboard/src/components/dict/DictCreate.tsx
  40. 5 5
      dashboard/src/components/dict/DictEdit.tsx
  41. 1 2
      dashboard/src/components/dict/DictEditInner.tsx
  42. 20 18
      dashboard/src/components/dict/DictList.tsx
  43. 31 29
      dashboard/src/components/dict/DictSearch.tsx
  44. 38 39
      dashboard/src/components/dict/GrammarPop.tsx
  45. 1 1
      dashboard/src/components/dict/SelectCase.tsx
  46. 40 40
      dashboard/src/components/dict/WordCard.tsx
  47. 27 25
      dashboard/src/components/dict/WordCardByDict.tsx
  48. 1 1
      dashboard/src/components/general/LangSelect.tsx
  49. 30 28
      dashboard/src/components/group/GroupCreate.tsx
  50. 92 87
      dashboard/src/components/group/GroupFile.tsx
  51. 91 84
      dashboard/src/components/group/GroupMember.tsx
  52. 184 184
      dashboard/src/components/studio/EditableTree.tsx
  53. 2 2
      dashboard/src/components/template/Quote.tsx
  54. 1 0
      dashboard/src/components/template/SentEdit/EditInfo.tsx
  55. 1 0
      dashboard/src/components/template/SentEdit/SentCell.tsx
  56. 4 2
      dashboard/src/components/template/SentEdit/SentCellEditable.tsx
  57. 1 0
      dashboard/src/components/template/SentEdit/SentContent.tsx
  58. 0 1
      dashboard/src/components/template/SentEdit/SentMenu.tsx
  59. 1 1
      dashboard/src/components/template/SentEdit/SentTabButton.tsx
  60. 1 0
      dashboard/src/components/template/SentEdit/SuggestionList.tsx
  61. 1 0
      dashboard/src/components/template/SentEdit/SuggestionTabs.tsx
  62. 3 2
      dashboard/src/components/template/SentRead.tsx
  63. 3 2
      dashboard/src/components/template/Term.tsx
  64. 1 0
      dashboard/src/components/template/WbwSent.tsx
  65. 1 0
      dashboard/src/components/template/Wd.tsx
  66. 2 0
      dashboard/src/components/template/utilities.ts
  67. 35 34
      dashboard/src/pages/library/anthology/show.tsx
  68. 1 0
      dashboard/src/pages/library/blog/anthology.tsx
  69. 1 0
      dashboard/src/pages/library/blog/course.tsx
  70. 1 0
      dashboard/src/pages/library/blog/overview.tsx
  71. 1 0
      dashboard/src/pages/library/blog/term.tsx
  72. 1 0
      dashboard/src/pages/library/blog/translation.tsx
  73. 1 0
      dashboard/src/pages/library/course/course.tsx
  74. 1 0
      dashboard/src/pages/library/course/index.tsx
  75. 2 1
      dashboard/src/pages/library/dict/show.tsx
  76. 0 1
      dashboard/src/pages/library/palicanon/chapter.tsx
  77. 6 7
      dashboard/src/pages/studio/anthology/edit.tsx
  78. 1 1
      dashboard/src/pages/studio/article/edit.tsx
  79. 8 9
      dashboard/src/pages/studio/article/index.tsx
  80. 3 2
      dashboard/src/pages/studio/channel/edit.tsx
  81. 3 5
      dashboard/src/pages/studio/channel/index.tsx
  82. 1 3
      dashboard/src/pages/studio/dict/index.tsx
  83. 1 1
      dashboard/src/pages/studio/dict/list.tsx
  84. 2 1
      dashboard/src/pages/studio/group/edit.tsx
  85. 12 11
      dashboard/src/pages/studio/group/index.tsx
  86. 3 4
      dashboard/src/pages/studio/group/list.tsx
  87. 4 3
      dashboard/src/pages/studio/group/show.tsx
  88. 174 188
      dashboard/src/pages/studio/recent/index.tsx
  89. 1 1
      dashboard/src/pages/studio/term/list.tsx

+ 52 - 51
dashboard/src/components/article/AnthologyList.tsx

@@ -1,66 +1,67 @@
 import { useState, useEffect } from "react";
 import { List } from "antd";
+
+import { get } from "../../request";
+import type { IAnthologyListResponse } from "../api/Article";
 import AnthologyCard from "./AnthologyCard";
 import type { IAnthologyData } from "./AnthologyCard";
-import type { IAnthologyListResponse } from "../api/Article";
-import { get } from "../../request";
 
 const defaultData: IAnthologyData[] = [];
 interface IWidgetAnthologyList {
-	view: string;
-	id?: string;
+  view: string;
+  id?: string;
 }
 const Widget = (prop: IWidgetAnthologyList) => {
-	const [tableData, setTableData] = useState(defaultData);
+  const [tableData, setTableData] = useState(defaultData);
 
-	useEffect(() => {
-		console.log("useEffect", prop);
-		if (typeof prop.id === "undefined") {
-			fetchData(prop.view);
-		} else {
-			fetchData(prop.view, prop.id);
-		}
-	}, [prop]);
+  useEffect(() => {
+    console.log("useEffect", prop);
+    if (typeof prop.id === "undefined") {
+      fetchData(prop.view);
+    } else {
+      fetchData(prop.view, prop.id);
+    }
+  }, [prop]);
 
-	function fetchData(view: string, id?: string) {
-		let url = `/v2/anthology?view=${view}` + (id ? `&studio=${id}` : "");
-		console.log("get-url", url);
-		get<IAnthologyListResponse>(url).then(function (response) {
-			console.log("ajex", response);
-			let newTree: IAnthologyData[] = response.data.rows.map((item) => {
-				return {
-					id: item.uid,
-					title: item.title,
-					subTitle: item.subtitle,
-					summary: item.summary,
-					articles: item.article_list.map((al) => {
-						return {
-							key: al.article,
-							title: al.title,
-							level: parseInt(al.level),
-						};
-					}),
-					studio: item.studio,
-					created_at: item.created_at,
-					updated_at: item.updated_at,
-				};
-			});
-			setTableData(newTree);
-		});
-	}
+  function fetchData(view: string, id?: string) {
+    let url = `/v2/anthology?view=${view}` + (id ? `&studio=${id}` : "");
+    console.log("get-url", url);
+    get<IAnthologyListResponse>(url).then(function (response) {
+      console.log("ajex", response);
+      let newTree: IAnthologyData[] = response.data.rows.map((item) => {
+        return {
+          id: item.uid,
+          title: item.title,
+          subTitle: item.subtitle,
+          summary: item.summary,
+          articles: item.article_list.map((al) => {
+            return {
+              key: al.article,
+              title: al.title,
+              level: parseInt(al.level),
+            };
+          }),
+          studio: item.studio,
+          created_at: item.created_at,
+          updated_at: item.updated_at,
+        };
+      });
+      setTableData(newTree);
+    });
+  }
 
-	return (
-		<List
-			itemLayout="vertical"
-			size="large"
-			dataSource={tableData}
-			renderItem={(item) => (
-				<List.Item>
-					<AnthologyCard data={item} />
-				</List.Item>
-			)}
-		/>
-	);
+  return (
+    <List
+      itemLayout="vertical"
+      size="large"
+      dataSource={tableData}
+      renderItem={(item) => (
+        <List.Item>
+          <AnthologyCard data={item} />
+        </List.Item>
+      )}
+    />
+  );
 };
 
 export default Widget;

+ 1 - 0
dashboard/src/components/article/Article.tsx

@@ -1,5 +1,6 @@
 import { message } from "antd";
 import { useEffect, useState } from "react";
+
 import { get } from "../../request";
 import { IArticleDataResponse, IArticleResponse } from "../api/Article";
 import ArticleView from "./ArticleView";

+ 5 - 4
dashboard/src/components/article/ArticleCard.tsx

@@ -1,12 +1,13 @@
+import { useIntl } from "react-intl";
+import { useState } from "react";
 import { Button, Card, Dropdown, Space, Segmented } from "antd";
 import { MoreOutlined, ReloadOutlined } from "@ant-design/icons";
 import type { MenuProps } from "antd";
-import { IWidgetArticleData } from "./ArticleView";
-import { useIntl } from "react-intl";
-import { useState } from "react";
-import ArticleCardMainMenu from "./ArticleCardMainMenu";
+
 import store from "../../store";
 import { modeChange } from "../../reducers/article-mode";
+import { IWidgetArticleData } from "./ArticleView";
+import ArticleCardMainMenu from "./ArticleCardMainMenu";
 import { ArticleMode } from "./Article";
 
 interface IWidgetArticleCard {

+ 1 - 0
dashboard/src/components/article/ArticleCardMainMenu.tsx

@@ -1,5 +1,6 @@
 import { Tabs, Button, Popover } from "antd";
 import { MenuOutlined, PushpinOutlined } from "@ant-design/icons";
+
 import PaliTextToc from "./PaliTextToc";
 import Find from "./Find";
 import Nav from "./Nav";

+ 1 - 1
dashboard/src/components/article/ArticleCreate.tsx

@@ -2,9 +2,9 @@ import { useIntl } from "react-intl";
 import { ProForm, ProFormText } from "@ant-design/pro-components";
 import { message } from "antd";
 
-import LangSelect from "../general/LangSelect";
 import { post } from "../../request";
 import { IArticleCreateRequest, IArticleResponse } from "../api/Article";
+import LangSelect from "../general/LangSelect";
 
 interface IFormData {
   title: string;

+ 1 - 0
dashboard/src/components/article/ArticleTabs.tsx

@@ -1,6 +1,7 @@
 import { useState, useRef, useEffect } from "react";
 import { Tabs, Button } from "antd";
 import { CloseOutlined } from "@ant-design/icons";
+
 import { useAppSelector } from "../../hooks";
 import { siteInfo as _siteInfo } from "../../reducers/open-article";
 import Article from "./Article";

+ 1 - 1
dashboard/src/components/article/Find.tsx

@@ -1,5 +1,5 @@
-import { Input, Space, Select } from "antd";
 import { useState } from "react";
+import { Input, Space, Select } from "antd";
 
 const { Search } = Input;
 

+ 1 - 0
dashboard/src/components/article/PaliTextToc.tsx

@@ -1,4 +1,5 @@
 import { useState, useEffect } from "react";
+
 import { get } from "../../request";
 import { IPaliTocListResponse } from "../api/Corpus";
 import { ListNodeData } from "../studio/EditableTree";

+ 1 - 0
dashboard/src/components/article/TermShell.tsx

@@ -1,4 +1,5 @@
 import { useEffect, useState } from "react";
+
 import { useAppSelector } from "../../hooks";
 import { message } from "../../reducers/command";
 

+ 1 - 0
dashboard/src/components/article/TocTree.tsx

@@ -1,4 +1,5 @@
 import { Tree } from "antd";
+
 import type { TreeProps } from "antd/es/tree";
 import type { ListNodeData } from "../studio/EditableTree";
 

+ 13 - 13
dashboard/src/components/auth/ToStudio.tsx

@@ -1,23 +1,23 @@
 import { Button } from "antd";
-//import { useEffect, useState } from "react";
 import { Link } from "react-router-dom";
+
 import { useAppSelector } from "../../hooks";
 import { currentUser as _currentUser } from "../../reducers/current-user";
 
 const Widget = () => {
-	const user = useAppSelector(_currentUser);
+  const user = useAppSelector(_currentUser);
 
-	if (typeof user !== "undefined") {
-		return (
-			<>
-				<Link to={`/studio/${user.realName}/home`}>
-					<Button type="primary">藏经阁</Button>
-				</Link>
-			</>
-		);
-	} else {
-		return <></>;
-	}
+  if (typeof user !== "undefined") {
+    return (
+      <>
+        <Link to={`/studio/${user.realName}/home`}>
+          <Button type="primary">藏经阁</Button>
+        </Link>
+      </>
+    );
+  } else {
+    return <></>;
+  }
 };
 
 export default Widget;

+ 11 - 10
dashboard/src/components/auth/User.tsx

@@ -1,17 +1,18 @@
 import { Avatar } from "antd";
+
 export interface IUser {
-	id: string;
-	nickName: string;
-	realName: string;
-	avatar: string;
+  id: string;
+  nickName: string;
+  realName: string;
+  avatar: string;
 }
 const Widget = ({ nickName, realName, avatar }: IUser) => {
-	return (
-		<>
-			<Avatar size="small">{nickName?.slice(0, 1)}</Avatar>
-			{nickName}
-		</>
-	);
+  return (
+    <>
+      <Avatar size="small">{nickName?.slice(0, 1)}</Avatar>
+      {nickName}
+    </>
+  );
 };
 
 export default Widget;

+ 1 - 0
dashboard/src/components/auth/setting/SettingArticle.tsx

@@ -1,4 +1,5 @@
 import { Divider } from "antd";
+
 import { SettingFind } from "./default";
 import SettingItem from "./SettingItem";
 

+ 2 - 1
dashboard/src/components/auth/setting/SettingItem.tsx

@@ -1,4 +1,6 @@
+import { useEffect, useState } from "react";
 import { Switch, Typography, Radio, RadioChangeEvent, Select } from "antd";
+
 import {
   onChange as onSettingChanged,
   settingInfo,
@@ -7,7 +9,6 @@ import {
 import { useAppSelector } from "../../../hooks";
 import store from "../../../store";
 import { ISetting } from "./default";
-import { useEffect, useState } from "react";
 
 const { Title, Text } = Typography;
 

+ 1 - 0
dashboard/src/components/auth/setting/default.ts

@@ -1,4 +1,5 @@
 import { useIntl } from "react-intl";
+
 import { ISettingItem } from "../../../reducers/setting";
 
 export interface ISettingItemOption {

+ 58 - 54
dashboard/src/components/blog/TopArticleCard.tsx

@@ -1,73 +1,77 @@
+import { Link } from "react-router-dom";
 import { Card } from "antd";
-import { AppstoreOutlined, LikeOutlined, FieldTimeOutlined } from "@ant-design/icons";
-
+import {
+  AppstoreOutlined,
+  LikeOutlined,
+  FieldTimeOutlined,
+} from "@ant-design/icons";
 import { Space } from "antd";
-import { Link } from "react-router-dom";
 
 interface IIconParamListData {
-	label: React.ReactNode;
-	key: string;
-	icon: React.ReactNode;
+  label: React.ReactNode;
+  key: string;
+  icon: React.ReactNode;
 }
 interface IWidgetIconParamList {
-	data: IIconParamListData[];
+  data: IIconParamListData[];
 }
 const IconParamList = (prop: IWidgetIconParamList) => {
-	return (
-		<><Space>
-			{prop.data.map((item, id) => {
-				return (
-					<Space>
-						{item.icon} {item.label}
-					</Space>
-				);
-			})}
-			</Space>
-		</>
-	);
+  return (
+    <>
+      <Space>
+        {prop.data.map((item, id) => {
+          return (
+            <Space>
+              {item.icon} {item.label}
+            </Space>
+          );
+        })}
+      </Space>
+    </>
+  );
 };
 
 export interface ITopArticleCardData {
-	title: string;
-	link: string;
-	like: number;
-	hit: number;
-	updatedAt: string;
+  title: string;
+  link: string;
+  like: number;
+  hit: number;
+  updatedAt: string;
 }
 interface IWidgetTopArticleCard {
-	data: ITopArticleCardData;
+  data: ITopArticleCardData;
 }
 const Widget = (prop: IWidgetTopArticleCard) => {
-	const items: IIconParamListData[] = [
-		{
-			label: "经藏",
-			key: "sutta",
-			icon: <AppstoreOutlined />,
-		},
-		{
-			label: prop.data.like,
-			key: "like",
-			icon: <LikeOutlined />,
-		},
-		{
-			label: prop.data.updatedAt,
-			key: "updated",
-			icon: <FieldTimeOutlined />,
-		},
-	];
+  const items: IIconParamListData[] = [
+    {
+      label: "经藏",
+      key: "sutta",
+      icon: <AppstoreOutlined />,
+    },
+    {
+      label: prop.data.like,
+      key: "like",
+      icon: <LikeOutlined />,
+    },
+    {
+      label: prop.data.updatedAt,
+      key: "updated",
+      icon: <FieldTimeOutlined />,
+    },
+  ];
 
-	return (
-		<>
-			<Card>
-				<h4>
-					<Link to={prop.data.link}>{prop.data.title}</Link>
-				</h4>
-				<div>
-					<IconParamList data={items} />
-				</div>
-			</Card>
-		</>
-	);
+  return (
+    <>
+      <Card>
+        <h4>
+          <Link to={prop.data.link}>{prop.data.title}</Link>
+        </h4>
+        <div>
+          <IconParamList data={items} />
+        </div>
+      </Card>
+    </>
+  );
 };
 
 export default Widget;

+ 41 - 21
dashboard/src/components/blog/TopArticles.tsx

@@ -1,30 +1,50 @@
 import { Row, Col } from "antd";
+
 import TopArticleCard, { ITopArticleCardData } from "./TopArticleCard";
+
 interface IWidgetTopArticles {
-	studio: string;
+  studio: string;
 }
 const Widget = (prop: IWidgetTopArticles) => {
-	const data: ITopArticleCardData[] = [
-		{
-			title: "法句心品",
-			link: "/my/article/12345",
-			like: 50,
-			hit: 100,
-			updatedAt: "2022-12-3",
-		},
-		{ title: "法句心品", link: "/my/article/12345", like: 50, hit: 100, updatedAt: "2022-12-3" },
-		{ title: "疑惑度脱-应学法", link: "/my/article/12345", like: 50, hit: 100, updatedAt: "2022-12-3" },
-		{ title: "法句心品", link: "/my/article/12345", like: 50, hit: 100, updatedAt: "2022-12-3" },
-	];
+  const data: ITopArticleCardData[] = [
+    {
+      title: "法句心品",
+      link: "/my/article/12345",
+      like: 50,
+      hit: 100,
+      updatedAt: "2022-12-3",
+    },
+    {
+      title: "法句心品",
+      link: "/my/article/12345",
+      like: 50,
+      hit: 100,
+      updatedAt: "2022-12-3",
+    },
+    {
+      title: "疑惑度脱-应学法",
+      link: "/my/article/12345",
+      like: 50,
+      hit: 100,
+      updatedAt: "2022-12-3",
+    },
+    {
+      title: "法句心品",
+      link: "/my/article/12345",
+      like: 50,
+      hit: 100,
+      updatedAt: "2022-12-3",
+    },
+  ];
 
-	const list = data.map((item, id) => {
-		return (
-			<Col flex="400px">
-				<TopArticleCard data={item} key={id} />
-			</Col>
-		);
-	});
-	return <Row>{list}</Row>;
+  const list = data.map((item, id) => {
+    return (
+      <Col flex="400px">
+        <TopArticleCard data={item} key={id} />
+      </Col>
+    );
+  });
+  return <Row>{list}</Row>;
 };
 
 export default Widget;

+ 1 - 1
dashboard/src/components/channel/ChannelCreate.tsx

@@ -2,9 +2,9 @@ import { useIntl } from "react-intl";
 import { ProForm, ProFormText } from "@ant-design/pro-components";
 import { message } from "antd";
 
-import ChannelTypeSelect from "./ChannelTypeSelect";
 import { post } from "../../request";
 import { IApiResponseChannel } from "../api/Channel";
+import ChannelTypeSelect from "./ChannelTypeSelect";
 import LangSelect from "../general/LangSelect";
 
 interface IFormData {

+ 2 - 1
dashboard/src/components/channel/ChannelList.tsx

@@ -1,9 +1,10 @@
 import { useState, useEffect } from "react";
 import { List } from "antd";
-import ChannelListItem from "./ChannelListItem";
+
 import type { ChannelInfoProps } from "../api/Channel";
 import { IApiResponseChannelList } from "../api/Corpus";
 import { get } from "../../request";
+import ChannelListItem from "./ChannelListItem";
 
 export interface ChannelFilterProps {
   chapterProgress: number;

+ 1 - 0
dashboard/src/components/channel/ChannelListItem.tsx

@@ -1,5 +1,6 @@
 import { Space } from "antd";
 import { Avatar } from "antd";
+
 import type { ChannelInfoProps } from "../api/Channel";
 
 type IWidgetChannelListItem = {

+ 1 - 0
dashboard/src/components/channel/ChannelPicker.tsx

@@ -1,5 +1,6 @@
 import { useState } from "react";
 import { Button, Modal } from "antd";
+
 import ChannelPickerTable from "./ChannelPickerTable";
 import { IChannel } from "./Channel";
 

+ 42 - 42
dashboard/src/components/channel/ChannelTypeSelect.tsx

@@ -1,49 +1,49 @@
-import { ProFormSelect } from "@ant-design/pro-components";
 import { useIntl } from "react-intl";
+import { ProFormSelect } from "@ant-design/pro-components";
 
 const Widget = () => {
-	const intl = useIntl();
+  const intl = useIntl();
 
-	const channelTypeOptions = [
-		{
-			value: "translation",
-			label: intl.formatMessage({ id: "channel.type.translation.label" }),
-		},
-		{
-			value: "nissaya",
-			label: intl.formatMessage({ id: "channel.type.nissaya.label" }),
-		},
-		{
-			value: "commentary",
-			label: intl.formatMessage({ id: "channel.type.commentary.label" }),
-		},
-		{
-			value: "original",
-			label: intl.formatMessage({ id: "channel.type.original.label" }),
-		},
-		{
-			value: "general",
-			label: intl.formatMessage({ id: "channel.type.general.label" }),
-		},
-	];
-	return (
-		<ProFormSelect
-			options={channelTypeOptions}
-			initialValue="translation"
-			width="xs"
-			name="type"
-			allowClear={false}
-			label={intl.formatMessage({ id: "channel.type" })}
-			rules={[
-				{
-					required: true,
-					message: intl.formatMessage({
-						id: "channel.type.message.required",
-					}),
-				},
-			]}
-		/>
-	);
+  const channelTypeOptions = [
+    {
+      value: "translation",
+      label: intl.formatMessage({ id: "channel.type.translation.label" }),
+    },
+    {
+      value: "nissaya",
+      label: intl.formatMessage({ id: "channel.type.nissaya.label" }),
+    },
+    {
+      value: "commentary",
+      label: intl.formatMessage({ id: "channel.type.commentary.label" }),
+    },
+    {
+      value: "original",
+      label: intl.formatMessage({ id: "channel.type.original.label" }),
+    },
+    {
+      value: "general",
+      label: intl.formatMessage({ id: "channel.type.general.label" }),
+    },
+  ];
+  return (
+    <ProFormSelect
+      options={channelTypeOptions}
+      initialValue="translation"
+      width="xs"
+      name="type"
+      allowClear={false}
+      label={intl.formatMessage({ id: "channel.type" })}
+      rules={[
+        {
+          required: true,
+          message: intl.formatMessage({
+            id: "channel.type.message.required",
+          }),
+        },
+      ]}
+    />
+  );
 };
 
 export default Widget;

+ 69 - 69
dashboard/src/components/corpus/BookTree.tsx

@@ -1,90 +1,90 @@
-//import { useIntl } from "react-intl";
 import { useState, useEffect } from "react";
 import { DownOutlined } from "@ant-design/icons";
 import { Layout, Space, Tree } from "antd";
 import { Select } from "antd";
 import { Typography } from "antd";
 import type { TreeProps } from "antd/es/tree";
+
 import { get } from "../../request";
 
 const { Text } = Typography;
 
 const { Option } = Select;
 interface IWidgetBookTree {
-	root?: string;
-	path?: string[];
+  root?: string;
+  path?: string[];
 }
 const Widget = (prop: IWidgetBookTree) => {
-	//Library foot bar
-	//const intl = useIntl(); //i18n
-	const defaultTreeData: NewTree[] = [];
-	const [treeData, setTreeData] = useState(defaultTreeData);
+  //Library foot bar
+  //const intl = useIntl(); //i18n
+  const defaultTreeData: NewTree[] = [];
+  const [treeData, setTreeData] = useState(defaultTreeData);
 
-	useEffect(() => {
-		if (typeof prop.root !== "undefined") fetchBookTree(prop.root);
-	}, [prop.root]);
+  useEffect(() => {
+    if (typeof prop.root !== "undefined") fetchBookTree(prop.root);
+  }, [prop.root]);
 
-	type OrgTree = {
-		name: string;
-		tag: string[];
-		children: OrgTree[];
-	};
-	type NewTree = {
-		title: string;
-		key: string;
-		tag: string[];
-		children: NewTree[];
-	};
-	const onSelect: TreeProps["onSelect"] = (selectedKeys, info) => {
-		//let aaa: NewTree = info.node;
-		//console.log("selected", aaa.tag);
-	};
+  type OrgTree = {
+    name: string;
+    tag: string[];
+    children: OrgTree[];
+  };
+  type NewTree = {
+    title: string;
+    key: string;
+    tag: string[];
+    children: NewTree[];
+  };
+  const onSelect: TreeProps["onSelect"] = (selectedKeys, info) => {
+    //let aaa: NewTree = info.node;
+    //console.log("selected", aaa.tag);
+  };
 
-	function fetchBookTree(value: string) {
-		function treeMap(params: OrgTree): NewTree {
-			return {
-				title: params.name,
-				key: params.tag.join(),
-				tag: params.tag,
-				children: Array.isArray(params.children)
-					? params.children.map(treeMap)
-					: [],
-			};
-		}
-		get(`/v2/palibook/${value}`).then((response) => {
-			const myJson = response as unknown as OrgTree[];
-			let newTree = myJson.map(treeMap);
-			setTreeData(newTree);
-		});
-	}
-	const handleChange = (value: string) => {
-		console.log(`selected ${value}`);
-		fetchBookTree(value);
-	};
+  function fetchBookTree(value: string) {
+    function treeMap(params: OrgTree): NewTree {
+      return {
+        title: params.name,
+        key: params.tag.join(),
+        tag: params.tag,
+        children: Array.isArray(params.children)
+          ? params.children.map(treeMap)
+          : [],
+      };
+    }
+    get(`/v2/palibook/${value}`).then((response) => {
+      const myJson = response as unknown as OrgTree[];
+      let newTree = myJson.map(treeMap);
+      setTreeData(newTree);
+    });
+  }
+  const handleChange = (value: string) => {
+    console.log(`selected ${value}`);
+    fetchBookTree(value);
+  };
 
-	// TODO
-	return (
-		<Layout>
-			<Space>
-				<Text>目录风格</Text>
-				<Select
-					defaultValue={prop.root}
-					loading={false}
-					onChange={handleChange}
-				>
-					<Option value="defualt">Defualt</Option>
-					<Option value="cscd">CSCD</Option>
-				</Select>
-			</Space>
-			<Tree
-				showLine
-				switcherIcon={<DownOutlined />}
-				defaultExpandedKeys={["sutta"]}
-				onSelect={onSelect}
-				treeData={treeData}
-			/>
-		</Layout>
-	);
+  // TODO
+  return (
+    <Layout>
+      <Space>
+        <Text>目录风格</Text>
+        <Select
+          defaultValue={prop.root}
+          loading={false}
+          onChange={handleChange}
+        >
+          <Option value="defualt">Defualt</Option>
+          <Option value="cscd">CSCD</Option>
+        </Select>
+      </Space>
+      <Tree
+        showLine
+        switcherIcon={<DownOutlined />}
+        defaultExpandedKeys={["sutta"]}
+        onSelect={onSelect}
+        treeData={treeData}
+      />
+    </Layout>
+  );
 };
 
 export default Widget;

+ 2 - 0
dashboard/src/components/corpus/BookTreeList.tsx

@@ -1,9 +1,11 @@
 import { Link } from "react-router-dom";
 import { useState, useEffect } from "react";
 import { List, Breadcrumb, Card, Select, Space } from "antd";
+
 import { PaliToEn } from "../../utils";
 import { get } from "../../request";
 import { IPaliBookListResponse } from "../api/Corpus";
+
 const { Option } = Select;
 
 interface IWidgetBookTreeList {

+ 1 - 0
dashboard/src/components/corpus/ChapterCard.tsx

@@ -1,5 +1,6 @@
 import { Row, Col } from "antd";
 import { Typography } from "antd";
+
 import TimeShow from "../general/TimeShow";
 import TocPath from "../corpus/TocPath";
 import TagArea from "../tag/TagArea";

+ 20 - 19
dashboard/src/components/corpus/ChapterFilter.tsx

@@ -1,29 +1,30 @@
 import { Layout, Row, Col } from "antd";
 import { Button } from "antd";
+
 import ChapterFilterType from "./ChapterFilterType";
 import ChapterFilterLang from "./ChapterFilterLang";
 import ChapterFilterProgress from "./ChapterFilterProgress";
 
 const Widget = () => {
-	return (
-		<Layout>
-			<Row>
-				<Col>
-					<ChapterFilterType />
-				</Col>
-				<Col>
-					<ChapterFilterLang />
-				</Col>
-				<Col>
-					<ChapterFilterProgress />
-				</Col>
-				<Col>
-					<Button>Search</Button>
-					<Button>Reset</Button>
-				</Col>
-			</Row>
-		</Layout>
-	);
+  return (
+    <Layout>
+      <Row>
+        <Col>
+          <ChapterFilterType />
+        </Col>
+        <Col>
+          <ChapterFilterLang />
+        </Col>
+        <Col>
+          <ChapterFilterProgress />
+        </Col>
+        <Col>
+          <Button>Search</Button>
+          <Button>Reset</Button>
+        </Col>
+      </Row>
+    </Layout>
+  );
 };
 
 export default Widget;

+ 1 - 0
dashboard/src/components/corpus/ChapterInChannel.tsx

@@ -1,6 +1,7 @@
 import { Col, Progress, Row, Space, Tabs } from "antd";
 import { Typography } from "antd";
 import { LikeOutlined, EyeOutlined } from "@ant-design/icons";
+
 import { ChannelInfoProps } from "../api/Channel";
 import ChannelListItem from "../channel/ChannelListItem";
 import TimeShow from "../general/TimeShow";

+ 3 - 2
dashboard/src/components/corpus/ChapterList.tsx

@@ -1,10 +1,11 @@
 import { useState, useEffect } from "react";
 import { List } from "antd";
+
+import { get } from "../../request";
+import { IChapterData, IChapterListResponse } from "../api/Corpus";
 import ChapterCard from "./ChapterCard";
 import type { ChapterData } from "./ChapterCard";
 import type { ChannelFilterProps } from "../channel/ChannelList";
-import { IChapterData, IChapterListResponse } from "../api/Corpus";
-import { get } from "../../request";
 
 const defaultChannelFilterProps: ChannelFilterProps = {
   chapterProgress: 0.9,

+ 50 - 49
dashboard/src/components/corpus/ChapterTagList.tsx

@@ -1,64 +1,65 @@
 import { message, Tag, Button } from "antd";
 import { useState, useEffect } from "react";
+
 import { get } from "../../request";
 import { IApiChapterTag, IApiResponseChapterTagList } from "../api/Corpus";
 
 interface ITagData {
-	title: string;
-	key: string;
+  title: string;
+  key: string;
 }
 interface IWidgetChapterTagList {
-	max?: number;
-	onTagClick: Function;
+  max?: number;
+  onTagClick: Function;
 }
 const Widget = (prop: IWidgetChapterTagList) => {
-	const defaultData: ITagData[] = [];
-	const [tableData, setTableData] = useState(defaultData);
+  const defaultData: ITagData[] = [];
+  const [tableData, setTableData] = useState(defaultData);
 
-	useEffect(() => {
-		console.log("useEffect");
-		fetchData();
-	}, []);
+  useEffect(() => {
+    console.log("useEffect");
+    fetchData();
+  }, []);
 
-	function fetchData() {
-		get(`/v2/progress?view=chapter-tag`)
-			.then((response) => {
-				const json = response as unknown as IApiResponseChapterTagList;
-				const tags: IApiChapterTag[] = json.data.rows;
-				let newTags: ITagData[] = tags.map((item) => {
-					return {
-						key: item.name,
-						title: `${item.name}(${item.count})`,
-					};
-				});
-				setTableData(newTags);
-			})
-			.catch((error) => {
-				message.error(error);
-			});
-	}
-	let iTag = prop.max ? prop.max : tableData.length;
-	if (iTag > tableData.length) {
-		iTag = tableData.length;
-	}
-	return (
-		<>
-			{tableData.map((item, id) => {
-				return (
-					<Tag
-						key={id}
-						onClick={() => {
-							if (typeof prop.onTagClick !== "undefined") {
-								prop.onTagClick(item.key);
-							}
-						}}
-					>
-						<Button type="link">{item.title}</Button>
-					</Tag>
-				);
-			})}
-		</>
-	);
+  function fetchData() {
+    get(`/v2/progress?view=chapter-tag`)
+      .then((response) => {
+        const json = response as unknown as IApiResponseChapterTagList;
+        const tags: IApiChapterTag[] = json.data.rows;
+        let newTags: ITagData[] = tags.map((item) => {
+          return {
+            key: item.name,
+            title: `${item.name}(${item.count})`,
+          };
+        });
+        setTableData(newTags);
+      })
+      .catch((error) => {
+        message.error(error);
+      });
+  }
+  let iTag = prop.max ? prop.max : tableData.length;
+  if (iTag > tableData.length) {
+    iTag = tableData.length;
+  }
+  return (
+    <>
+      {tableData.map((item, id) => {
+        return (
+          <Tag
+            key={id}
+            onClick={() => {
+              if (typeof prop.onTagClick !== "undefined") {
+                prop.onTagClick(item.key);
+              }
+            }}
+          >
+            <Button type="link">{item.title}</Button>
+          </Tag>
+        );
+      })}
+    </>
+  );
 };
 
 export default Widget;

+ 51 - 50
dashboard/src/components/corpus/PaliChapterCard.tsx

@@ -1,67 +1,68 @@
 import { Row, Col } from "antd";
 import { Typography } from "antd";
+
 import TocPath from "./TocPath";
 
 const { Title, Link } = Typography;
 
 export interface IPaliChapterData {
-	Title: string;
-	PaliTitle: string;
-	Path: string;
-	Book: number;
-	Paragraph: number;
+  Title: string;
+  PaliTitle: string;
+  Path: string;
+  Book: number;
+  Paragraph: number;
 }
 
 interface IWidgetPaliChapterCard {
-	data: IPaliChapterData;
-	onTitleClick?: Function;
+  data: IPaliChapterData;
+  onTitleClick?: Function;
 }
 
 const Widget = (prop: IWidgetPaliChapterCard) => {
-	const path = JSON.parse(prop.data.Path);
+  const path = JSON.parse(prop.data.Path);
 
-	return (
-		<>
-			<Row>
-				<Col span={3}>封面</Col>
-				<Col span={21}>
-					<Row>
-						<Col span={16}>
-							<Row>
-								<Col>
-									<Title
-										level={5}
-										onClick={(e) => {
-											if (typeof prop.onTitleClick !== "undefined") {
-												prop.onTitleClick(e);
-											}
-										}}
-									>
-										<Link>{prop.data.Title}</Link>
-									</Title>
-								</Col>
-							</Row>
-							<Row>
-								<Col>{prop.data.PaliTitle}</Col>
-							</Row>
-							<Row>
-								<Col>
-									<TocPath data={path} />
-								</Col>
-							</Row>
-						</Col>
-						<Col span={8}>进度条</Col>
-					</Row>
-					<Row>
-						<Col></Col>
-					</Row>
-					<Row>
-						<Col span={16}></Col>
-					</Row>
-				</Col>
-			</Row>
-		</>
-	);
+  return (
+    <>
+      <Row>
+        <Col span={3}>封面</Col>
+        <Col span={21}>
+          <Row>
+            <Col span={16}>
+              <Row>
+                <Col>
+                  <Title
+                    level={5}
+                    onClick={(e) => {
+                      if (typeof prop.onTitleClick !== "undefined") {
+                        prop.onTitleClick(e);
+                      }
+                    }}
+                  >
+                    <Link>{prop.data.Title}</Link>
+                  </Title>
+                </Col>
+              </Row>
+              <Row>
+                <Col>{prop.data.PaliTitle}</Col>
+              </Row>
+              <Row>
+                <Col>
+                  <TocPath data={path} />
+                </Col>
+              </Row>
+            </Col>
+            <Col span={8}>进度条</Col>
+          </Row>
+          <Row>
+            <Col></Col>
+          </Row>
+          <Row>
+            <Col span={16}></Col>
+          </Row>
+        </Col>
+      </Row>
+    </>
+  );
 };
 
 export default Widget;

+ 1 - 0
dashboard/src/components/corpus/PaliChapterChannelList.tsx

@@ -1,4 +1,5 @@
 import { useState, useEffect } from "react";
+
 import { get } from "../../request";
 import { IApiResponseChapterChannelList } from "../api/Corpus";
 import { IParagraph } from "./BookViewer";

+ 3 - 2
dashboard/src/components/corpus/PaliChapterHead.tsx

@@ -1,10 +1,11 @@
 import { useState, useEffect } from "react";
 import { message } from "antd";
+
+import { IApiResponsePaliChapter } from "../api/Corpus";
+import { get } from "../../request";
 import ChapterHead, { IChapterInfo } from "./ChapterHead";
 import { IParagraph } from "./BookViewer";
 import TocPath, { ITocPathNode } from "./TocPath";
-import { IApiResponsePaliChapter } from "../api/Corpus";
-import { get } from "../../request";
 
 interface IWidgetPaliChapterHead {
   para: IParagraph;

+ 27 - 26
dashboard/src/components/corpus/PaliChapterList.tsx

@@ -1,38 +1,39 @@
 import { List } from "antd";
+
 import PaliChapterCard, { IPaliChapterData } from "./PaliChapterCard";
 
 interface IWidgetPaliChapterList {
-	data: IPaliChapterData[];
-	onChapterClick?: Function;
+  data: IPaliChapterData[];
+  onChapterClick?: Function;
 }
 
 export interface IChapterClickEvent {
-	para: IPaliChapterData;
-	event: React.MouseEvent<HTMLDivElement, MouseEvent>;
+  para: IPaliChapterData;
+  event: React.MouseEvent<HTMLDivElement, MouseEvent>;
 }
 const Widget = (prop: IWidgetPaliChapterList) => {
-	return (
-		<List
-			itemLayout="vertical"
-			size="large"
-			dataSource={prop.data}
-			renderItem={(item) => (
-				<List.Item>
-					<PaliChapterCard
-						onTitleClick={(e: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
-							if (typeof prop.onChapterClick !== "undefined") {
-								prop.onChapterClick({
-									para: item,
-									event: e,
-								});
-							}
-						}}
-						data={item}
-					/>
-				</List.Item>
-			)}
-		/>
-	);
+  return (
+    <List
+      itemLayout="vertical"
+      size="large"
+      dataSource={prop.data}
+      renderItem={(item) => (
+        <List.Item>
+          <PaliChapterCard
+            onTitleClick={(e: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
+              if (typeof prop.onChapterClick !== "undefined") {
+                prop.onChapterClick({
+                  para: item,
+                  event: e,
+                });
+              }
+            }}
+            data={item}
+          />
+        </List.Item>
+      )}
+    />
+  );
 };
 
 export default Widget;

+ 1 - 0
dashboard/src/components/corpus/PaliChapterListByPara.tsx

@@ -1,4 +1,5 @@
 import { useState, useEffect } from "react";
+
 import { get } from "../../request";
 import { IApiResponsePaliChapterList } from "../api/Corpus";
 import { IParagraph } from "./BookViewer";

+ 1 - 0
dashboard/src/components/corpus/PaliChapterListByTag.tsx

@@ -1,4 +1,5 @@
 import { useState, useEffect } from "react";
+
 import { get } from "../../request";
 import { IApiResponsePaliChapterList } from "../api/Corpus";
 import { IPaliChapterData } from "./PaliChapterCard";

+ 18 - 18
dashboard/src/components/dict/Confidence.tsx

@@ -1,27 +1,27 @@
+import { useIntl } from "react-intl";
 import { ProFormSlider } from "@ant-design/pro-components";
 import type { SliderMarks } from "antd/es/slider";
-import { useIntl } from "react-intl";
 
 type IWidgetConfidence = {
-	defaultValue?: number;
+  defaultValue?: number;
 };
 const Widget = ({ defaultValue = 75 }: IWidgetConfidence) => {
-	const intl = useIntl();
-	const marks: SliderMarks = {
-		0: intl.formatMessage({ id: "forms.fields.confidence.0.label" }),
-		25: intl.formatMessage({ id: "forms.fields.confidence.25.label" }),
-		50: intl.formatMessage({ id: "forms.fields.confidence.50.label" }),
-		75: intl.formatMessage({ id: "forms.fields.confidence.75.label" }),
-		100: intl.formatMessage({ id: "forms.fields.confidence.100.label" }),
-	};
-	return (
-		<ProFormSlider
-			name="confidence"
-			label={intl.formatMessage({ id: "forms.fields.confidence.label" })}
-			width="xl"
-			marks={marks}
-		/>
-	);
+  const intl = useIntl();
+  const marks: SliderMarks = {
+    0: intl.formatMessage({ id: "forms.fields.confidence.0.label" }),
+    25: intl.formatMessage({ id: "forms.fields.confidence.25.label" }),
+    50: intl.formatMessage({ id: "forms.fields.confidence.50.label" }),
+    75: intl.formatMessage({ id: "forms.fields.confidence.75.label" }),
+    100: intl.formatMessage({ id: "forms.fields.confidence.100.label" }),
+  };
+  return (
+    <ProFormSlider
+      name="confidence"
+      label={intl.formatMessage({ id: "forms.fields.confidence.label" })}
+      width="xl"
+      marks={marks}
+    />
+  );
 };
 
 export default Widget;

+ 2 - 2
dashboard/src/components/dict/DictComponent.tsx

@@ -2,11 +2,11 @@ import { useState, useEffect } from "react";
 import { Affix, Col, Row } from "antd";
 import { Input } from "antd";
 
-import DictSearch from "./DictSearch";
-
 import { useAppSelector } from "../../hooks";
 import { message } from "../../reducers/command";
 
+import DictSearch from "./DictSearch";
+
 const { Search } = Input;
 
 export interface IWidgetDict {

+ 28 - 28
dashboard/src/components/dict/DictContent.tsx

@@ -1,46 +1,46 @@
 import { Col, Row } from "antd";
 
-import WordCard from "./WordCard";
-import CaseList from "./CaseList";
-import DictList from "./DictList";
-
 import type { IAnchorData } from "./DictList";
 import type { IWidgetWordCardData } from "./WordCard";
 import type { ICaseListData } from "./CaseList";
 
+import WordCard from "./WordCard";
+import CaseList from "./CaseList";
+import DictList from "./DictList";
+
 export interface IWidgetDictContentData {
-	dictlist: IAnchorData[];
-	words: IWidgetWordCardData[];
-	caselist: ICaseListData[];
+  dictlist: IAnchorData[];
+  words: IWidgetWordCardData[];
+  caselist: ICaseListData[];
 }
 export interface IApiDictContentData {
-	ok: boolean;
-	message: string;
-	data: IWidgetDictContentData;
+  ok: boolean;
+  message: string;
+  data: IWidgetDictContentData;
 }
 
 interface IWidgetDictContent {
-	data: IWidgetDictContentData;
+  data: IWidgetDictContentData;
 }
 
 const Widget = (prop: IWidgetDictContent) => {
-	return (
-		<>
-			<Row>
-				<Col flex="200px">
-					<DictList data={prop.data.dictlist} />
-				</Col>
-				<Col flex="760px">
-					{prop.data.words.map((it, id) => {
-						return <WordCard key={id} data={it} />;
-					})}
-				</Col>
-				<Col flex="200px">
-					<CaseList data={prop.data.caselist} />
-				</Col>
-			</Row>
-		</>
-	);
+  return (
+    <>
+      <Row>
+        <Col flex="200px">
+          <DictList data={prop.data.dictlist} />
+        </Col>
+        <Col flex="760px">
+          {prop.data.words.map((it, id) => {
+            return <WordCard key={id} data={it} />;
+          })}
+        </Col>
+        <Col flex="200px">
+          <CaseList data={prop.data.caselist} />
+        </Col>
+      </Row>
+    </>
+  );
 };
 
 export default Widget;

+ 29 - 32
dashboard/src/components/dict/DictCreate.tsx

@@ -1,31 +1,30 @@
-import { ProForm } from "@ant-design/pro-components";
-
 import { useIntl } from "react-intl";
+import { ProForm } from "@ant-design/pro-components";
 import { message } from "antd";
 
 import DictEditInner from "./DictEditInner";
 
 export interface IDictFormData {
-	id: number;
-	word: string;
-	type: string;
-	grammar: string;
-	parent: string;
-	meaning: string;
-	note: string;
-	factors: string;
-	factormeaning: string;
-	lang: string;
-	confidence: number;
+  id: number;
+  word: string;
+  type: string;
+  grammar: string;
+  parent: string;
+  meaning: string;
+  note: string;
+  factors: string;
+  factormeaning: string;
+  lang: string;
+  confidence: number;
 }
 
 type IWidgetDictCreate = {
-	studio: string;
-	word?: string;
+  studio: string;
+  word?: string;
 };
 const Widget = (prop: IWidgetDictCreate) => {
-	const intl = useIntl();
-	/*
+  const intl = useIntl();
+  /*
 	const onLangChange = (value: string) => {
 		console.log(`selected ${value}`);
 	};
@@ -34,21 +33,19 @@ const Widget = (prop: IWidgetDictCreate) => {
 		console.log("search:", value);
 	};
 	*/
-	return (
-		<>
-			<ProForm<IDictFormData>
-				onFinish={async (values: IDictFormData) => {
-					// TODO
-					console.log(values);
-					message.success(
-						intl.formatMessage({ id: "flashes.success" })
-					);
-				}}
-			>
-				<DictEditInner word={prop.word} />
-			</ProForm>
-		</>
-	);
+  return (
+    <>
+      <ProForm<IDictFormData>
+        onFinish={async (values: IDictFormData) => {
+          // TODO
+          console.log(values);
+          message.success(intl.formatMessage({ id: "flashes.success" }));
+        }}
+      >
+        <DictEditInner word={prop.word} />
+      </ProForm>
+    </>
+  );
 };
 
 export default Widget;

+ 5 - 5
dashboard/src/components/dict/DictEdit.tsx

@@ -1,13 +1,13 @@
-import { ProForm } from "@ant-design/pro-components";
-
+import { useEffect } from "react";
 import { useIntl } from "react-intl";
+import { ProForm } from "@ant-design/pro-components";
 import { message } from "antd";
 
-import DictEditInner from "./DictEditInner";
-import { IDictFormData } from "./DictCreate";
 import { IApiResponseDict, IDictDataRequest } from "../api/Dict";
 import { get, put } from "../../request";
-import { useEffect } from "react";
+
+import DictEditInner from "./DictEditInner";
+import { IDictFormData } from "./DictCreate";
 
 type IWidgetDictEdit = {
   wordId: number;

+ 1 - 2
dashboard/src/components/dict/DictEditInner.tsx

@@ -1,11 +1,10 @@
+import { useIntl } from "react-intl";
 import {
   ProForm,
   ProFormText,
   ProFormTextArea,
 } from "@ant-design/pro-components";
 
-import { useIntl } from "react-intl";
-
 import LangSelect from "../general/LangSelect";
 import SelectCase from "./SelectCase";
 import Confidence from "./Confidence";

+ 20 - 18
dashboard/src/components/dict/DictList.tsx

@@ -1,29 +1,31 @@
 import { Anchor } from "antd";
+
 const { Link } = Anchor;
+
 export interface IAnchorData {
-	href: string;
-	title: string;
-	children?: IAnchorData[];
+  href: string;
+  title: string;
+  children?: IAnchorData[];
 }
 interface IWidgetDictList {
-	data: IAnchorData[];
+  data: IAnchorData[];
 }
 const Widget = (prop: IWidgetDictList) => {
-	function GetLink(anchors: IAnchorData[]) {
-		return anchors.map((it, id) => {
-			return (
-				<Link key={id} href={it.href} title={it.title}>
-					{it.children ? GetLink(it.children) : ""}
-				</Link>
-			);
-		});
-	}
+  function GetLink(anchors: IAnchorData[]) {
+    return anchors.map((it, id) => {
+      return (
+        <Link key={id} href={it.href} title={it.title}>
+          {it.children ? GetLink(it.children) : ""}
+        </Link>
+      );
+    });
+  }
 
-	return (
-		<>
-			<Anchor offsetTop={50}>{GetLink(prop.data)}</Anchor>
-		</>
-	);
+  return (
+    <>
+      <Anchor offsetTop={50}>{GetLink(prop.data)}</Anchor>
+    </>
+  );
 };
 
 export default Widget;

+ 31 - 29
dashboard/src/components/dict/DictSearch.tsx

@@ -1,43 +1,45 @@
 import { useState, useEffect } from "react";
-import DictContent from "./DictContent";
+
 import type {
-	IWidgetDictContentData,
-	IApiDictContentData,
+  IWidgetDictContentData,
+  IApiDictContentData,
 } from "./DictContent";
 import { get } from "../../request";
 
+import DictContent from "./DictContent";
+
 interface IWidgetDictSearch {
-	word: string | undefined;
+  word: string | undefined;
 }
 
 const Widget = (prop: IWidgetDictSearch) => {
-	const defaultData: IWidgetDictContentData = {
-		dictlist: [],
-		words: [],
-		caselist: [],
-	};
-	const [tableData, setTableData] = useState(defaultData);
+  const defaultData: IWidgetDictContentData = {
+    dictlist: [],
+    words: [],
+    caselist: [],
+  };
+  const [tableData, setTableData] = useState(defaultData);
 
-	useEffect(() => {
-		console.log("useEffect");
-		const url = `/v2/dict?word=${prop.word}`;
-		console.log("url", url);
-		get(url)
-			.then((response) => {
-				const json = response as unknown as IApiDictContentData;
-				console.log("data", json);
-				setTableData(json.data);
-			})
-			.catch((error) => {
-				console.error(error);
-			});
-	}, [prop.word, setTableData]);
+  useEffect(() => {
+    console.log("useEffect");
+    const url = `/v2/dict?word=${prop.word}`;
+    console.log("url", url);
+    get(url)
+      .then((response) => {
+        const json = response as unknown as IApiDictContentData;
+        console.log("data", json);
+        setTableData(json.data);
+      })
+      .catch((error) => {
+        console.error(error);
+      });
+  }, [prop.word, setTableData]);
 
-	return (
-		<>
-			<DictContent data={tableData} />
-		</>
-	);
+  return (
+    <>
+      <DictContent data={tableData} />
+    </>
+  );
 };
 
 export default Widget;

+ 38 - 39
dashboard/src/components/dict/GrammarPop.tsx

@@ -1,51 +1,50 @@
-/* eslint-disable jsx-a11y/anchor-is-valid */
 import { useState } from "react";
 import { Popover } from "antd";
 import { ProCard } from "@ant-design/pro-components";
+import MDEditor from "@uiw/react-md-editor";
 
 import { ApiGetText } from "../../utils";
-import MDEditor from "@uiw/react-md-editor";
 
 interface IWidgetGrammarPop {
-	text: string;
-	gid: string;
+  text: string;
+  gid: string;
 }
 const Widget = (prop: IWidgetGrammarPop) => {
-	const [guide, setGuide] = useState("Loading");
-	const grammarProfix = "guide-grammar-";
-	const handleMouseMouseEnter = () => {
-		console.log("mouseenter", prop.gid);
-		//sessionStorage缓存
-		const value = sessionStorage.getItem(grammarProfix + prop.gid);
-		if (value === null) {
-			fetchData(prop.gid);
-		} else {
-			const sGuide: string = value ? value : "";
-			setGuide(sGuide);
-		}
-	};
-	const userCard = (
-		<>
-			<ProCard style={{ maxWidth: 500, minWidth: 300, margin: 0 }}>
-				<MDEditor.Markdown source={guide} />
-			</ProCard>
-		</>
-	);
-	function fetchData(key: string) {
-		const url = `/guide/zh-cn/${key}`;
-		ApiGetText(url).then((response: String) => {
-			const text = response as unknown as string;
-			sessionStorage.setItem(grammarProfix + key, text);
-			setGuide(text);
-		});
-	}
-	return (
-		<Popover content={userCard} placement="bottom">
-			<a href="#" onMouseEnter={handleMouseMouseEnter}>
-				{prop.text}
-			</a>
-		</Popover>
-	);
+  const [guide, setGuide] = useState("Loading");
+  const grammarProfix = "guide-grammar-";
+  const handleMouseMouseEnter = () => {
+    console.log("mouseenter", prop.gid);
+    //sessionStorage缓存
+    const value = sessionStorage.getItem(grammarProfix + prop.gid);
+    if (value === null) {
+      fetchData(prop.gid);
+    } else {
+      const sGuide: string = value ? value : "";
+      setGuide(sGuide);
+    }
+  };
+  const userCard = (
+    <>
+      <ProCard style={{ maxWidth: 500, minWidth: 300, margin: 0 }}>
+        <MDEditor.Markdown source={guide} />
+      </ProCard>
+    </>
+  );
+  function fetchData(key: string) {
+    const url = `/guide/zh-cn/${key}`;
+    ApiGetText(url).then((response: String) => {
+      const text = response as unknown as string;
+      sessionStorage.setItem(grammarProfix + key, text);
+      setGuide(text);
+    });
+  }
+  return (
+    <Popover content={userCard} placement="bottom">
+      <a href="#" onMouseEnter={handleMouseMouseEnter}>
+        {prop.text}
+      </a>
+    </Popover>
+  );
 };
 
 export default Widget;

+ 1 - 1
dashboard/src/components/dict/SelectCase.tsx

@@ -1,5 +1,5 @@
-import { Cascader } from "antd";
 import { useIntl } from "react-intl";
+import { Cascader } from "antd";
 
 interface CascaderOption {
   value: string | number;

+ 40 - 40
dashboard/src/components/dict/WordCard.tsx

@@ -1,56 +1,56 @@
 /* eslint-disable jsx-a11y/anchor-is-valid */
 import { Typography } from "antd";
-import IWidgetGrammarPop from "./GrammarPop";
-import WordCardByDict from "./WordCardByDict";
 
 import type { IWordByDict } from "./WordCardByDict";
+import IWidgetGrammarPop from "./GrammarPop";
+import WordCardByDict from "./WordCardByDict";
 
 const { Title, Text } = Typography;
 
 export interface IWidgetWordCardData {
-	word: string;
-	factors: string;
-	parents: string;
-	case: string[];
-	anchor: string;
-	dict: IWordByDict[];
+  word: string;
+  factors: string;
+  parents: string;
+  case: string[];
+  anchor: string;
+  dict: IWordByDict[];
 }
 interface IWidgetWordCard {
-	data: IWidgetWordCardData;
+  data: IWidgetWordCardData;
 }
 const Widget = (prop: IWidgetWordCard) => {
-	const caseList = prop.data.case.map((element) => {
-		return element.split("|").map((it, id) => {
-			if (it.slice(0, 1) === "@") {
-				const [showText, keyText] = it.slice(1).split("-");
-				return <IWidgetGrammarPop key={id} gid={keyText} text={showText} />;
-			} else {
-				return <span key={id * 200}>{it}</span>;
-			}
-		});
-	});
-	return (
-		<>
-			<Title level={4} id={prop.data.anchor}>
-				{prop.data.word}
-			</Title>
+  const caseList = prop.data.case.map((element) => {
+    return element.split("|").map((it, id) => {
+      if (it.slice(0, 1) === "@") {
+        const [showText, keyText] = it.slice(1).split("-");
+        return <IWidgetGrammarPop key={id} gid={keyText} text={showText} />;
+      } else {
+        return <span key={id * 200}>{it}</span>;
+      }
+    });
+  });
+  return (
+    <>
+      <Title level={4} id={prop.data.anchor}>
+        {prop.data.word}
+      </Title>
 
-			<div>
-				<Text>{prop.data.factors}</Text>
-			</div>
-			<div>
-				<Text>{prop.data.parents}</Text>
-			</div>
-			<div>
-				<Text>{caseList}</Text>
-			</div>
-			<div>
-				{prop.data.dict.map((it, id) => {
-					return <WordCardByDict key={id} data={it} />;
-				})}
-			</div>
-		</>
-	);
+      <div>
+        <Text>{prop.data.factors}</Text>
+      </div>
+      <div>
+        <Text>{prop.data.parents}</Text>
+      </div>
+      <div>
+        <Text>{caseList}</Text>
+      </div>
+      <div>
+        {prop.data.dict.map((it, id) => {
+          return <WordCardByDict key={id} data={it} />;
+        })}
+      </div>
+    </>
+  );
 };
 
 export default Widget;

+ 27 - 25
dashboard/src/components/dict/WordCardByDict.tsx

@@ -1,39 +1,41 @@
-/* eslint-disable jsx-a11y/anchor-is-valid */
 import { Card } from "antd";
 import { Typography } from "antd";
+
 import IWidgetGrammarPop from "./GrammarPop";
 
 const { Title, Text } = Typography;
 
 export interface IWordByDict {
-	dictname: string;
-	word: string;
-	note: string;
-	anchor: string;
+  dictname: string;
+  word: string;
+  note: string;
+  anchor: string;
 }
 interface IWidgetWordCardByDict {
-	data: IWordByDict;
+  data: IWordByDict;
 }
 const Widget = (prop: IWidgetWordCardByDict) => {
-	return (
-		<Card>
-			<Title level={5} id={prop.data.anchor}>
-				{prop.data.dictname}
-			</Title>
-			<div>
-				<Text>
-					{prop.data.note.split("|").map((it, id) => {
-						if (it.slice(0, 1) === "@") {
-							const [showText, keyText] = it.slice(1).split("-");
-							return <IWidgetGrammarPop key={id} gid={keyText} text={showText} />;
-						} else {
-							return <span key={id * 200}>{it}</span>;
-						}
-					})}
-				</Text>
-			</div>
-		</Card>
-	);
+  return (
+    <Card>
+      <Title level={5} id={prop.data.anchor}>
+        {prop.data.dictname}
+      </Title>
+      <div>
+        <Text>
+          {prop.data.note.split("|").map((it, id) => {
+            if (it.slice(0, 1) === "@") {
+              const [showText, keyText] = it.slice(1).split("-");
+              return (
+                <IWidgetGrammarPop key={id} gid={keyText} text={showText} />
+              );
+            } else {
+              return <span key={id * 200}>{it}</span>;
+            }
+          })}
+        </Text>
+      </div>
+    </Card>
+  );
 };
 
 export default Widget;

+ 1 - 1
dashboard/src/components/general/LangSelect.tsx

@@ -1,5 +1,5 @@
-import { ProFormSelect } from "@ant-design/pro-components";
 import { useIntl } from "react-intl";
+import { ProFormSelect } from "@ant-design/pro-components";
 
 const Widget = () => {
   const intl = useIntl();

+ 30 - 28
dashboard/src/components/group/GroupCreate.tsx

@@ -1,41 +1,43 @@
-import { ProForm, ProFormText } from "@ant-design/pro-components";
 import { useIntl } from "react-intl";
+import { ProForm, ProFormText } from "@ant-design/pro-components";
 import { message } from "antd";
 
 interface IFormData {
-	name: string;
+  name: string;
 }
 
 type IWidgetGroupCreate = {
-	studio: string | undefined;
+  studio: string | undefined;
 };
 const Widget = (param: IWidgetGroupCreate) => {
-	const intl = useIntl();
+  const intl = useIntl();
 
-	return (
-		<ProForm<IFormData>
-			onFinish={async (values: IFormData) => {
-				// TODO
-				console.log(values);
-				message.success(intl.formatMessage({ id: "flashes.success" }));
-			}}
-		>
-			<ProForm.Group>
-				<ProFormText
-					width="md"
-					name="name"
-					required
-					label={intl.formatMessage({ id: "channel.name" })}
-					rules={[
-						{
-							required: true,
-							message: intl.formatMessage({ id: "channel.create.message.noname" }),
-						},
-					]}
-				/>
-			</ProForm.Group>
-		</ProForm>
-	);
+  return (
+    <ProForm<IFormData>
+      onFinish={async (values: IFormData) => {
+        // TODO
+        console.log(values);
+        message.success(intl.formatMessage({ id: "flashes.success" }));
+      }}
+    >
+      <ProForm.Group>
+        <ProFormText
+          width="md"
+          name="name"
+          required
+          label={intl.formatMessage({ id: "channel.name" })}
+          rules={[
+            {
+              required: true,
+              message: intl.formatMessage({
+                id: "channel.create.message.noname",
+              }),
+            },
+          ]}
+        />
+      </ProForm.Group>
+    </ProForm>
+  );
 };
 
 export default Widget;

+ 92 - 87
dashboard/src/components/group/GroupFile.tsx

@@ -2,102 +2,107 @@ import { useIntl } from "react-intl";
 import { useState } from "react";
 import { ProList } from "@ant-design/pro-components";
 import { Space, Tag, Button, Layout } from "antd";
+
 const { Content } = Layout;
 
 const defaultData = [
-	{
-		id: "1",
-		name: "庄春江工作站",
-		tag: [{ title: "可编辑", color: "success" }],
-		image: "https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg",
-		description: "IAPT|2022-1-3",
-	},
-	{
-		id: "2",
-		name: "元亨寺·CBETA",
-		tag: [{ title: "可编辑", color: "success" }],
-		image: "https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg",
-		description: "我是一条测试的描述",
-	},
-	{
-		id: "3",
-		name: "叶均居士",
-		tag: [{ title: "只读", color: "default" }],
-		image: "https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg",
-		description: "我是一条测试的描述",
-	},
-	{
-		id: "4",
-		name: "玛欣德尊者",
-		tag: [{ title: "只读", color: "default" }],
-		image: "https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg",
-		description: "我是一条测试的描述",
-	},
+  {
+    id: "1",
+    name: "庄春江工作站",
+    tag: [{ title: "可编辑", color: "success" }],
+    image:
+      "https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg",
+    description: "IAPT|2022-1-3",
+  },
+  {
+    id: "2",
+    name: "元亨寺·CBETA",
+    tag: [{ title: "可编辑", color: "success" }],
+    image:
+      "https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg",
+    description: "我是一条测试的描述",
+  },
+  {
+    id: "3",
+    name: "叶均居士",
+    tag: [{ title: "只读", color: "default" }],
+    image:
+      "https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg",
+    description: "我是一条测试的描述",
+  },
+  {
+    id: "4",
+    name: "玛欣德尊者",
+    tag: [{ title: "只读", color: "default" }],
+    image:
+      "https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg",
+    description: "我是一条测试的描述",
+  },
 ];
 type DataItem = typeof defaultData[number];
 type IWidgetGroupFile = {
-	groupid?: string;
+  groupid?: string;
 };
 const Widget = ({ groupid = "" }: IWidgetGroupFile) => {
-	const intl = useIntl(); //i18n
-	const [dataSource, setDataSource] = useState<DataItem[]>(defaultData);
+  const intl = useIntl(); //i18n
+  const [dataSource, setDataSource] = useState<DataItem[]>(defaultData);
 
-	return (
-		<Content>
-			<Space>{groupid}</Space>
-			<ProList<DataItem>
-				rowKey="id"
-				headerTitle={intl.formatMessage({ id: "group.files" })}
-				dataSource={dataSource}
-				showActions="hover"
-				onDataSourceChange={setDataSource}
-				metas={{
-					title: {
-						dataIndex: "name",
-					},
-					avatar: {
-						dataIndex: "image",
-						editable: false,
-					},
-					description: {
-						dataIndex: "description",
-					},
-					content: {
-						dataIndex: "content",
-						editable: false,
-					},
-					subTitle: {
-						render: (text, row, index, action) => {
-							const showtag = row.tag.map((item, id) => {
-								return (
-									<Tag color={item.color} key={id}>
-										{item.title}
-									</Tag>
-								);
-							});
-							return <Space size={0}>{showtag}</Space>;
-						},
-					},
-					actions: {
-						render: (text, row, index, action) => [
-							<Button
-								onClick={() => {
-									action?.startEditable(row.id);
-								}}
-								key="link"
-							>
-								删除
-							</Button>,
-						],
-					},
-				}}
-				pagination={{
-					showQuickJumper: true,
-					showSizeChanger: true,
-				}}
-			/>
-		</Content>
-	);
+  return (
+    <Content>
+      <Space>{groupid}</Space>
+      <ProList<DataItem>
+        rowKey="id"
+        headerTitle={intl.formatMessage({ id: "group.files" })}
+        dataSource={dataSource}
+        showActions="hover"
+        onDataSourceChange={setDataSource}
+        metas={{
+          title: {
+            dataIndex: "name",
+          },
+          avatar: {
+            dataIndex: "image",
+            editable: false,
+          },
+          description: {
+            dataIndex: "description",
+          },
+          content: {
+            dataIndex: "content",
+            editable: false,
+          },
+          subTitle: {
+            render: (text, row, index, action) => {
+              const showtag = row.tag.map((item, id) => {
+                return (
+                  <Tag color={item.color} key={id}>
+                    {item.title}
+                  </Tag>
+                );
+              });
+              return <Space size={0}>{showtag}</Space>;
+            },
+          },
+          actions: {
+            render: (text, row, index, action) => [
+              <Button
+                onClick={() => {
+                  action?.startEditable(row.id);
+                }}
+                key="link"
+              >
+                删除
+              </Button>,
+            ],
+          },
+        }}
+        pagination={{
+          showQuickJumper: true,
+          showSizeChanger: true,
+        }}
+      />
+    </Content>
+  );
 };
 
 export default Widget;

+ 91 - 84
dashboard/src/components/group/GroupMember.tsx

@@ -2,99 +2,106 @@ import { useIntl } from "react-intl";
 import { useState } from "react";
 import { ProList } from "@ant-design/pro-components";
 import { Space, Tag, Button, Layout } from "antd";
+
 const { Content } = Layout;
 
 const defaultData = [
-	{
-		id: "1",
-		name: "小僧善巧",
-		tag: [{ title: "管理员", color: "success" }],
-		image: "https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg",
-	},
-	{
-		id: "2",
-		name: "无语",
-		tag: [{ title: "管理员", color: "success" }],
-		image: "https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg",
-	},
-	{
-		id: "3",
-		name: "慧欣",
-		tag: [],
-		image: "https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg",
-	},
-	{
-		id: "4",
-		name: "谭博文",
-		tag: [],
-		image: "https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg",
-	},
-	{
-		id: "4",
-		name: "豆沙猫",
-		tag: [],
-		image: "https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg",
-	},
-	{
-		id: "4",
-		name: "visuddhinanda",
-		tag: [],
-		image: "https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg",
-	},
+  {
+    id: "1",
+    name: "小僧善巧",
+    tag: [{ title: "管理员", color: "success" }],
+    image:
+      "https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg",
+  },
+  {
+    id: "2",
+    name: "无语",
+    tag: [{ title: "管理员", color: "success" }],
+    image:
+      "https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg",
+  },
+  {
+    id: "3",
+    name: "慧欣",
+    tag: [],
+    image:
+      "https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg",
+  },
+  {
+    id: "4",
+    name: "谭博文",
+    tag: [],
+    image:
+      "https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg",
+  },
+  {
+    id: "4",
+    name: "豆沙猫",
+    tag: [],
+    image:
+      "https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg",
+  },
+  {
+    id: "4",
+    name: "visuddhinanda",
+    tag: [],
+    image:
+      "https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg",
+  },
 ];
 type DataItem = typeof defaultData[number];
 type IWidgetGroupFile = {
-	groupid?: string;
+  groupid?: string;
 };
 const Widget = ({ groupid = "" }: IWidgetGroupFile) => {
-	const intl = useIntl(); //i18n
-	const [dataSource, setDataSource] = useState<DataItem[]>(defaultData);
+  const intl = useIntl(); //i18n
+  const [dataSource, setDataSource] = useState<DataItem[]>(defaultData);
 
-	return (
-		<Content>
-			<Space>{groupid}</Space>
-			<ProList<DataItem>
-				rowKey="id"
-				headerTitle={intl.formatMessage({ id: "group.member" })}
-				dataSource={dataSource}
-				showActions="hover"
-				onDataSourceChange={setDataSource}
-				metas={{
-					title: {
-						dataIndex: "name",
-					},
-					avatar: {
-						dataIndex: "image",
-						editable: false,
-					},
-					subTitle: {
-						render: (text, row, index, action) => {
-							const showtag = row.tag.map((item, id) => {
-								return (
-									<Tag color={item.color} key={id}>
-										{item.title}
-									</Tag>
-								);
-							});
-							return <Space size={0}>{showtag}</Space>;
-						},
-					},
-					actions: {
-						render: (text, row, index, action) => [
-							<Button
-								onClick={() => {
-									action?.startEditable(row.id);
-								}}
-								key="link"
-							>
-								删除
-							</Button>,
-						],
-					},
-				}}
-			/>
-		</Content>
-	);
+  return (
+    <Content>
+      <Space>{groupid}</Space>
+      <ProList<DataItem>
+        rowKey="id"
+        headerTitle={intl.formatMessage({ id: "group.member" })}
+        dataSource={dataSource}
+        showActions="hover"
+        onDataSourceChange={setDataSource}
+        metas={{
+          title: {
+            dataIndex: "name",
+          },
+          avatar: {
+            dataIndex: "image",
+            editable: false,
+          },
+          subTitle: {
+            render: (text, row, index, action) => {
+              const showtag = row.tag.map((item, id) => {
+                return (
+                  <Tag color={item.color} key={id}>
+                    {item.title}
+                  </Tag>
+                );
+              });
+              return <Space size={0}>{showtag}</Space>;
+            },
+          },
+          actions: {
+            render: (text, row, index, action) => [
+              <Button
+                onClick={() => {
+                  action?.startEditable(row.id);
+                }}
+                key="link"
+              >
+                删除
+              </Button>,
+            ],
+          },
+        }}
+      />
+    </Content>
+  );
 };
 
 export default Widget;

+ 184 - 184
dashboard/src/components/studio/EditableTree.tsx

@@ -1,209 +1,209 @@
 import React, { useState } from "react";
+import { useEffect } from "react";
 import { Tree } from "antd";
 import type { DataNode, TreeProps } from "antd/es/tree";
-import { useEffect } from "react";
 
 type TreeNodeData = {
-	key: string;
-	title: string;
-	children: TreeNodeData[];
-	level: number;
+  key: string;
+  title: string;
+  children: TreeNodeData[];
+  level: number;
 };
 export type ListNodeData = {
-	key: string;
-	title: string;
-	level: number;
+  key: string;
+  title: string;
+  level: number;
 };
 
 var tocActivePath: TreeNodeData[] = [];
 function tocGetTreeData(articles: ListNodeData[], active = "") {
-	let treeData = [];
-
-	let treeParents = [];
-	let rootNode: TreeNodeData = {
-		key: "0",
-		title: "root",
-		level: 0,
-		children: [],
-	};
-	treeData.push(rootNode);
-	let lastInsNode: TreeNodeData = rootNode;
-
-	let iCurrLevel = 0;
-	for (let index = 0; index < articles.length; index++) {
-		const element = articles[index];
-
-		let newNode: TreeNodeData = {
-			key: element.key,
-			title: element.title,
-			children: [],
-			level: element.level,
-		};
-		/*
+  let treeData = [];
+
+  let treeParents = [];
+  let rootNode: TreeNodeData = {
+    key: "0",
+    title: "root",
+    level: 0,
+    children: [],
+  };
+  treeData.push(rootNode);
+  let lastInsNode: TreeNodeData = rootNode;
+
+  let iCurrLevel = 0;
+  for (let index = 0; index < articles.length; index++) {
+    const element = articles[index];
+
+    let newNode: TreeNodeData = {
+      key: element.key,
+      title: element.title,
+      children: [],
+      level: element.level,
+    };
+    /*
 		if (active == element.article) {
 			newNode["extraClasses"] = "active";
 		}
 */
-		if (newNode.level > iCurrLevel) {
-			//新的层级比较大,为上一个的子目录
-			treeParents.push(lastInsNode);
-			lastInsNode.children.push(newNode);
-		} else if (newNode.level === iCurrLevel) {
-			//目录层级相同,为平级
-			treeParents[treeParents.length - 1].children.push(newNode);
-		} else {
-			// 小于 挂在上一个层级
-			while (treeParents.length > 1) {
-				treeParents.pop();
-				if (treeParents[treeParents.length - 1].level < newNode.level) {
-					break;
-				}
-			}
-			treeParents[treeParents.length - 1].children.push(newNode);
-		}
-		lastInsNode = newNode;
-		iCurrLevel = newNode.level;
-
-		if (active === element.key) {
-			tocActivePath = [];
-			for (let index = 1; index < treeParents.length; index++) {
-				tocActivePath.push(treeParents[index]);
-			}
-		}
-	}
-	return treeData[0].children;
+    if (newNode.level > iCurrLevel) {
+      //新的层级比较大,为上一个的子目录
+      treeParents.push(lastInsNode);
+      lastInsNode.children.push(newNode);
+    } else if (newNode.level === iCurrLevel) {
+      //目录层级相同,为平级
+      treeParents[treeParents.length - 1].children.push(newNode);
+    } else {
+      // 小于 挂在上一个层级
+      while (treeParents.length > 1) {
+        treeParents.pop();
+        if (treeParents[treeParents.length - 1].level < newNode.level) {
+          break;
+        }
+      }
+      treeParents[treeParents.length - 1].children.push(newNode);
+    }
+    lastInsNode = newNode;
+    iCurrLevel = newNode.level;
+
+    if (active === element.key) {
+      tocActivePath = [];
+      for (let index = 1; index < treeParents.length; index++) {
+        tocActivePath.push(treeParents[index]);
+      }
+    }
+  }
+  return treeData[0].children;
 }
 
 function treeToList(treeNode: TreeNodeData[]): ListNodeData[] {
-	let iTocTreeCurrLevel = 1;
-
-	let arrTocTree: ListNodeData[] = [];
-
-	for (const iterator of treeNode) {
-		getTreeNodeData(iterator);
-	}
-
-	function getTreeNodeData(node: TreeNodeData) {
-		let children = 0;
-		if (typeof node.children != "undefined") {
-			children = node.children.length;
-		}
-		arrTocTree.push({
-			key: node.key,
-			title: node.title,
-			level: iTocTreeCurrLevel,
-		});
-		if (children > 0) {
-			iTocTreeCurrLevel++;
-			for (const iterator of node.children) {
-				getTreeNodeData(iterator);
-			}
-			iTocTreeCurrLevel--;
-		}
-	}
-
-	return arrTocTree;
+  let iTocTreeCurrLevel = 1;
+
+  let arrTocTree: ListNodeData[] = [];
+
+  for (const iterator of treeNode) {
+    getTreeNodeData(iterator);
+  }
+
+  function getTreeNodeData(node: TreeNodeData) {
+    let children = 0;
+    if (typeof node.children != "undefined") {
+      children = node.children.length;
+    }
+    arrTocTree.push({
+      key: node.key,
+      title: node.title,
+      level: iTocTreeCurrLevel,
+    });
+    if (children > 0) {
+      iTocTreeCurrLevel++;
+      for (const iterator of node.children) {
+        getTreeNodeData(iterator);
+      }
+      iTocTreeCurrLevel--;
+    }
+  }
+
+  return arrTocTree;
 }
 interface IWidgetEditableTree {
-	treeData: ListNodeData[];
-	onChange?: Function;
+  treeData: ListNodeData[];
+  onChange?: Function;
 }
 const Widget = (prop: IWidgetEditableTree) => {
-	const data = tocGetTreeData(prop.treeData);
-	console.log("treedata", data);
-	const [gData, setGData] = useState(data);
-	useEffect(() => {
-		const data = tocGetTreeData(prop.treeData);
-		setGData(data);
-	}, [prop]);
-
-	const onDragEnter: TreeProps["onDragEnter"] = (info) => {
-		console.log(info);
-		// expandedKeys 需要受控时设置
-		// setExpandedKeys(info.expandedKeys)
-	};
-
-	const onDrop: TreeProps["onDrop"] = (info) => {
-		console.log(info);
-		const dropKey = info.node.key;
-		const dragKey = info.dragNode.key;
-		const dropPos = info.node.pos.split("-");
-		const dropPosition =
-			info.dropPosition - Number(dropPos[dropPos.length - 1]);
-
-		const loop = (
-			data: DataNode[],
-			key: React.Key,
-			callback: (node: DataNode, i: number, data: DataNode[]) => void
-		) => {
-			for (let i = 0; i < data.length; i++) {
-				if (data[i].key === key) {
-					return callback(data[i], i, data);
-				}
-				if (data[i].children) {
-					loop(data[i].children!, key, callback);
-				}
-			}
-		};
-		const data = [...gData];
-
-		// Find dragObject
-		let dragObj: DataNode;
-		loop(data, dragKey, (item, index, arr) => {
-			arr.splice(index, 1);
-			dragObj = item;
-		});
-
-		if (!info.dropToGap) {
-			// Drop on the content
-			loop(data, dropKey, (item) => {
-				item.children = item.children || [];
-				// where to insert 示例添加到头部,可以是随意位置
-				item.children.unshift(dragObj);
-			});
-		} else if (
-			((info.node as any).props.children || []).length > 0 && // Has children
-			(info.node as any).props.expanded && // Is expanded
-			dropPosition === 1 // On the bottom gap
-		) {
-			loop(data, dropKey, (item) => {
-				item.children = item.children || [];
-				// where to insert 示例添加到头部,可以是随意位置
-				item.children.unshift(dragObj);
-				// in previous version, we use item.children.push(dragObj) to insert the
-				// item to the tail of the children
-			});
-		} else {
-			let ar: DataNode[] = [];
-			let i: number;
-			loop(data, dropKey, (_item, index, arr) => {
-				ar = arr;
-				i = index;
-			});
-			if (dropPosition === -1) {
-				ar.splice(i!, 0, dragObj!);
-			} else {
-				ar.splice(i! + 1, 0, dragObj!);
-			}
-		}
-		setGData(data);
-		if (typeof prop.onChange !== "undefined") {
-			prop.onChange(treeToList(data));
-		}
-	};
-
-	return (
-		<>
-			<Tree
-				rootClassName="draggable-tree"
-				draggable
-				blockNode
-				onDragEnter={onDragEnter}
-				onDrop={onDrop}
-				treeData={gData}
-			/>
-		</>
-	);
+  const data = tocGetTreeData(prop.treeData);
+  console.log("treedata", data);
+  const [gData, setGData] = useState(data);
+  useEffect(() => {
+    const data = tocGetTreeData(prop.treeData);
+    setGData(data);
+  }, [prop]);
+
+  const onDragEnter: TreeProps["onDragEnter"] = (info) => {
+    console.log(info);
+    // expandedKeys 需要受控时设置
+    // setExpandedKeys(info.expandedKeys)
+  };
+
+  const onDrop: TreeProps["onDrop"] = (info) => {
+    console.log(info);
+    const dropKey = info.node.key;
+    const dragKey = info.dragNode.key;
+    const dropPos = info.node.pos.split("-");
+    const dropPosition =
+      info.dropPosition - Number(dropPos[dropPos.length - 1]);
+
+    const loop = (
+      data: DataNode[],
+      key: React.Key,
+      callback: (node: DataNode, i: number, data: DataNode[]) => void
+    ) => {
+      for (let i = 0; i < data.length; i++) {
+        if (data[i].key === key) {
+          return callback(data[i], i, data);
+        }
+        if (data[i].children) {
+          loop(data[i].children!, key, callback);
+        }
+      }
+    };
+    const data = [...gData];
+
+    // Find dragObject
+    let dragObj: DataNode;
+    loop(data, dragKey, (item, index, arr) => {
+      arr.splice(index, 1);
+      dragObj = item;
+    });
+
+    if (!info.dropToGap) {
+      // Drop on the content
+      loop(data, dropKey, (item) => {
+        item.children = item.children || [];
+        // where to insert 示例添加到头部,可以是随意位置
+        item.children.unshift(dragObj);
+      });
+    } else if (
+      ((info.node as any).props.children || []).length > 0 && // Has children
+      (info.node as any).props.expanded && // Is expanded
+      dropPosition === 1 // On the bottom gap
+    ) {
+      loop(data, dropKey, (item) => {
+        item.children = item.children || [];
+        // where to insert 示例添加到头部,可以是随意位置
+        item.children.unshift(dragObj);
+        // in previous version, we use item.children.push(dragObj) to insert the
+        // item to the tail of the children
+      });
+    } else {
+      let ar: DataNode[] = [];
+      let i: number;
+      loop(data, dropKey, (_item, index, arr) => {
+        ar = arr;
+        i = index;
+      });
+      if (dropPosition === -1) {
+        ar.splice(i!, 0, dragObj!);
+      } else {
+        ar.splice(i! + 1, 0, dragObj!);
+      }
+    }
+    setGData(data);
+    if (typeof prop.onChange !== "undefined") {
+      prop.onChange(treeToList(data));
+    }
+  };
+
+  return (
+    <>
+      <Tree
+        rootClassName="draggable-tree"
+        draggable
+        blockNode
+        onDragEnter={onDragEnter}
+        onDrop={onDrop}
+        treeData={gData}
+      />
+    </>
+  );
 };
 
 export default Widget;

+ 2 - 2
dashboard/src/components/template/Quote.tsx

@@ -1,7 +1,7 @@
-import { ProCard } from "@ant-design/pro-components";
 import { Button, Popover } from "antd";
-import { SearchOutlined, CopyOutlined } from "@ant-design/icons";
 import { Typography } from "antd";
+import { SearchOutlined, CopyOutlined } from "@ant-design/icons";
+import { ProCard } from "@ant-design/pro-components";
 
 const { Text, Link } = Typography;
 

+ 1 - 0
dashboard/src/components/template/SentEdit/EditInfo.tsx

@@ -1,5 +1,6 @@
 import { Typography } from "antd";
 import { Space } from "antd";
+
 import User from "../../auth/User";
 import TimeShow from "../../general/TimeShow";
 import { ISentence } from "../SentEdit";

+ 1 - 0
dashboard/src/components/template/SentEdit/SentCell.tsx

@@ -1,4 +1,5 @@
 import { useState } from "react";
+
 import { ISentence } from "../SentEdit";
 import SentEditMenu from "./SentEditMenu";
 import SentCellEditable from "./SentCellEditable";

+ 4 - 2
dashboard/src/components/template/SentEdit/SentCellEditable.tsx

@@ -1,11 +1,13 @@
+import { useState } from "react";
+import { useIntl } from "react-intl";
 import { Button, message, Typography } from "antd";
 import { SaveOutlined } from "@ant-design/icons";
 import TextArea from "antd/lib/input/TextArea";
-import { useState } from "react";
-import { useIntl } from "react-intl";
+
 import { put } from "../../../request";
 import { ISentenceRequest, ISentenceResponse } from "../../api/Corpus";
 import { ISentence } from "../SentEdit";
+
 const { Text } = Typography;
 
 interface ISentCellEditable {

+ 1 - 0
dashboard/src/components/template/SentEdit/SentContent.tsx

@@ -1,6 +1,7 @@
 import { ISentence } from "../SentEdit";
 import SentCell from "./SentCell";
 import { WbwSentCtl } from "../WbwSent";
+
 interface IWidgetSentContent {
   origin?: ISentence[];
   translation?: ISentence[];

+ 0 - 1
dashboard/src/components/template/SentEdit/SentMenu.tsx

@@ -1,7 +1,6 @@
 import { useState } from "react";
 import { Button, Dropdown } from "antd";
 import { MoreOutlined } from "@ant-design/icons";
-
 import type { MenuProps } from "antd";
 
 const onClick: MenuProps["onClick"] = ({ key }) => {

+ 1 - 1
dashboard/src/components/template/SentEdit/SentTabButton.tsx

@@ -1,5 +1,6 @@
 import { useIntl } from "react-intl";
 import { Badge, Dropdown } from "antd";
+import type { MenuProps } from "antd";
 import {
   OneToOneOutlined,
   LinkOutlined,
@@ -11,7 +12,6 @@ import {
   ISite,
   refresh as refreshLayout,
 } from "../../../reducers/open-article";
-import type { MenuProps } from "antd";
 
 const handleButtonClick = (e: React.MouseEvent<HTMLButtonElement>) => {
   console.log("click left button", e);

+ 1 - 0
dashboard/src/components/template/SentEdit/SuggestionList.tsx

@@ -1,4 +1,5 @@
 import { useEffect, useState } from "react";
+
 import { get } from "../../../request";
 import { ISuggestionListResponse } from "../../api/Suggestion";
 import { IChannel } from "../../channel/Channel";

+ 1 - 0
dashboard/src/components/template/SentEdit/SuggestionTabs.tsx

@@ -1,6 +1,7 @@
 import { useState } from "react";
 import { RadioChangeEvent, Space } from "antd";
 import { Radio } from "antd";
+
 import { ISentence } from "../SentEdit";
 import { SuggestionIcon } from "../../../assets/icon";
 import SuggestionAdd from "./SuggestionAdd";

+ 3 - 2
dashboard/src/components/template/SentRead.tsx

@@ -1,7 +1,6 @@
 import { useEffect, useRef, useState } from "react";
 import { Tooltip, Button } from "antd";
-import MdView from "./MdView";
-import { ISentence } from "./SentEdit";
+
 import { useAppSelector } from "../../hooks";
 import {
   onChangeKey,
@@ -10,6 +9,8 @@ import {
 } from "../../reducers/setting";
 import { GetUserSetting } from "../auth/setting/default";
 import { TCodeConvertor } from "./utilities";
+import { ISentence } from "./SentEdit";
+import MdView from "./MdView";
 
 interface IWidgetSentReadFrame {
   origin?: ISentence[];

+ 3 - 2
dashboard/src/components/template/Term.tsx

@@ -1,7 +1,8 @@
-import { ProCard } from "@ant-design/pro-components";
 import { Button, Popover } from "antd";
-import { SearchOutlined } from "@ant-design/icons";
 import { Typography } from "antd";
+import { SearchOutlined } from "@ant-design/icons";
+import { ProCard } from "@ant-design/pro-components";
+
 import TermCreate, { IWidgetDictCreate } from "../term/TermCreate";
 import { command } from "../../reducers/command";
 import store from "../../store";

+ 1 - 0
dashboard/src/components/template/WbwSent.tsx

@@ -1,4 +1,5 @@
 import { useEffect, useState } from "react";
+
 import { useAppSelector } from "../../hooks";
 import { mode } from "../../reducers/article-mode";
 import WbwWord, { IWbw, IWbwFields } from "./Wbw/WbwWord";

+ 1 - 0
dashboard/src/components/template/Wd.tsx

@@ -1,4 +1,5 @@
 import { useState } from "react";
+
 import { command } from "../../reducers/command";
 import store from "../../store";
 import { IWidgetDict } from "../dict/DictComponent";

+ 2 - 0
dashboard/src/components/template/utilities.ts

@@ -1,4 +1,5 @@
 import React from "react";
+
 import MdTpl from "./MdTpl";
 import { WdCtl } from "./Wd";
 import { Divider } from "antd";
@@ -6,6 +7,7 @@ import { roman_to_my, my_to_roman } from "../code/my";
 import { roman_to_si } from "../code/si";
 import { roman_to_thai } from "../code/thai";
 import { roman_to_taitham } from "../code/tai-tham";
+
 export type TCodeConvertor =
   | "none"
   | "roman"

+ 35 - 34
dashboard/src/pages/library/anthology/show.tsx

@@ -1,46 +1,47 @@
 import { useParams } from "react-router-dom";
 import { Layout, Affix, Col, Row } from "antd";
+
 import AnthologyDetail from "../../../components/article/AnthologyDetail";
 
 const { Content, Header } = Layout;
 
 const Widget = () => {
-	// TODO
-	const { id, tags } = useParams(); //url 参数
-	let aid = id ? id : "";
-	let channel = tags ? tags : "";
+  // TODO
+  const { id, tags } = useParams(); //url 参数
+  let aid = id ? id : "";
+  let channel = tags ? tags : "";
 
-	const pageMaxWidth = "1260px";
-	return (
-		<Layout>
-			<Affix offsetTop={0}>
-				<Header style={{ backgroundColor: "gray", height: "3.5em" }}>
-					<Col flex="auto"></Col>
-					<Col flex={pageMaxWidth}>
-						<div>
-							{aid}@{channel}
-						</div>
-					</Col>
-					<Col flex="auto"></Col>
-				</Header>
-			</Affix>
+  const pageMaxWidth = "1260px";
+  return (
+    <Layout>
+      <Affix offsetTop={0}>
+        <Header style={{ backgroundColor: "gray", height: "3.5em" }}>
+          <Col flex="auto"></Col>
+          <Col flex={pageMaxWidth}>
+            <div>
+              {aid}@{channel}
+            </div>
+          </Col>
+          <Col flex="auto"></Col>
+        </Header>
+      </Affix>
 
-			<Content>
-				<Row>
-					<Col flex="auto"></Col>
-					<Col flex={pageMaxWidth}>
-						<Row>
-							<Col span="18">
-								<AnthologyDetail aid={aid} />
-							</Col>
-							<Col span="6"></Col>
-						</Row>
-					</Col>
-					<Col flex="auto"></Col>
-				</Row>
-			</Content>
-		</Layout>
-	);
+      <Content>
+        <Row>
+          <Col flex="auto"></Col>
+          <Col flex={pageMaxWidth}>
+            <Row>
+              <Col span="18">
+                <AnthologyDetail aid={aid} />
+              </Col>
+              <Col span="6"></Col>
+            </Row>
+          </Col>
+          <Col flex="auto"></Col>
+        </Row>
+      </Content>
+    </Layout>
+  );
 };
 
 export default Widget;

+ 1 - 0
dashboard/src/pages/library/blog/anthology.tsx

@@ -1,4 +1,5 @@
 import { useParams } from "react-router-dom";
+
 import AnthologyList from "../../../components/article/AnthologyList";
 import BlogNav from "../../../components/blog/BlogNav";
 

+ 1 - 0
dashboard/src/pages/library/blog/course.tsx

@@ -1,4 +1,5 @@
 import { useParams } from "react-router-dom";
+
 import BlogNav from "../../../components/blog/BlogNav";
 
 const Widget = () => {

+ 1 - 0
dashboard/src/pages/library/blog/overview.tsx

@@ -1,6 +1,7 @@
 import { useParams } from "react-router-dom";
 import { Row, Col } from "antd";
 import { Affix } from "antd";
+
 import BlogNav from "../../../components/blog/BlogNav";
 import Profile from "../../../components/blog/Profile";
 import AuthorTimeLine from "../../../components/blog/TimeLine";

+ 1 - 0
dashboard/src/pages/library/blog/term.tsx

@@ -1,4 +1,5 @@
 import { useParams } from "react-router-dom";
+
 import BlogNav from "../../../components/blog/BlogNav";
 
 const Widget = () => {

+ 1 - 0
dashboard/src/pages/library/blog/translation.tsx

@@ -1,4 +1,5 @@
 import { useParams } from "react-router-dom";
+
 import BlogNav from "../../../components/blog/BlogNav";
 
 const Widget = () => {

+ 1 - 0
dashboard/src/pages/library/course/course.tsx

@@ -1,5 +1,6 @@
 import { Link } from "react-router-dom";
 import { useParams } from "react-router-dom";
+
 const Widget = () => {
 	// TODO
 	const { courseid } = useParams(); //url 参数

+ 1 - 0
dashboard/src/pages/library/course/index.tsx

@@ -1,4 +1,5 @@
 import { Outlet } from "react-router-dom";
+
 import HeadBar from "../../../components/library/HeadBar";
 import FooterBar from "../../../components/library/FooterBar";
 

+ 2 - 1
dashboard/src/pages/library/dict/show.tsx

@@ -1,10 +1,11 @@
+import { useState } from "react";
 import { useParams } from "react-router-dom";
 import { useNavigate } from "react-router-dom";
 import { Layout, Affix, Col, Row } from "antd";
 import { Input } from "antd";
 
-import { useState } from "react";
 import DictSearch from "../../../components/dict/DictSearch";
+
 const { Content } = Layout;
 const { Search } = Input;
 

+ 0 - 1
dashboard/src/pages/library/palicanon/chapter.tsx

@@ -1,6 +1,5 @@
 import { useParams } from "react-router-dom";
 import { useNavigate } from "react-router-dom";
-
 import { Layout } from "antd";
 
 import BookViewer, { IParagraph } from "../../../components/corpus/BookViewer";

+ 6 - 7
dashboard/src/pages/studio/anthology/edit.tsx

@@ -1,6 +1,6 @@
+import { useState } from "react";
 import { useParams } from "react-router-dom";
 import { useIntl } from "react-intl";
-
 import {
   ProForm,
   ProFormText,
@@ -8,16 +8,15 @@ import {
 } from "@ant-design/pro-components";
 import { Card, Col, message, Row } from "antd";
 
-import EditableTree from "../../../components/studio/EditableTree";
-import type { ListNodeData } from "../../../components/studio/EditableTree";
-import LangSelect from "../../../components/general/LangSelect";
-import PublicitySelect from "../../../components/studio/PublicitySelect";
+import { get, put } from "../../../request";
 import {
   IAnthologyDataRequest,
   IAnthologyResponse,
 } from "../../../components/api/Article";
-import { get, put } from "../../../request";
-import { useState } from "react";
+import EditableTree from "../../../components/studio/EditableTree";
+import type { ListNodeData } from "../../../components/studio/EditableTree";
+import LangSelect from "../../../components/general/LangSelect";
+import PublicitySelect from "../../../components/studio/PublicitySelect";
 import GoBack from "../../../components/studio/GoBack";
 
 interface IFormData {

+ 1 - 1
dashboard/src/pages/studio/article/edit.tsx

@@ -1,3 +1,4 @@
+import { useState } from "react";
 import { useParams } from "react-router-dom";
 import { useIntl } from "react-intl";
 import {
@@ -15,7 +16,6 @@ import {
 import LangSelect from "../../../components/general/LangSelect";
 import PublicitySelect from "../../../components/studio/PublicitySelect";
 import GoBack from "../../../components/studio/GoBack";
-import { useState } from "react";
 
 interface IFormData {
   uid: string;

+ 8 - 9
dashboard/src/pages/studio/article/index.tsx

@@ -7,15 +7,14 @@ import { styleStudioContent } from "../style";
 const { Content } = Layout;
 
 const Widget = () => {
-	return (
-			<Layout>
-			<Layout>
-				<LeftSider selectedKeys="article" />
-				<Content style={styleStudioContent}>
-					<Outlet />
-				</Content>
-			</Layout>
-	);
+  return (
+    <Layout>
+      <LeftSider selectedKeys="article" />
+      <Content style={styleStudioContent}>
+        <Outlet />
+      </Content>
+    </Layout>
+  );
 };
 
 export default Widget;

+ 3 - 2
dashboard/src/pages/studio/channel/edit.tsx

@@ -1,18 +1,19 @@
+import { useState } from "react";
+import { useIntl } from "react-intl";
 import { useParams } from "react-router-dom";
 import {
   ProForm,
   ProFormText,
   ProFormTextArea,
 } from "@ant-design/pro-components";
-import { useIntl } from "react-intl";
 import { Card, message, Space } from "antd";
+
 import { IApiResponseChannel } from "../../../components/api/Channel";
 import { get, put } from "../../../request";
 import ChannelTypeSelect from "../../../components/channel/ChannelTypeSelect";
 import LangSelect from "../../../components/general/LangSelect";
 import PublicitySelect from "../../../components/studio/PublicitySelect";
 import GoBack from "../../../components/studio/GoBack";
-import { useState } from "react";
 
 interface IFormData {
   name: string;

+ 3 - 5
dashboard/src/pages/studio/channel/index.tsx

@@ -7,16 +7,14 @@ import { styleStudioContent } from "../style";
 const { Content } = Layout;
 
 const Widget = () => {
-	return (
-		<Layout>
-			<Layout>
+  return (
+    <Layout>
       <LeftSider selectedKeys="channel" />
       <Content style={styleStudioContent}>
         <Outlet />
       </Content>
     </Layout>
-		</Layout>
-	);
+  );
 };
 
 export default Widget;

+ 1 - 3
dashboard/src/pages/studio/dict/index.tsx

@@ -9,14 +9,12 @@ const { Content } = Layout;
 const Widget = () => {
   return (
     <Layout>
-			<Layout>
       <LeftSider selectedKeys="userdict" />
       <Content style={styleStudioContent}>
         <Outlet />
       </Content>
     </Layout>
-		</Layout>
-	);
+  );
 };
 
 export default Widget;

+ 1 - 1
dashboard/src/pages/studio/dict/list.tsx

@@ -1,5 +1,4 @@
 import { useParams } from "react-router-dom";
-import { ProTable } from "@ant-design/pro-components";
 import { useIntl } from "react-intl";
 
 import {
@@ -13,6 +12,7 @@ import {
   Drawer,
 } from "antd";
 import { PlusOutlined, SearchOutlined } from "@ant-design/icons";
+import { ProTable } from "@ant-design/pro-components";
 
 import DictCreate from "../../../components/dict/DictCreate";
 import { IApiResponseDictList } from "../../../components/api/Dict";

+ 2 - 1
dashboard/src/pages/studio/group/edit.tsx

@@ -1,3 +1,4 @@
+import { useIntl } from "react-intl";
 import { useParams } from "react-router-dom";
 import {
 	ProForm,
@@ -5,8 +6,8 @@ import {
 	ProFormSelect,
 	ProFormTextArea,
 } from "@ant-design/pro-components";
-import { useIntl } from "react-intl";
 import { message, Card } from "antd";
+
 import { IGroupResponse } from "../../../components/api/Group";
 import { useEffect, useState } from "react";
 import { get } from "../../../request";

+ 12 - 11
dashboard/src/pages/studio/group/index.tsx

@@ -1,21 +1,22 @@
-import { Layout } from "antd";
 import { Outlet } from "react-router-dom";
+import { Layout } from "antd";
+
 import LeftSider from "../../../components/studio/LeftSider";
 import { styleStudioContent } from "../style";
 
 const { Content } = Layout;
 
 const Widget = () => {
-	return (
-		<Layout>
-			<Layout>
-				<LeftSider selectedKeys="group" />
-				<Content style={styleStudioContent}>
-					<Outlet />
-				</Content>
-			</Layout>
-		</Layout>
-	);
+  return (
+    <Layout>
+      <Layout>
+        <LeftSider selectedKeys="group" />
+        <Content style={styleStudioContent}>
+          <Outlet />
+        </Content>
+      </Layout>
+    </Layout>
+  );
 };
 
 export default Widget;

+ 3 - 4
dashboard/src/pages/studio/group/list.tsx

@@ -1,14 +1,13 @@
 import { useParams, Link } from "react-router-dom";
 import { useIntl } from "react-intl";
-import { ProTable } from "@ant-design/pro-components";
-
 import { Button, Popover, Typography, Dropdown, Menu, MenuProps } from "antd";
+import { ProTable } from "@ant-design/pro-components";
 import { PlusOutlined, SearchOutlined } from "@ant-design/icons";
 
+import { get } from "../../../request";
+import { IGroupListResponse } from "../../../components/api/Group";
 import GroupCreate from "../../../components/group/GroupCreate";
 import { RoleValueEnum } from "../../../components/studio/table";
-import { IGroupListResponse } from "../../../components/api/Group";
-import { get } from "../../../request";
 
 const { Text } = Typography;
 

+ 4 - 3
dashboard/src/pages/studio/group/show.tsx

@@ -1,11 +1,12 @@
+import { useEffect, useState } from "react";
 import { useParams } from "react-router-dom";
 import { Button, Card } from "antd";
 import { Col, Row } from "antd";
-import GroupFile from "../../../components/group/GroupFile";
-import GroupMember from "../../../components/group/GroupMember";
-import { useEffect, useState } from "react";
+
 import { get } from "../../../request";
 import { IGroupResponse } from "../../../components/api/Group";
+import GroupFile from "../../../components/group/GroupFile";
+import GroupMember from "../../../components/group/GroupMember";
 import GoBack from "../../../components/studio/GoBack";
 
 const Widget = () => {

+ 174 - 188
dashboard/src/pages/studio/recent/index.tsx

@@ -1,215 +1,201 @@
 import { useParams } from "react-router-dom";
-import { ProTable } from "@ant-design/pro-components";
 import { useIntl } from "react-intl";
 import { Link } from "react-router-dom";
 import { Layout, Space, Table } from "antd";
-
 import type { MenuProps } from "antd";
 import { Button, Dropdown, Menu } from "antd";
 import { SearchOutlined } from "@ant-design/icons";
+import { ProTable } from "@ant-design/pro-components";
 
 import LeftSider from "../../../components/studio/LeftSider";
 
 const { Content } = Layout;
 
 const onMenuClick: MenuProps["onClick"] = (e) => {
-	console.log("click", e);
+  console.log("click", e);
 };
 
 const menu = (
-	<Menu
-		onClick={onMenuClick}
-		items={[
-			{
-				key: "1",
-				label: "在藏经阁中打开",
-				icon: <SearchOutlined />,
-			},
-			{
-				key: "2",
-				label: "分享",
-				icon: <SearchOutlined />,
-			},
-			{
-				key: "3",
-				label: "删除",
-				icon: <SearchOutlined />,
-			},
-		]}
-	/>
+  <Menu
+    onClick={onMenuClick}
+    items={[
+      {
+        key: "1",
+        label: "在藏经阁中打开",
+        icon: <SearchOutlined />,
+      },
+      {
+        key: "2",
+        label: "分享",
+        icon: <SearchOutlined />,
+      },
+      {
+        key: "3",
+        label: "删除",
+        icon: <SearchOutlined />,
+      },
+    ]}
+  />
 );
 
 interface IItem {
-	id: number;
-	title: string;
-	subtitle: string;
-	publicity: number;
-	createdAt: number;
+  id: number;
+  title: string;
+  subtitle: string;
+  publicity: number;
+  createdAt: number;
 }
 
 const Widget = () => {
-	const intl = useIntl();
-	const { studioname } = useParams();
-	return (
-		<Layout>
-			<LeftSider selectedKeys="recent" />
-			<Content>
-				<Layout>{studioname}</Layout>
-				<ProTable<IItem>
-					columns={[
-						{
-							title: intl.formatMessage({
-								id: "dict.fields.sn.label",
-							}),
-							dataIndex: "id",
-							key: "id",
-							width: 50,
-							search: false,
-						},
-						{
-							title: intl.formatMessage({
-								id: "forms.fields.title.label",
-							}),
-							dataIndex: "title",
-							key: "title",
-							tip: "过长会自动收缩",
-							ellipsis: true,
-							render: (text, row, index, action) => {
-								return (
-									<div>
-										<div>
-											<Link to="edit/12345">
-												{row.title}
-											</Link>
-										</div>
-										<div>{row.subtitle}</div>
-									</div>
-								);
-							},
-						},
-						{
-							title: intl.formatMessage({
-								id: "forms.fields.publicity.label",
-							}),
-							dataIndex: "publicity",
-							key: "publicity",
-							width: 100,
-							search: false,
-							filters: true,
-							onFilter: true,
-							valueEnum: {
-								all: { text: "全部", status: "Default" },
-								0: { text: "禁用", status: "Success" },
-								10: { text: "私有", status: "Processing" },
-								20: { text: "链接阅读", status: "Default" },
-								30: { text: "公开阅读", status: "Default" },
-								40: { text: "公开可编辑", status: "Default" },
-							},
-						},
-						{
-							title: intl.formatMessage({
-								id: "forms.fields.created-at.label",
-							}),
-							key: "created-at",
-							width: 100,
-							search: false,
-							dataIndex: "createdAt",
-							valueType: "date",
-							sorter: (a, b) => a.createdAt - b.createdAt,
-						},
-						{
-							title: intl.formatMessage({ id: "buttons.option" }),
-							key: "option",
-							width: 120,
-							valueType: "option",
-							render: (text, row, index, action) => [
-								<Dropdown.Button
-									type="link"
-									key={index}
-									overlay={menu}
-								>
-									{intl.formatMessage({ id: "buttons.edit" })}
-								</Dropdown.Button>,
-							],
-						},
-					]}
-					rowSelection={{
-						// 自定义选择项参考: https://ant.design/components/table-cn/#components-table-demo-row-selection-custom
-						// 注释该行则默认不显示下拉选项
-						selections: [
-							Table.SELECTION_ALL,
-							Table.SELECTION_INVERT,
-						],
-					}}
-					tableAlertRender={({
-						selectedRowKeys,
-						selectedRows,
-						onCleanSelected,
-					}) => (
-						<Space size={24}>
-							<span>
-								{intl.formatMessage({ id: "buttons.selected" })}
-								{selectedRowKeys.length}
-								<Button
-									type="link"
-									style={{ marginInlineStart: 8 }}
-									onClick={onCleanSelected}
-								>
-									{intl.formatMessage({
-										id: "buttons.unselect",
-									})}
-								</Button>
-							</span>
-						</Space>
-					)}
-					tableAlertOptionRender={() => {
-						return (
-							<Space size={16}>
-								<Button type="link">批量删除</Button>
-							</Space>
-						);
-					}}
-					request={async (params = {}, sorter, filter) => {
-						// TODO
-						console.log(params, sorter, filter);
+  const intl = useIntl();
+  const { studioname } = useParams();
+  return (
+    <Layout>
+      <LeftSider selectedKeys="recent" />
+      <Content>
+        <Layout>{studioname}</Layout>
+        <ProTable<IItem>
+          columns={[
+            {
+              title: intl.formatMessage({
+                id: "dict.fields.sn.label",
+              }),
+              dataIndex: "id",
+              key: "id",
+              width: 50,
+              search: false,
+            },
+            {
+              title: intl.formatMessage({
+                id: "forms.fields.title.label",
+              }),
+              dataIndex: "title",
+              key: "title",
+              tip: "过长会自动收缩",
+              ellipsis: true,
+              render: (text, row, index, action) => {
+                return (
+                  <div>
+                    <div>
+                      <Link to="edit/12345">{row.title}</Link>
+                    </div>
+                    <div>{row.subtitle}</div>
+                  </div>
+                );
+              },
+            },
+            {
+              title: intl.formatMessage({
+                id: "forms.fields.publicity.label",
+              }),
+              dataIndex: "publicity",
+              key: "publicity",
+              width: 100,
+              search: false,
+              filters: true,
+              onFilter: true,
+              valueEnum: {
+                all: { text: "全部", status: "Default" },
+                0: { text: "禁用", status: "Success" },
+                10: { text: "私有", status: "Processing" },
+                20: { text: "链接阅读", status: "Default" },
+                30: { text: "公开阅读", status: "Default" },
+                40: { text: "公开可编辑", status: "Default" },
+              },
+            },
+            {
+              title: intl.formatMessage({
+                id: "forms.fields.created-at.label",
+              }),
+              key: "created-at",
+              width: 100,
+              search: false,
+              dataIndex: "createdAt",
+              valueType: "date",
+              sorter: (a, b) => a.createdAt - b.createdAt,
+            },
+            {
+              title: intl.formatMessage({ id: "buttons.option" }),
+              key: "option",
+              width: 120,
+              valueType: "option",
+              render: (text, row, index, action) => [
+                <Dropdown.Button type="link" key={index} overlay={menu}>
+                  {intl.formatMessage({ id: "buttons.edit" })}
+                </Dropdown.Button>,
+              ],
+            },
+          ]}
+          rowSelection={{
+            // 自定义选择项参考: https://ant.design/components/table-cn/#components-table-demo-row-selection-custom
+            // 注释该行则默认不显示下拉选项
+            selections: [Table.SELECTION_ALL, Table.SELECTION_INVERT],
+          }}
+          tableAlertRender={({
+            selectedRowKeys,
+            selectedRows,
+            onCleanSelected,
+          }) => (
+            <Space size={24}>
+              <span>
+                {intl.formatMessage({ id: "buttons.selected" })}
+                {selectedRowKeys.length}
+                <Button
+                  type="link"
+                  style={{ marginInlineStart: 8 }}
+                  onClick={onCleanSelected}
+                >
+                  {intl.formatMessage({
+                    id: "buttons.unselect",
+                  })}
+                </Button>
+              </span>
+            </Space>
+          )}
+          tableAlertOptionRender={() => {
+            return (
+              <Space size={16}>
+                <Button type="link">批量删除</Button>
+              </Space>
+            );
+          }}
+          request={async (params = {}, sorter, filter) => {
+            // TODO
+            console.log(params, sorter, filter);
 
-						const size = params.pageSize || 20;
-						return {
-							total: 1 << 12,
-							success: true,
-							data: Array.from(Array(size).keys()).map((x) => {
-								const id =
-									((params.current || 1) - 1) * size + x + 1;
+            const size = params.pageSize || 20;
+            return {
+              total: 1 << 12,
+              success: true,
+              data: Array.from(Array(size).keys()).map((x) => {
+                const id = ((params.current || 1) - 1) * size + x + 1;
 
-								var it: IItem = {
-									id,
-									title: `title ${id}`,
-									subtitle: `subtitle ${id}`,
-									publicity:
-										(Math.floor(Math.random() * 3) + 1) *
-										10,
-									createdAt:
-										Date.now() -
-										Math.floor(Math.random() * 2000000000),
-								};
-								return it;
-							}),
-						};
-					}}
-					rowKey="id"
-					bordered
-					pagination={{
-						showQuickJumper: true,
-						showSizeChanger: true,
-					}}
-					search={false}
-					options={{
-						search: true,
-					}}
-				/>
-			</Content>
-		</Layout>
-	);
+                var it: IItem = {
+                  id,
+                  title: `title ${id}`,
+                  subtitle: `subtitle ${id}`,
+                  publicity: (Math.floor(Math.random() * 3) + 1) * 10,
+                  createdAt:
+                    Date.now() - Math.floor(Math.random() * 2000000000),
+                };
+                return it;
+              }),
+            };
+          }}
+          rowKey="id"
+          bordered
+          pagination={{
+            showQuickJumper: true,
+            showSizeChanger: true,
+          }}
+          search={false}
+          options={{
+            search: true,
+          }}
+        />
+      </Content>
+    </Layout>
+  );
 };
 
 export default Widget;

+ 1 - 1
dashboard/src/pages/studio/term/list.tsx

@@ -8,9 +8,9 @@ import {
   ShareAltOutlined,
 } from "@ant-design/icons";
 
-import TermCreate from "../../../components/term/TermCreate";
 import { ITermListResponse } from "../../../components/api/Term";
 import { get } from "../../../request";
+import TermCreate from "../../../components/term/TermCreate";
 
 const onMenuClick: MenuProps["onClick"] = (e) => {
   console.log("click", e);