فهرست منبع

Merge pull request #1023 from visuddhinanda/agile

format code
visuddhinanda 3 سال پیش
والد
کامیت
d1c398cf4e

+ 28 - 12
dashboard/src/pages/studio/article/edit.tsx

@@ -19,14 +19,17 @@ interface IFormData {
 
 const Widget = () => {
 	const intl = useIntl();
-	const { studioname, articleid } = useParams();//url 参数
+	const { studioname, articleid } = useParams(); //url 参数
 	return (
 		<Layout>
 			<HeadBar />
 			<Layout>
 				<LeftSider selectedKeys="article" />
 				<Content>
-					<h2>studio/{studioname}/{intl.formatMessage({ id: "columns.studio.article.title" })}/edit/{articleid}</h2>
+					<h2>
+						studio/{studioname}/{intl.formatMessage({ id: "columns.studio.article.title" })}/edit/
+						{articleid}
+					</h2>
 
 					<ProForm<IFormData>
 						onFinish={async (values: IFormData) => {
@@ -42,7 +45,12 @@ const Widget = () => {
 								name="title"
 								required
 								label={intl.formatMessage({ id: "forms.fields.title.label" })}
-								rules={[{ required: true, message: intl.formatMessage({ id: "forms.create.message.no.title" }) }]}
+								rules={[
+									{
+										required: true,
+										message: intl.formatMessage({ id: "forms.create.message.no.title" }),
+									},
+								]}
 							/>
 						</ProForm.Group>
 						<ProForm.Group>
@@ -53,33 +61,41 @@ const Widget = () => {
 							/>
 						</ProForm.Group>
 						<ProForm.Group>
-							<ProFormTextArea name="summary" label={intl.formatMessage({ id: "forms.fields.summary.label" })} />
+							<ProFormTextArea
+								name="summary"
+								label={intl.formatMessage({ id: "forms.fields.summary.label" })}
+							/>
 						</ProForm.Group>
 						<ProForm.Group>
 							<ProFormSelect
 								options={[
-									{ value: 'zh-Hans', label: "简体中文", },
-									{ value: 'zh-Hant', label: "繁体中文", },
-									{ value: 'en-US', label: "English", },
+									{ value: "zh-Hans", label: "简体中文" },
+									{ value: "zh-Hant", label: "繁体中文" },
+									{ value: "en-US", label: "English" },
 								]}
 								width="md"
 								name="lang"
-								rules={[{ required: true, message: intl.formatMessage({ id: "forms.create.message.no.lang" }) }]}
+								rules={[
+									{
+										required: true,
+										message: intl.formatMessage({ id: "forms.create.message.no.lang" }),
+									},
+								]}
 								label={intl.formatMessage({ id: "channel.lang" })}
 							/>
 						</ProForm.Group>
 
 						<ProForm.Group>
-							<ProFormTextArea name="content" label={intl.formatMessage({ id: "forms.fields.content.label" })} />
+							<ProFormTextArea
+								name="content"
+								label={intl.formatMessage({ id: "forms.fields.content.label" })}
+							/>
 						</ProForm.Group>
-
 					</ProForm>
-
 				</Content>
 			</Layout>
 			<Footer />
 		</Layout>
-
 	);
 };
 

+ 118 - 136
dashboard/src/pages/studio/article/index.tsx

@@ -1,165 +1,147 @@
-import { useParams ,Link} from "react-router-dom";
+import { useParams, Link } from "react-router-dom";
 import { useIntl } from "react-intl";
-import { useState } from 'react';
+import { useState } from "react";
 
-import { Space,Layout,Breadcrumb,Button,Tag } from "antd";
-import { ProList } from '@ant-design/pro-components';
-import {
-	CheckCircleOutlined,
-  } from '@ant-design/icons';
+import { Space, Layout, Breadcrumb, Button, Tag } from "antd";
+import { ProList } from "@ant-design/pro-components";
+import { CheckCircleOutlined } from "@ant-design/icons";
 
 import HeadBar from "../../../components/studio/HeadBar";
 import LeftSider from "../../../components/studio/LeftSider";
 import Footer from "../../../components/studio/Footer";
-const {  Content } = Layout;
+const { Content } = Layout;
 
 const defaultData = [
 	{
-	  id: '1',
-	  title:'Nissaya Book List',
-	  name: 'Nissaya Book List',
-	  tag:[{title:"公开",color:"success",icon:<CheckCircleOutlined />}],
-	  description: '文章概要',
+		id: "1",
+		title: "Nissaya Book List",
+		name: "Nissaya Book List",
+		tag: [{ title: "公开", color: "success", icon: <CheckCircleOutlined /> }],
+		description: "文章概要",
 	},
 	{
-	  id: '2',
-	  title:"初级巴利语入门",
-	  name: '初级巴利语入门',
-	  tag:[{title:"公开",color:"success",icon:<CheckCircleOutlined />}],
-		description: '文章概要',
+		id: "2",
+		title: "初级巴利语入门",
+		name: "初级巴利语入门",
+		tag: [{ title: "公开", color: "success", icon: <CheckCircleOutlined /> }],
+		description: "文章概要",
 	},
 	{
-	  id: '3',
-	  title: '何人有资格接受卡提那衣',
-	  name: '何人有资格接受卡提那衣',
-	  tag:[{title:"公开",color:"success",icon:<CheckCircleOutlined />}],
-		description: '文章概要',
+		id: "3",
+		title: "何人有资格接受卡提那衣",
+		name: "何人有资格接受卡提那衣",
+		tag: [{ title: "公开", color: "success", icon: <CheckCircleOutlined /> }],
+		description: "文章概要",
 	},
 	{
-	  id: '4',
-	  title: 'Adhiṭṭhana 定名/决意',
-	  name: 'Adhiṭṭhana 定名/决意',
-	  tag:[{title:"公开",color:"success",icon:<CheckCircleOutlined />}],
-		description: '文章概要',
+		id: "4",
+		title: "Adhiṭṭhana 定名/决意",
+		name: "Adhiṭṭhana 定名/决意",
+		tag: [{ title: "公开", color: "success", icon: <CheckCircleOutlined /> }],
+		description: "文章概要",
 	},
-  ];
-  type DataItem = typeof defaultData[number];
+];
+type DataItem = typeof defaultData[number];
 
 const Widget = () => {
-	const intl = useIntl();//i18n
-	const { studioname } = useParams();//url 参数
+	const intl = useIntl(); //i18n
+	const { studioname } = useParams(); //url 参数
 	const [dataSource, setDataSource] = useState<DataItem[]>(defaultData);
 
 	const linkRead = `/article/show/12345`;
 	const linkStudio = `/studio/${studioname}`;
-  return (
-	<Layout>
-		<HeadBar/>
+
+	return (
 		<Layout>
-			<LeftSider selectedKeys="article"/>
-			<Content>
-			<Breadcrumb>
-					<Breadcrumb.Item>
-						<Link to={linkStudio}>{intl.formatMessage({ id: "columns.studio.title" })}</Link>
-					</Breadcrumb.Item>
-					<Breadcrumb.Item>
-						{intl.formatMessage({ id: "columns.studio.collaboration.title" })}
-					</Breadcrumb.Item>
-					<Breadcrumb.Item >
-						{intl.formatMessage({ id: "columns.studio.article.title" })}
-					</Breadcrumb.Item>
-				</Breadcrumb>
-				<Layout>
-					<ProList<DataItem>
-					rowKey="id"
-					headerTitle="文章"
-					dataSource={dataSource}
-					showActions="hover"
-					editable={{
-						onSave: async (key, record, originRow) => {
-						console.log(key, record, originRow);
-						return true;
-						},
-					}}
-					onDataSourceChange={setDataSource}
-					metas={{
-						title: {
-						dataIndex: 'name',
-						render: (text, row, index, action) => {
-							const linkEdit = `/studio/${studioname}/article/edit/${row.id}`;
+			<HeadBar />
+			<Layout>
+				<LeftSider selectedKeys="article" />
+				<Content>
+					<Breadcrumb>
+						<Breadcrumb.Item>
+							<Link to={linkStudio}>{intl.formatMessage({ id: "columns.studio.title" })}</Link>
+						</Breadcrumb.Item>
+						<Breadcrumb.Item>
+							{intl.formatMessage({ id: "columns.studio.collaboration.title" })}
+						</Breadcrumb.Item>
+						<Breadcrumb.Item>{intl.formatMessage({ id: "columns.studio.article.title" })}</Breadcrumb.Item>
+					</Breadcrumb>
+					<Layout>
+						<ProList<DataItem>
+							rowKey="id"
+							headerTitle="文章"
+							dataSource={dataSource}
+							showActions="hover"
+							editable={{
+								onSave: async (key, record, originRow) => {
+									console.log(key, record, originRow);
+									return true;
+								},
+							}}
+							onDataSourceChange={setDataSource}
+							metas={{
+								title: {
+									dataIndex: "name",
+									render: (text, row, index, action) => {
+										const linkEdit = `/studio/${studioname}/article/edit/${row.id}`;
 
-							return (
-								<Link to ={linkEdit}>{row.name}</Link>
-							);
-						},
-						},
-						description: {
-						  dataIndex: 'description',
-						  search:false,
-						},
-						content: {
-							dataIndex: 'content',
-							editable: false,
-							search:false,
-						},
-						subTitle: {
-							search:false,
-						render: (text, row, index, action) => {
-							const showtag = row.tag.map((item,key) => {
-								return <Tag color={item.color} icon={item.icon}>{item.title}</Tag>
-							});
-							return (
-							<Space size={0}>
-								{showtag}
-							</Space>
-							);
-						},
-						},
-						actions: {
-						render: (text, row, index, action) => {
-							const linkEdit = `/studio/${studioname}/article/edit/${row.id}`;
+										return <Link to={linkEdit}>{row.name}</Link>;
+									},
+								},
+								description: {
+									dataIndex: "description",
+									search: false,
+								},
+								content: {
+									dataIndex: "content",
+									editable: false,
+									search: false,
+								},
+								subTitle: {
+									search: false,
+									render: (text, row, index, action) => {
+										const showtag = row.tag.map((item, key) => {
+											return (
+												<Tag color={item.color} icon={item.icon}>
+													{item.title}
+												</Tag>
+											);
+										});
+										return <Space size={0}>{showtag}</Space>;
+									},
+								},
+								actions: {
+									render: (text, row, index, action) => {
+										const linkEdit = `/studio/${studioname}/article/edit/${row.id}`;
 
-							return (
-								[
-									<Link to = {linkEdit}>编辑</Link>,
-									<Link to = {linkRead}>阅读</Link>,
-									<Button
-									onClick={() => {
-										
-									}}
-									key="link" danger
-									>
-									删除
-									</Button>,
-									<Button
-									onClick={() => {
-										
-									}}
-									key="link"
-									>
-									分享
-									</Button>,
-								]
-							);
-						},
-						},
-						
-					}}
-					search={{
-						filterType: 'light',
-					  }}
-					  bordered
-					  pagination={{
-						  showQuickJumper: true,
-						  showSizeChanger: true,
-					  }}
-					/>
-				</Layout>
-			</Content>
+										return [
+											<Link to={linkEdit}>编辑</Link>,
+											<Link to={linkRead}>阅读</Link>,
+											<Button onClick={() => {}} key="link" danger>
+												删除
+											</Button>,
+											<Button onClick={() => {}} key="link">
+												分享
+											</Button>,
+										];
+									},
+								},
+							}}
+							search={{
+								filterType: "light",
+							}}
+							bordered
+							pagination={{
+								showQuickJumper: true,
+								showSizeChanger: true,
+							}}
+						/>
+					</Layout>
+				</Content>
+			</Layout>
+			<Footer />
 		</Layout>
-		<Footer/>
-	</Layout>
-  );
+	);
 };
 
 export default Widget;

+ 60 - 46
dashboard/src/pages/studio/channel/create.tsx

@@ -1,60 +1,74 @@
 import { useParams } from "react-router-dom";
-import { ProForm, ProFormText , ProFormSelect } from "@ant-design/pro-components";
+import { ProForm, ProFormText, ProFormSelect } from "@ant-design/pro-components";
 import { useIntl } from "react-intl";
 import { Link } from "react-router-dom";
-import { Space,message } from "antd";
+import { Space, message } from "antd";
 interface IFormData {
 	name: string;
 	type: string;
-  }
+}
 const Widget = () => {
 	const intl = useIntl();
 	const { studioname } = useParams();
-  return (
-    <div>
-      <div>studio/{studioname}/{intl.formatMessage({ id: "title.channel" })}/create</div>
-      <div>
-        <div>
-          <Space>
-            <Link to="/">Home</Link>
-            <Link to="/community/myread">{studioname}</Link>
-          </Space>
-        </div>
-		
+
+	return (
 		<div>
-		<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.Group>
-			<ProFormSelect
-				options={[
-					{value: 'translation', label: intl.formatMessage({ id: "channel.type.translation.title" }), },
-					{value: 'nissaya', label: intl.formatMessage({ id: "channel.type.nissaya.title" }), },
-				]}
-				width="md"
-				name="type"
-				label={intl.formatMessage({ id: "channel.type" })}
-				/>
-		</ProForm.Group>
-		</ProForm>
+			<div>
+				studio/{studioname}/{intl.formatMessage({ id: "title.channel" })}/create
+			</div>
+			<div>
+				<div>
+					<Space>
+						<Link to="/">Home</Link>
+						<Link to="/community/myread">{studioname}</Link>
+					</Space>
+				</div>
+
+				<div>
+					<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.Group>
+							<ProFormSelect
+								options={[
+									{
+										value: "translation",
+										label: intl.formatMessage({ id: "channel.type.translation.title" }),
+									},
+									{
+										value: "nissaya",
+										label: intl.formatMessage({ id: "channel.type.nissaya.title" }),
+									},
+								]}
+								width="md"
+								name="type"
+								label={intl.formatMessage({ id: "channel.type" })}
+							/>
+						</ProForm.Group>
+					</ProForm>
+				</div>
+			</div>
+			<div>底部区域</div>
 		</div>
-      </div>
-      <div>底部区域</div>
-    </div>
-  );
+	);
 };
 
 export default Widget;

+ 85 - 64
dashboard/src/pages/studio/channel/edit.tsx

@@ -1,12 +1,12 @@
 import { useParams } from "react-router-dom";
-import { ProForm, ProFormText , ProFormSelect,ProFormTextArea } from "@ant-design/pro-components";
+import { ProForm, ProFormText, ProFormSelect, ProFormTextArea } from "@ant-design/pro-components";
 import { useIntl } from "react-intl";
-import { message,Layout,Space } from "antd";
+import { message, Layout, Space } from "antd";
 import HeadBar from "../../../components/studio/HeadBar";
 import LeftSider from "../../../components/studio/LeftSider";
 import Footer from "../../../components/studio/Footer";
 
-const {  Content } = Layout;
+const { Content } = Layout;
 
 interface IFormData {
 	name: string;
@@ -14,71 +14,92 @@ interface IFormData {
 	lang: string;
 	summary: string;
 	studio: string;
-  }
+}
 const Widget = () => {
 	const intl = useIntl();
-	const { channelid } = useParams();//url 参数
-  return (
-	<Layout>
-	<HeadBar/>
-	<Layout>
-		<LeftSider selectedKeys="channel"/>
-		<Content>
-		<Space>{channelid}</Space>
-		<ProForm<IFormData>
-		onFinish={async (values: IFormData) => {
-			// TODO
-			values.studio = "aaaa";
-			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.Group>
-			<ProFormSelect
-				options={[
-					{value: 'translation', label: intl.formatMessage({ id: "channel.type.translation.title" }), },
-					{value: 'nissaya', label: intl.formatMessage({ id: "channel.type.nissaya.title" }), },
-				]}
-				width="md"
-				name="type"
-				rules={[{ required: true, message: intl.formatMessage({ id: "channel.create.message.noname" }) }]}
-				label={intl.formatMessage({ id: "channel.type" })}
-				/>
-		</ProForm.Group>
-		<ProForm.Group>
-			<ProFormSelect
-				options={[
-					{value: 'zh-Hans', label: "简体中文", },
-					{value: 'zh-Hant', label: "繁体中文", },
-					{value: 'en-US', label: "English", },
-				]}
-				width="md"
-				name="lang"
-				rules={[{ required: true, message: intl.formatMessage({ id: "channel.create.message.noname" }) }]}
-				label={intl.formatMessage({ id: "channel.lang" })}
-				/>
-		</ProForm.Group>
+	const { channelid } = useParams(); //url 参数
 
-		<ProForm.Group>
-			<ProFormTextArea  name="summary" label="简介" />
-		</ProForm.Group>
+	return (
+		<Layout>
+			<HeadBar />
+			<Layout>
+				<LeftSider selectedKeys="channel" />
+				<Content>
+					<Space>{channelid}</Space>
+					<ProForm<IFormData>
+						onFinish={async (values: IFormData) => {
+							// TODO
+							values.studio = "aaaa";
+							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>
-	  </Content>
-	</Layout>
-	<Footer/>
-</Layout>
-  );
+						<ProForm.Group>
+							<ProFormSelect
+								options={[
+									{
+										value: "translation",
+										label: intl.formatMessage({ id: "channel.type.translation.title" }),
+									},
+									{
+										value: "nissaya",
+										label: intl.formatMessage({ id: "channel.type.nissaya.title" }),
+									},
+								]}
+								width="md"
+								name="type"
+								rules={[
+									{
+										required: true,
+										message: intl.formatMessage({ id: "channel.create.message.noname" }),
+									},
+								]}
+								label={intl.formatMessage({ id: "channel.type" })}
+							/>
+						</ProForm.Group>
+						<ProForm.Group>
+							<ProFormSelect
+								options={[
+									{ value: "zh-Hans", label: "简体中文" },
+									{ value: "zh-Hant", label: "繁体中文" },
+									{ value: "en-US", label: "English" },
+								]}
+								width="md"
+								name="lang"
+								rules={[
+									{
+										required: true,
+										message: intl.formatMessage({ id: "channel.create.message.noname" }),
+									},
+								]}
+								label={intl.formatMessage({ id: "channel.lang" })}
+							/>
+						</ProForm.Group>
+
+						<ProForm.Group>
+							<ProFormTextArea name="summary" label="简介" />
+						</ProForm.Group>
+					</ProForm>
+				</Content>
+			</Layout>
+			<Footer />
+		</Layout>
+	);
 };
 
 export default Widget;

+ 24 - 23
dashboard/src/pages/studio/channel/index.tsx

@@ -1,34 +1,35 @@
-import { useParams ,Link} from "react-router-dom";
+import { useParams, Link } from "react-router-dom";
 import { useIntl } from "react-intl";
-import { Space,Layout } from "antd";
+import { Space, Layout } from "antd";
 import HeadBar from "../../../components/studio/HeadBar";
 import LeftSider from "../../../components/studio/LeftSider";
 import Footer from "../../../components/studio/Footer";
 
-const {  Content } = Layout;
+const { Content } = Layout;
 
 const Widget = () => {
-	const intl = useIntl();//i18n
-	const { studioname } = useParams();//url 参数
+	const intl = useIntl(); //i18n
+	const { studioname } = useParams(); //url 参数
 	const linkEdit = `/studio/${studioname}/channel/edit/12345`;
-  return (
-	<Layout>
-	<HeadBar/>
-	<Layout>
-		<LeftSider selectedKeys="channel"/>
-		<Content>
-		<h2>studio/{studioname}/{intl.formatMessage({ id: "columns.studio.channel.title" })}/版本列表</h2>
-		<div>
-			<Space>
-				<Link to={linkEdit}> channel1 edit </Link>
-			</Space>
-		</div>		
-	  </Content>
-	</Layout>
-	<Footer/>
-</Layout>
-
-  );
+	return (
+		<Layout>
+			<HeadBar />
+			<Layout>
+				<LeftSider selectedKeys="channel" />
+				<Content>
+					<h2>
+						studio/{studioname}/{intl.formatMessage({ id: "columns.studio.channel.title" })}/版本列表
+					</h2>
+					<div>
+						<Space>
+							<Link to={linkEdit}> channel1 edit </Link>
+						</Space>
+					</div>
+				</Content>
+			</Layout>
+			<Footer />
+		</Layout>
+	);
 };
 
 export default Widget;

+ 173 - 175
dashboard/src/pages/studio/dict/index.tsx

@@ -2,196 +2,194 @@ 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 { Button,Layout,Space, Table  } from "antd";
-import {  PlusOutlined } from '@ant-design/icons';
+import { Button, Layout, Space, Table } from "antd";
+import { PlusOutlined } from "@ant-design/icons";
 
 import HeadBar from "../../../components/studio/HeadBar";
 import LeftSider from "../../../components/studio/LeftSider";
 import Footer from "../../../components/studio/Footer";
 
-const {  Content } = Layout;
+const { Content } = Layout;
 
 interface IItem {
-  id: number;
-  word: string;
-  type: string;
-  grammar: string;
-  parent: string;
-  meaning: string;  
-  note: string;
-  factors: string;
-  createdAt: number;
+	id: number;
+	word: string;
+	type: string;
+	grammar: string;
+	parent: string;
+	meaning: string;
+	note: string;
+	factors: string;
+	createdAt: number;
 }
 
 const valueEnum = {
-	0: 'n',
-	1: 'ti',
-	2: 'v',
-	3: 'ind',
-  };
-
+	0: "n",
+	1: "ti",
+	2: "v",
+	3: "ind",
+};
 
 const Widget = () => {
-  const intl = useIntl();
-  const { studioname } = useParams();
-  return (
-	<Layout>
-	<HeadBar/>
-	<Layout>
-		<LeftSider selectedKeys="userdict"/>
-		<Content>
+	const intl = useIntl();
+	const { studioname } = useParams();
+	return (
+		<Layout>
+			<HeadBar />
+			<Layout>
+				<LeftSider selectedKeys="userdict" />
+				<Content>
+					<Layout>{studioname}</Layout>
+					<ProTable<IItem>
+						columns={[
+							{
+								title: intl.formatMessage({ id: "dict.fields.sn.label" }),
+								dataIndex: "id",
+								key: "id",
+								width: 80,
+								search: false,
+							},
+							{
+								title: intl.formatMessage({ id: "dict.fields.word.label" }),
+								dataIndex: "word",
+								key: "word",
+								render: (_) => <Link to="">{_}</Link>,
+								tip: "单词过长会自动收缩",
+								ellipsis: true,
+								formItemProps: {
+									rules: [
+										{
+											required: true,
+											message: "此项为必填项",
+										},
+									],
+								},
+							},
+							{
+								title: intl.formatMessage({ id: "dict.fields.type.label" }),
+								dataIndex: "type",
+								key: "type",
+								search: false,
+								filters: true,
+								onFilter: true,
+								valueEnum: {
+									all: { text: "全部", status: "Default" },
+									n: { text: "名词", status: "Default" },
+									ti: { text: "三性", status: "Processing" },
+									v: { text: "动词", status: "Success" },
+									ind: { text: "不变词", status: "Success" },
+								},
+							},
+							{
+								title: intl.formatMessage({ id: "dict.fields.grammar.label" }),
+								dataIndex: "grammar",
+								key: "grammar",
+								search: false,
+							},
+							{
+								title: intl.formatMessage({ id: "dict.fields.parent.label" }),
+								dataIndex: "parent",
+								key: "parent",
+							},
+							{
+								title: intl.formatMessage({ id: "dict.fields.meaning.label" }),
+								dataIndex: "meaning",
+								key: "meaning",
+								tip: "意思过长会自动收缩",
+								ellipsis: true,
+							},
+							{
+								title: intl.formatMessage({ id: "dict.fields.note.label" }),
+								dataIndex: "note",
+								key: "note",
+								search: false,
+								tip: "注释过长会自动收缩",
+								ellipsis: true,
+							},
+							{
+								title: intl.formatMessage({ id: "dict.fields.factors.label" }),
+								dataIndex: "factors",
+								key: "factors",
+								search: false,
+							},
+							{
+								title: intl.formatMessage({ id: "forms.fields.created-at.label" }),
+								key: "created-at",
+								width: 200,
 
-		<Layout>{studioname}</Layout>
-		<ProTable<IItem>
-		columns={[
-			{
-			title: intl.formatMessage({ id: "dict.fields.sn.label" }),
-			dataIndex: "id",
-			key: "id",
-			width: 80,
-			search: false,
-			},
-			{
-			title: intl.formatMessage({ id: "dict.fields.word.label" }),
-			dataIndex: "word",
-			key: "word",
-			render: (_) => <Link to="">{_}</Link>,
-			tip: '单词过长会自动收缩',
-			ellipsis: true,
-			formItemProps: {
-				rules: [
-				{
-					required: true,
-					message: '此项为必填项',
-				},
-				],
-			},
-			},
-			{
-				title: intl.formatMessage({ id: "dict.fields.type.label" }),
-				dataIndex: "type",
-				key: "type",
-				search: false,
-				filters: true,
-				onFilter: true,
-				valueEnum: {
-				all: { text: '全部', status: 'Default' },
-				n: { text: '名词', status: 'Default' },
-				ti: { text: '三性', status: 'Processing' },
-				v: { text: '动词', status: 'Success' },
-				ind: { text: '不变词', status: 'Success' },
-				},
-			},
-			{
-				title: intl.formatMessage({ id: "dict.fields.grammar.label" }),
-				dataIndex: "grammar",
-				key: "grammar",
-				search: false,
-			},
-			{
-				title: intl.formatMessage({ id: "dict.fields.parent.label" }),
-				dataIndex: "parent",
-				key: "parent",
-			},
-			{
-				title: intl.formatMessage({ id: "dict.fields.meaning.label" }),
-				dataIndex: "meaning",
-				key: "meaning",
-				tip: '意思过长会自动收缩',
-				ellipsis: true,
-			},
-			{
-				title: intl.formatMessage({ id: "dict.fields.note.label" }),
-				dataIndex: "note",
-				key: "note",
-				search: false,
-				tip: '注释过长会自动收缩',
-				ellipsis: true,
-			},
-			{
-				title: intl.formatMessage({ id: "dict.fields.factors.label" }),
-				dataIndex: "factors",
-				key: "factors",
-				search: false,
-			},
-			{
-			title: intl.formatMessage({ id: "forms.fields.created-at.label" }),
-			key: "created-at",
-			width: 200,
+								search: false,
+								dataIndex: "createdAt",
+								valueType: "date",
+								sorter: (a, b) => a.createdAt - b.createdAt,
+							},
+						]}
+						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>
+									已选 {selectedRowKeys.length} 项
+									<Button type="link" style={{ marginInlineStart: 8 }} onClick={onCleanSelected}>
+										取消选择
+									</Button>
+								</span>
+							</Space>
+						)}
+						tableAlertOptionRender={() => {
+							return (
+								<Space size={16}>
+									<Button type="link">批量删除</Button>
+									<Button type="link">导出数据</Button>
+								</Space>
+							);
+						}}
+						request={async (params = {}, sorter, filter) => {
+							// TODO
+							console.log(params, sorter, filter);
 
-			search: false,
-			dataIndex: 'createdAt',
-			valueType: 'date',
-			sorter: (a, b) => a.createdAt - b.createdAt,
-			},
-		]}
-		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>
-				已选 {selectedRowKeys.length} 项
-				<Button type="link" style={{ marginInlineStart: 8 }} onClick={onCleanSelected}>
-				取消选择
-				</Button>
-			</span>
-			</Space>
-		)}
-		tableAlertOptionRender={() => {
-			return (
-			<Space size={16}>
-				<Button type="link">批量删除</Button>
-				<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,
-				word: `word ${id}`,
-				type: valueEnum[2],
-				grammar: "阳-单-属",
-				parent: `parent ${id}`,
-				meaning: `meaning ${id}`,
-				note: `note ${id}`,
-				factors: `factors ${id}`,
-				createdAt: Date.now() - Math.floor(Math.random() * 200000),
-				};
-				return it;
-			}),
-			};
-		}}
-		rowKey="id"
-		bordered
-		pagination={{
-			showQuickJumper: true,
-			showSizeChanger: true,
-		}}
-		headerTitle={intl.formatMessage({ id: "dict" })}
-		toolBarRender={() => [
-			<Button key="button" icon={<PlusOutlined />} type="primary">
-			新建
-			</Button>,
-		]}
-		/>
-	  	</Content>
-	</Layout>
-	<Footer/>
-	</Layout>
-  );
+									var it: IItem = {
+										id,
+										word: `word ${id}`,
+										type: valueEnum[2],
+										grammar: "阳-单-属",
+										parent: `parent ${id}`,
+										meaning: `meaning ${id}`,
+										note: `note ${id}`,
+										factors: `factors ${id}`,
+										createdAt: Date.now() - Math.floor(Math.random() * 200000),
+									};
+									return it;
+								}),
+							};
+						}}
+						rowKey="id"
+						bordered
+						pagination={{
+							showQuickJumper: true,
+							showSizeChanger: true,
+						}}
+						headerTitle={intl.formatMessage({ id: "dict" })}
+						toolBarRender={() => [
+							<Button key="button" icon={<PlusOutlined />} type="primary">
+								新建
+							</Button>,
+						]}
+					/>
+				</Content>
+			</Layout>
+			<Footer />
+		</Layout>
+	);
 };
 
 export default Widget;

+ 86 - 66
dashboard/src/pages/studio/group/edit.tsx

@@ -1,14 +1,13 @@
 import { useParams } from "react-router-dom";
-import { ProForm, ProFormText , ProFormSelect,ProFormTextArea } from "@ant-design/pro-components";
+import { ProForm, ProFormText, ProFormSelect, ProFormTextArea } from "@ant-design/pro-components";
 import { useIntl } from "react-intl";
-import { message,Layout } from "antd";
+import { message, Layout } from "antd";
 
 import HeadBar from "../../../components/studio/HeadBar";
 import LeftSider from "../../../components/studio/LeftSider";
 import Footer from "../../../components/studio/Footer";
 
-const {  Content } = Layout;
-
+const { Content } = Layout;
 
 interface IFormData {
 	name: string;
@@ -16,74 +15,95 @@ interface IFormData {
 	lang: string;
 	summary: string;
 	studio: string;
-  }
+}
 const Widget = () => {
 	const intl = useIntl();
-	const { studioname,groupid } = useParams();//url 参数
-  return (
-	<Layout>
-	<HeadBar/>
-	<Layout>
-		<LeftSider selectedKeys="userdict"/>
-		<Content>
-           <h2>studio/{studioname}/{intl.formatMessage({ id: "columns.studio.group.title" })}/edit/{groupid}</h2>
+	const { studioname, groupid } = useParams(); //url 参数
+	return (
+		<Layout>
+			<HeadBar />
+			<Layout>
+				<LeftSider selectedKeys="userdict" />
+				<Content>
+					<h2>
+						studio/{studioname}/{intl.formatMessage({ id: "columns.studio.group.title" })}/edit/{groupid}
+					</h2>
 
-		<ProForm<IFormData>
-		onFinish={async (values: IFormData) => {
-			// TODO
-			values.studio = "aaaa";
-			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.Group>
-			<ProFormSelect
-				options={[
-					{value: 'translation', label: intl.formatMessage({ id: "channel.type.translation.title" }), },
-					{value: 'nissaya', label: intl.formatMessage({ id: "channel.type.nissaya.title" }), },
-				]}
-				width="md"
-				name="type"
-				rules={[{ required: true, message: intl.formatMessage({ id: "channel.create.message.noname" }) }]}
-				label={intl.formatMessage({ id: "channel.type" })}
-				/>
-		</ProForm.Group>
-		<ProForm.Group>
-			<ProFormSelect
-				options={[
-					{value: 'zh-Hans', label: "简体中文", },
-					{value: 'zh-Hant', label: "繁体中文", },
-					{value: 'en-US', label: "English", },
-				]}
-				width="md"
-				name="lang"
-				rules={[{ required: true, message: intl.formatMessage({ id: "channel.create.message.noname" }) }]}
-				label={intl.formatMessage({ id: "channel.lang" })}
-				/>
-		</ProForm.Group>
+					<ProForm<IFormData>
+						onFinish={async (values: IFormData) => {
+							// TODO
+							values.studio = "aaaa";
+							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.Group>
-			<ProFormTextArea  name="summary" label="简介" />
-		</ProForm.Group>
+						<ProForm.Group>
+							<ProFormSelect
+								options={[
+									{
+										value: "translation",
+										label: intl.formatMessage({ id: "channel.type.translation.title" }),
+									},
+									{
+										value: "nissaya",
+										label: intl.formatMessage({ id: "channel.type.nissaya.title" }),
+									},
+								]}
+								width="md"
+								name="type"
+								rules={[
+									{
+										required: true,
+										message: intl.formatMessage({ id: "channel.create.message.noname" }),
+									},
+								]}
+								label={intl.formatMessage({ id: "channel.type" })}
+							/>
+						</ProForm.Group>
+						<ProForm.Group>
+							<ProFormSelect
+								options={[
+									{ value: "zh-Hans", label: "简体中文" },
+									{ value: "zh-Hant", label: "繁体中文" },
+									{ value: "en-US", label: "English" },
+								]}
+								width="md"
+								name="lang"
+								rules={[
+									{
+										required: true,
+										message: intl.formatMessage({ id: "channel.create.message.noname" }),
+									},
+								]}
+								label={intl.formatMessage({ id: "channel.lang" })}
+							/>
+						</ProForm.Group>
 
-		</ProForm>
-		
-		</Content>
-	</Layout>
+						<ProForm.Group>
+							<ProFormTextArea name="summary" label="简介" />
+						</ProForm.Group>
+					</ProForm>
+				</Content>
+			</Layout>
 
-      <Footer/>
-    </Layout>
-  );
+			<Footer />
+		</Layout>
+	);
 };
 
 export default Widget;

+ 116 - 115
dashboard/src/pages/studio/group/index.tsx

@@ -1,141 +1,142 @@
-import { useParams,Link } from "react-router-dom";
+import { useParams, Link } from "react-router-dom";
 import { useIntl } from "react-intl";
-import { useState } from 'react';
-import { ProList } from '@ant-design/pro-components';
-import { Space, Tag, Button, Layout,Breadcrumb } from 'antd';
-
+import { useState } from "react";
+import { ProList } from "@ant-design/pro-components";
+import { Space, Tag, Button, Layout, Breadcrumb } from "antd";
 
 import HeadBar from "../../../components/studio/HeadBar";
 import LeftSider from "../../../components/studio/LeftSider";
 import Footer from "../../../components/studio/Footer";
-const {  Content } = Layout;
-
+const { Content } = Layout;
 
 const defaultData = [
 	{
-	  id: '1',
-	  name: 'IAPT巴利语学习营',
-	  tag:[{title:"巴利语",color:"blue"},{title:"大金塔",color:"yellow"},{title:"拥有者",color:"success"}],
-	  image:
-		'https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg',
-		description: '我是一条测试的描述',
+		id: "1",
+		name: "IAPT巴利语学习营",
+		tag: [
+			{ title: "巴利语", color: "blue" },
+			{ title: "大金塔", color: "yellow" },
+			{ title: "拥有者", color: "success" },
+		],
+		image: "https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg",
+		description: "我是一条测试的描述",
 	},
 	{
-	  id: '2',
-	  name: '初级巴利语入门',
-	  tag:[{title:"巴利语",color:"blue"},{title:"管理员",color:"processing"}],
-	  image:
-		'https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg',
-		description: '我是一条测试的描述',
+		id: "2",
+		name: "初级巴利语入门",
+		tag: [
+			{ title: "巴利语", color: "blue" },
+			{ title: "管理员", color: "processing" },
+		],
+		image: "https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg",
+		description: "我是一条测试的描述",
 	},
 	{
-	  id: '3',
-	  name: '大金塔寺学习小组',
-	  tag:[{title:"大金塔",color:"yellow"},{title:"成员",color:"default"}],
-	  image:
-		'https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg',
-		description: '我是一条测试的描述',
+		id: "3",
+		name: "大金塔寺学习小组",
+		tag: [
+			{ title: "大金塔", color: "yellow" },
+			{ title: "成员", color: "default" },
+		],
+		image: "https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg",
+		description: "我是一条测试的描述",
 	},
 	{
-	  id: '4',
-	  name: '趣向涅槃之道第一册翻译组',
-	  tag:[{title:"大金塔",color:"yellow"},{title:"成员",color:"default"}],
-	  image:
-		'https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg',
-		description: '我是一条测试的描述',
+		id: "4",
+		name: "趣向涅槃之道第一册翻译组",
+		tag: [
+			{ title: "大金塔", color: "yellow" },
+			{ title: "成员", color: "default" },
+		],
+		image: "https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg",
+		description: "我是一条测试的描述",
 	},
-  ];
-  type DataItem = typeof defaultData[number];
+];
+type DataItem = typeof defaultData[number];
 
 const Widget = () => {
-	const intl = useIntl();//i18n
-	const { studioname } = useParams();//url 参数
+	const intl = useIntl(); //i18n
+	const { studioname } = useParams(); //url 参数
 	const [dataSource, setDataSource] = useState<DataItem[]>(defaultData);
 	const linkStudio = `/studio/${studioname}`;
 	const linkGroup = `${linkStudio}/group`;
-  return (
-    <Layout>
-		<HeadBar/>
+	return (
 		<Layout>
-			<LeftSider selectedKeys="group"/>
-			<Content>
-				<Breadcrumb>
-					<Breadcrumb.Item>
-						<Link to={linkStudio}>{intl.formatMessage({ id: "columns.studio.title" })}</Link>
-					</Breadcrumb.Item>
-					<Breadcrumb.Item>
-						{intl.formatMessage({ id: "columns.studio.collaboration.title" })}
-					</Breadcrumb.Item>
-					<Breadcrumb.Item >
-						<Link to={linkGroup}>{intl.formatMessage({ id: "columns.studio.group.title" })}</Link>
-					</Breadcrumb.Item>
-					<Breadcrumb.Item>列表</Breadcrumb.Item>
-				</Breadcrumb>
-				<Layout>
-					<ProList<DataItem>
-					rowKey="id"
-					headerTitle="群组列表"
-					dataSource={dataSource}
-					showActions="hover"
-					editable={{
-						onSave: async (key, record, originRow) => {
-						console.log(key, record, originRow);
-						return true;
-						},
-					}}
-					onDataSourceChange={setDataSource}
-					metas={{
-						title: {
-						dataIndex: 'name',
-						render: (text, row, index, action) => {
-							return (
-								<Link to ={row.id}>{row.name}</Link>
-							);
-						},
-						},
-						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,key) => {
-								return <Tag color={item.color}>{item.title}</Tag>
-							});
-							return (
-							<Space size={0}>
-								{showtag}
-							</Space>
-							);
-						},
-						},
-						actions: {
-						render: (text, row, index, action) => [
-							<Button
-							onClick={() => {
-								action?.startEditable(row.id);
+			<HeadBar />
+			<Layout>
+				<LeftSider selectedKeys="group" />
+				<Content>
+					<Breadcrumb>
+						<Breadcrumb.Item>
+							<Link to={linkStudio}>{intl.formatMessage({ id: "columns.studio.title" })}</Link>
+						</Breadcrumb.Item>
+						<Breadcrumb.Item>
+							{intl.formatMessage({ id: "columns.studio.collaboration.title" })}
+						</Breadcrumb.Item>
+						<Breadcrumb.Item>
+							<Link to={linkGroup}>{intl.formatMessage({ id: "columns.studio.group.title" })}</Link>
+						</Breadcrumb.Item>
+						<Breadcrumb.Item>列表</Breadcrumb.Item>
+					</Breadcrumb>
+					<Layout>
+						<ProList<DataItem>
+							rowKey="id"
+							headerTitle="群组列表"
+							dataSource={dataSource}
+							showActions="hover"
+							editable={{
+								onSave: async (key, record, originRow) => {
+									console.log(key, record, originRow);
+									return true;
+								},
+							}}
+							onDataSourceChange={setDataSource}
+							metas={{
+								title: {
+									dataIndex: "name",
+									render: (text, row, index, action) => {
+										return <Link to={row.id}>{row.name}</Link>;
+									},
+								},
+								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, key) => {
+											return <Tag color={item.color}>{item.title}</Tag>;
+										});
+										return <Space size={0}>{showtag}</Space>;
+									},
+								},
+								actions: {
+									render: (text, row, index, action) => [
+										<Button
+											onClick={() => {
+												action?.startEditable(row.id);
+											}}
+											key="link"
+										>
+											编辑
+										</Button>,
+									],
+								},
 							}}
-							key="link"
-							>
-							编辑
-							</Button>,
-						],
-						},
-					}}
-					/>
-				</Layout>				
-			</Content>
+						/>
+					</Layout>
+				</Content>
+			</Layout>
+			<Footer />
 		</Layout>
-        <Footer/>
-    </Layout>
-  );
+	);
 };
 
 export default Widget;

+ 34 - 30
dashboard/src/pages/studio/group/show.tsx

@@ -1,47 +1,51 @@
-import { useParams,Link } from "react-router-dom";
+import { useParams, Link } from "react-router-dom";
 import { useIntl } from "react-intl";
-import {  Layout,Breadcrumb } from 'antd';
-import { Col, Row} from 'antd';
+import { Layout, Breadcrumb } from "antd";
+import { Col, Row } from "antd";
 import HeadBar from "../../../components/studio/HeadBar";
 import LeftSider from "../../../components/studio/LeftSider";
 import Footer from "../../../components/studio/Footer";
 import GroupFile from "../../../components/studio/group/GroupFile";
 import GroupMember from "../../../components/studio/group/GroupMember";
 
-const {  Content } = Layout;
+const { Content } = Layout;
 
 const Widget = () => {
 	const intl = useIntl();
-	const { studioname,groupid } = useParams();//url 参数
+	const { studioname, groupid } = useParams(); //url 参数
 	const linkStudio = `/studio/${studioname}`;
 	const linkGroup = `${linkStudio}/group`;
-  return (
-    <Layout>
-		<HeadBar/>
+	return (
 		<Layout>
-			<LeftSider selectedKeys="group"/>
-			<Content>
-				<Breadcrumb>
-					<Breadcrumb.Item>
-						<Link to={linkStudio}>{intl.formatMessage({ id: "columns.studio.title" })}</Link>
-					</Breadcrumb.Item>
-					<Breadcrumb.Item>
-						{intl.formatMessage({ id: "columns.studio.collaboration.title" })}
-					</Breadcrumb.Item>
-					<Breadcrumb.Item >
-						<Link to={linkGroup}>{intl.formatMessage({ id: "columns.studio.group.title" })}</Link>
-					</Breadcrumb.Item>
-					<Breadcrumb.Item>{groupid}</Breadcrumb.Item>
-				</Breadcrumb>
-				<Row>
-					<Col flex="auto"><GroupFile groupid={groupid}/></Col>
-					<Col flex="400px"><GroupMember groupid={groupid}/></Col>
-				</Row>	
-			</Content>
+			<HeadBar />
+			<Layout>
+				<LeftSider selectedKeys="group" />
+				<Content>
+					<Breadcrumb>
+						<Breadcrumb.Item>
+							<Link to={linkStudio}>{intl.formatMessage({ id: "columns.studio.title" })}</Link>
+						</Breadcrumb.Item>
+						<Breadcrumb.Item>
+							{intl.formatMessage({ id: "columns.studio.collaboration.title" })}
+						</Breadcrumb.Item>
+						<Breadcrumb.Item>
+							<Link to={linkGroup}>{intl.formatMessage({ id: "columns.studio.group.title" })}</Link>
+						</Breadcrumb.Item>
+						<Breadcrumb.Item>{groupid}</Breadcrumb.Item>
+					</Breadcrumb>
+					<Row>
+						<Col flex="auto">
+							<GroupFile groupid={groupid} />
+						</Col>
+						<Col flex="400px">
+							<GroupMember groupid={groupid} />
+						</Col>
+					</Row>
+				</Content>
+			</Layout>
+			<Footer />
 		</Layout>
-        <Footer/>
-    </Layout>
-  );
+	);
 };
 
 export default Widget;

+ 16 - 16
dashboard/src/pages/studio/index.tsx

@@ -1,4 +1,4 @@
-import { useParams ,Link} from "react-router-dom";
+import { useParams, Link } from "react-router-dom";
 import { useIntl } from "react-intl";
 import { Space } from "antd";
 import HeadBar from "../../components/studio/HeadBar";
@@ -6,21 +6,21 @@ import LeftSider from "../../components/studio/LeftSider";
 import Footer from "../../components/studio/Footer";
 
 const Widget = () => {
-	const intl = useIntl();//i18n
-	const { studioname } = useParams();//url 参数
-  return (
-    <div>
-		<HeadBar/>
-		<LeftSider/>
-      <h2>studio/{studioname}/首页</h2>
-      <div>
-		<Space>
-			<Link to=""> </Link>
-		</Space>
-      </div>
-      <Footer/>
-    </div>
-  );
+	const intl = useIntl(); //i18n
+	const { studioname } = useParams(); //url 参数
+	return (
+		<div>
+			<HeadBar />
+			<LeftSider />
+			<h2>studio/{studioname}/首页</h2>
+			<div>
+				<Space>
+					<Link to=""> </Link>
+				</Space>
+			</div>
+			<Footer />
+		</div>
+	);
 };
 
 export default Widget;

+ 18 - 16
dashboard/src/pages/studio/palicanon/index.tsx

@@ -1,4 +1,4 @@
-import { useParams ,Link} from "react-router-dom";
+import { useParams, Link } from "react-router-dom";
 import { useIntl } from "react-intl";
 import { Space } from "antd";
 import HeadBar from "../../../components/studio/HeadBar";
@@ -6,21 +6,23 @@ import LeftSider from "../../../components/studio/LeftSider";
 import Footer from "../../../components/studio/Footer";
 
 const Widget = () => {
-	const intl = useIntl();//i18n
-	const { studioname } = useParams();//url 参数
-  return (
-    <div>
-		<HeadBar/>
-		<LeftSider/>
-      <h2>studio/{studioname}/{intl.formatMessage({ id: "columns.studio.palicanon.title" })}</h2>
-      <div>
-		<Space>
-			<Link to=""> </Link>
-		</Space>
-      </div>
-      <Footer/>
-    </div>
-  );
+	const intl = useIntl(); //i18n
+	const { studioname } = useParams(); //url 参数
+	return (
+		<div>
+			<HeadBar />
+			<LeftSider />
+			<h2>
+				studio/{studioname}/{intl.formatMessage({ id: "columns.studio.palicanon.title" })}
+			</h2>
+			<div>
+				<Space>
+					<Link to=""> </Link>
+				</Space>
+			</div>
+			<Footer />
+		</div>
+	);
 };
 
 export default Widget;

+ 18 - 16
dashboard/src/pages/studio/recent/index.tsx

@@ -1,4 +1,4 @@
-import { useParams ,Link} from "react-router-dom";
+import { useParams, Link } from "react-router-dom";
 import { useIntl } from "react-intl";
 import { Space } from "antd";
 import HeadBar from "../../../components/studio/HeadBar";
@@ -6,21 +6,23 @@ import LeftSider from "../../../components/studio/LeftSider";
 import Footer from "../../../components/studio/Footer";
 
 const Widget = () => {
-	const intl = useIntl();//i18n
-	const { studioname } = useParams();//url 参数
-  return (
-    <div>
-		<HeadBar/>
-		<LeftSider/>
-      <h2>studio/{studioname}/{intl.formatMessage({ id: "columns.studio.recent.title" })}</h2>
-      <div>
-		<Space>
-			<Link to=""> </Link>
-		</Space>
-      </div>
-      <Footer/>
-    </div>
-  );
+	const intl = useIntl(); //i18n
+	const { studioname } = useParams(); //url 参数
+	return (
+		<div>
+			<HeadBar />
+			<LeftSider />
+			<h2>
+				studio/{studioname}/{intl.formatMessage({ id: "columns.studio.recent.title" })}
+			</h2>
+			<div>
+				<Space>
+					<Link to=""> </Link>
+				</Space>
+			</div>
+			<Footer />
+		</div>
+	);
 };
 
 export default Widget;

+ 162 - 164
dashboard/src/pages/studio/term/index.tsx

@@ -2,182 +2,180 @@ 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 { Button,Layout,Space, Table  } from "antd";
-import {  PlusOutlined } from '@ant-design/icons';
+import { Button, Layout, Space, Table } from "antd";
+import { PlusOutlined } from "@ant-design/icons";
 
 import HeadBar from "../../../components/studio/HeadBar";
 import LeftSider from "../../../components/studio/LeftSider";
 import Footer from "../../../components/studio/Footer";
 
-const {  Content } = Layout;
+const { Content } = Layout;
 
 interface IItem {
-  id: number;
-  word: string;
-  tag: string;
-  channel: string;
-  meaning: string;  
-  meaning2: string;  
-  note: string;
-  createdAt: number;
+	id: number;
+	word: string;
+	tag: string;
+	channel: string;
+	meaning: string;
+	meaning2: string;
+	note: string;
+	createdAt: number;
 }
 
-
 const Widget = () => {
-  const intl = useIntl();
-  const { studioname } = useParams();
-  return (
-	<Layout>
-	<HeadBar/>
-	<Layout>
-		<LeftSider selectedKeys="term"/>
-		<Content>
+	const intl = useIntl();
+	const { studioname } = useParams();
+	return (
+		<Layout>
+			<HeadBar />
+			<Layout>
+				<LeftSider selectedKeys="term" />
+				<Content>
+					<Layout>{studioname}</Layout>
+					<ProTable<IItem>
+						columns={[
+							{
+								title: intl.formatMessage({ id: "term.fields.sn.label" }),
+								dataIndex: "id",
+								key: "id",
+								width: 80,
+								search: false,
+							},
+							{
+								title: intl.formatMessage({ id: "term.fields.word.label" }),
+								dataIndex: "word",
+								key: "word",
+								render: (_) => <Link to="">{_}</Link>,
+								tip: "单词过长会自动收缩",
+								ellipsis: true,
+								formItemProps: {
+									rules: [
+										{
+											required: true,
+											message: "此项为必填项",
+										},
+									],
+								},
+							},
+							{
+								title: intl.formatMessage({ id: "term.fields.description.label" }),
+								dataIndex: "tag",
+								key: "description",
+								search: false,
+							},
+							{
+								title: intl.formatMessage({ id: "term.fields.channel.label" }),
+								dataIndex: "channel",
+								valueType: "select",
+								valueEnum: {
+									all: { text: "全部" },
+									1: { text: "中文" },
+									2: { text: "中文草稿" },
+									3: { text: "英文" },
+									4: { text: "英文草稿" },
+									5: { text: "Visuddhinanda" },
+								},
+							},
+							{
+								title: intl.formatMessage({ id: "term.fields.meaning.label" }),
+								dataIndex: "meaning",
+								key: "meaning",
+							},
+							{
+								title: intl.formatMessage({ id: "term.fields.meaning2.label" }),
+								dataIndex: "meaning2",
+								key: "meaning2",
+								tip: "意思过长会自动收缩",
+								ellipsis: true,
+							},
+							{
+								title: intl.formatMessage({ id: "term.fields.note.label" }),
+								dataIndex: "note",
+								key: "note",
+								search: false,
+								tip: "注释过长会自动收缩",
+								ellipsis: true,
+							},
+							{
+								title: intl.formatMessage({ id: "forms.fields.created-at.label" }),
+								key: "created-at",
+								width: 200,
+								search: false,
+								dataIndex: "createdAt",
+								valueType: "date",
+								sorter: (a, b) => a.createdAt - b.createdAt,
+							},
+						]}
+						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>
+									已选 {selectedRowKeys.length} 项
+									<Button type="link" style={{ marginInlineStart: 8 }} onClick={onCleanSelected}>
+										取消选择
+									</Button>
+								</span>
+							</Space>
+						)}
+						tableAlertOptionRender={() => {
+							return (
+								<Space size={16}>
+									<Button type="link">批量删除</Button>
+									<Button type="link">导出数据</Button>
+								</Space>
+							);
+						}}
+						request={async (params = {}, sorter, filter) => {
+							// TODO
+							console.log(params, sorter, filter);
 
-		<Layout>{studioname}</Layout>
-		<ProTable<IItem>
-		columns={[
-			{
-			title: intl.formatMessage({ id: "term.fields.sn.label" }),
-			dataIndex: "id",
-			key: "id",
-			width: 80,
-			search: false,
-			},
-			{
-			title: intl.formatMessage({ id: "term.fields.word.label" }),
-			dataIndex: "word",
-			key: "word",
-			render: (_) => <Link to="">{_}</Link>,
-			tip: '单词过长会自动收缩',
-			ellipsis: true,
-			formItemProps: {
-				rules: [
-				{
-					required: true,
-					message: '此项为必填项',
-				},
-				],
-			},
-			},
-			{
-				title: intl.formatMessage({ id: "term.fields.description.label" }),
-				dataIndex: "tag",
-				key: "description",
-				search: false,
-			},
-			{
-				title: intl.formatMessage({ id: "term.fields.channel.label" }),
-				dataIndex: "channel",
-				valueType: 'select',
-				valueEnum: {
-				  all: { text: '全部' },
-				  1: { text: '中文' },
-				  2: { text: '中文草稿' },
-				  3: { text: '英文' },
-				  4: { text: '英文草稿' },
-				  5: { text: 'Visuddhinanda' },
-				},
-			},
-			{
-				title: intl.formatMessage({ id: "term.fields.meaning.label" }),
-				dataIndex: "meaning",
-				key: "meaning",
-			},
-			{
-				title: intl.formatMessage({ id: "term.fields.meaning2.label" }),
-				dataIndex: "meaning2",
-				key: "meaning2",
-				tip: '意思过长会自动收缩',
-				ellipsis: true,
-			},
-			{
-				title: intl.formatMessage({ id: "term.fields.note.label" }),
-				dataIndex: "note",
-				key: "note",
-				search: false,
-				tip: '注释过长会自动收缩',
-				ellipsis: true,
-			},
-			{
-			title: intl.formatMessage({ id: "forms.fields.created-at.label" }),
-			key: "created-at",
-			width: 200,
-			search: false,
-			dataIndex: 'createdAt',
-			valueType: 'date',
-			sorter: (a, b) => a.createdAt - b.createdAt,
-			},
-		]}
-		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>
-				已选 {selectedRowKeys.length} 项
-				<Button type="link" style={{ marginInlineStart: 8 }} onClick={onCleanSelected}>
-				取消选择
-				</Button>
-			</span>
-			</Space>
-		)}
-		tableAlertOptionRender={() => {
-			return (
-			<Space size={16}>
-				<Button type="link">批量删除</Button>
-				<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,
-				word: `word ${id}`,
-				tag: "",
-				channel: "2",
-				meaning: `parent ${id}`,
-				meaning2: `meaning ${id}`,
-				note: `note ${id}`,
-				createdAt: Date.now() - Math.floor(Math.random() * 200000),
-				};
-				return it;
-			}),
-			};
-		}}
-		rowKey="id"
-		//bordered
-		pagination={{
-			showQuickJumper: true,
-			showSizeChanger: true,
-		}}
-		headerTitle={intl.formatMessage({ id: "dict" })}
-		toolBarRender={() => [
-			<Button key="button" icon={<PlusOutlined />} type="primary">
-			新建
-			</Button>,
-		]}
-		search={{
-			filterType: 'light',
-		  }}
-		dateFormatter="string"
-		/>
-	  	</Content>
-	</Layout>
-	<Footer/>
-	</Layout>
-  );
+									var it: IItem = {
+										id,
+										word: `word ${id}`,
+										tag: "",
+										channel: "2",
+										meaning: `parent ${id}`,
+										meaning2: `meaning ${id}`,
+										note: `note ${id}`,
+										createdAt: Date.now() - Math.floor(Math.random() * 200000),
+									};
+									return it;
+								}),
+							};
+						}}
+						rowKey="id"
+						//bordered
+						pagination={{
+							showQuickJumper: true,
+							showSizeChanger: true,
+						}}
+						headerTitle={intl.formatMessage({ id: "dict" })}
+						toolBarRender={() => [
+							<Button key="button" icon={<PlusOutlined />} type="primary">
+								新建
+							</Button>,
+						]}
+						search={{
+							filterType: "light",
+						}}
+						dateFormatter="string"
+					/>
+				</Content>
+			</Layout>
+			<Footer />
+		</Layout>
+	);
 };
 
 export default Widget;