Przeglądaj źródła

与api server 连接

visuddhinanda 3 lat temu
rodzic
commit
12b266899a

+ 189 - 210
dashboard/src/pages/studio/course/edit.tsx

@@ -1,240 +1,219 @@
 import { useState } from "react";
 import { useParams } from "react-router-dom";
-import { useIntl, FormattedMessage } from "react-intl";
+import { useIntl } from "react-intl";
 import {
   ProForm,
   ProFormText,
-  ProFormTextArea,
   ProFormDateRangePicker,
+  ProFormSelect,
+  ProFormUploadButton,
+  RequestOptionsType,
 } from "@ant-design/pro-components";
-import { Card, message, Col, Row, Divider, Tabs } from "antd";
-import { get, put } from "../../../request";
-import { marked } from "marked";
+import { Card, message, Form } from "antd";
+
+import { API_HOST, get, put } from "../../../request";
 import {
   ICourseDataRequest,
   ICourseResponse,
 } from "../../../components/api/Course";
 import PublicitySelect from "../../../components/studio/PublicitySelect";
 import GoBack from "../../../components/studio/GoBack";
-import UploadTexture from "../../../components/library/course/UploadTexture";
-import TeacherSelect from "../../../components/library/course/TeacherSelect";
-import StudentsSelect from "../../../components/library/course/StudentsSelect";
+
 import LessonSelect from "../../../components/library/course/LessonSelect";
-import LessonTreeShow from "../../../components/library/course/LessonTreeShow";
+import { IUserListResponse } from "../../../components/api/Auth";
+import MDEditor from "@uiw/react-md-editor";
+import { DefaultOptionType } from "antd/lib/select";
+import { UploadFile } from "antd/es/upload/interface";
+import { IAttachmentResponse } from "../../../components/api/Attachments";
 
 interface IFormData {
-  uid: string;
   title: string;
-
-  t_type: string;
-  status: number;
-  lang: string;
+  subtitle: string;
+  content: string;
+  cover: UploadFile<IAttachmentResponse>[];
+  teacherId: string;
+  anthologyId: string;
+  dateRange?: Date[];
 }
-const onChange = (key: string) => {
-  console.log(key);
-};
-let groupid = "1";
 
 const Widget = () => {
   const intl = useIntl();
   const { studioname, courseId } = useParams(); //url 参数
   const [title, setTitle] = useState("loading");
-
+  const [contentValue, setContentValue] = useState("");
+  const [teacherOption, setTeacherOption] = useState<DefaultOptionType[]>([]);
+  const [currTeacher, setCurrTeacher] = useState<RequestOptionsType>();
   return (
-    <Tabs
-      onChange={onChange}
-      type="card"
-      items={[
-        {
-          label: `基本信息`,
-          key: "1",
-          children: (
-            <Card
-              title={
-                <GoBack
-                  to={`/studio/${studioname}/course/list`}
-                  title={title}
-                />
-              }
-            >
-              <ProForm<IFormData>
-                onFinish={async (values: IFormData) => {
-                  // TODO
-                  let request = {
-                    uid: courseId?.toString,
-                    title: "课程" + courseId,
-                    subtitle: "课程副标题" + courseId,
-                    teacher: 1,
-                    course_count: 2,
-                    type: 30,
-                    created_at: "",
-                    updated_at: "",
-                    article_id: 1, //"1e642dac-dcb2-468a-8cc7-0228e5ca6ac4",
-                    course_start_at: "", //课程开始时间
-                    course_end_at: "", //课程结束时间
-                    intro_markdown: "", //简介
-                    cover_img_name: "", //封面图片文件名
-                  };
-                }}
-                /*		    const request = {
-    uid: courseid ? courseid : "",
-    title: values.title,
-    subtitle: values.subtitle,
-    teacher: values.teacher,//UserID
-    course_count: values.course_count,//课程数
-    type: values.type,//类型-公开/内部
-    created_at: values.created_at,//创建时间
-    updated_at: values.updated_at,//修改时间
-    article_id: values.article_id,//文集ID
-    course_start_at: values.course_start_at,//课程开始时间
-    course_end_at: values.course_end_at,//课程结束时间
-    intro_markdown: values.intro_markdown,//简介
-    cover_img_name: values.cover_img_name,//封面图片文件名
-  };
-  console.log(request);
-  const res = await put<ICourseDataRequest, ICourseResponse>(
-    `/v2/course/${courseid}`,
-    request
-  );
-  console.log(res);
-  if (res.ok) {
-    message.success(intl.formatMessage({ id: "flashes.success" }));
-  } else {
-    message.error(res.message);
-  }
-}}
-request={async () => {
-  const res = await get<ICourseResponse>(`/v2/course/${courseid}`);
-  setTitle(res.data.title);
-  return {
-    uid: res.data.uid,
-    title: res.data.title,
-    subtitle: res.data.subtitle,
-    summary: res.data.summary,
-    content: res.data.content,
-    content_type: res.data.content_type,
-    lang: res.data.lang,
-    status: res.data.status,
-  };
-}}*/
-              >
-                <ProForm.Group>
-                  <ProFormText
-                    width="md"
-                    name="title"
-                    required
-                    label={intl.formatMessage({
-                      id: "forms.fields.title.label",
-                    })}
-                    rules={[
-                      {
-                        required: true,
-                        message: intl.formatMessage({
-                          id: "forms.message.title.required",
-                        }),
-                      },
-                    ]}
-                  />
-                </ProForm.Group>
-                <ProForm.Group>
-                  <ProFormText
-                    width="md"
-                    name="subtitle"
-                    label={intl.formatMessage({
-                      id: "forms.fields.subtitle.label",
-                    })}
-                  />
-                </ProForm.Group>
-                <ProForm.Group>
-                  <p style={{ fontWeight: "bold", fontSize: 15 }}>
-                    <FormattedMessage id="forms.fields.upload.texture" />{" "}
-                  </p>
-                  <UploadTexture />
-                </ProForm.Group>
-                <ProForm.Group>
-                  <ProFormDateRangePicker
-                    width="md"
-                    name={["contract", "createTime"]}
-                    label="课程区间"
-                  />
-                </ProForm.Group>
-                <ProForm.Group>
-                  <PublicitySelect />
-                </ProForm.Group>
-                <Divider />
+    <Card
+      title={<GoBack to={`/studio/${studioname}/course/list`} title={title} />}
+    >
+      <ProForm<IFormData>
+        formKey="course_edit"
+        onFinish={async (values: IFormData) => {
+          console.log("all data", values);
+          console.log(
+            "start",
+            values.dateRange ? values.dateRange[0].toString() : ""
+          );
+          console.log(values.cover);
+          const res = await put<ICourseDataRequest, ICourseResponse>(
+            `/v2/course/${courseId}`,
+            {
+              title: values.title, //标题
+              subtitle: values.subtitle, //副标题
+              content: contentValue, //简介
+              cover: values.cover[0].response?.data.url, //封面图片文件名
+              teacher_id: values.teacherId, //UserID
+              type: 1, //类型-公开/内部
+              anthology_id: values.anthologyId, //文集ID
+              start_at: values.dateRange
+                ? values.dateRange[0].toString()
+                : undefined, //课程开始时间
+              end_at: values.dateRange
+                ? values.dateRange[1].toString()
+                : undefined, //课程结束时间
+            }
+          );
+          console.log(res);
+          if (res.ok) {
+            message.success(intl.formatMessage({ id: "flashes.success" }));
+          } else {
+            message.error(res.message);
+          }
+        }}
+        request={async () => {
+          const res = await get<ICourseResponse>(`/v2/course/${courseId}`);
+          setTitle(res.data.title);
+          console.log(res.data);
+          setContentValue(res.data.content);
+          if (res.data.teacher) {
+            console.log("teacher", res.data.teacher);
+            setCurrTeacher({
+              value: res.data.teacher.id,
+              label: res.data.teacher.nickName,
+            });
+            setTeacherOption([
+              {
+                value: res.data.teacher.id,
+                label: res.data.teacher.nickName,
+              },
+            ]);
+          }
+          return {
+            title: res.data.title,
+            subtitle: res.data.subtitle,
+            content: res.data.content,
+            cover: res.data.cover
+              ? [
+                  {
+                    uid: "1",
+                    name: "cover",
+                    thumbUrl: API_HOST + "/" + res.data.cover,
+                  },
+                ]
+              : [],
+            teacherId: res.data.teacher?.id,
+            anthologyId: res.data.anthology_id,
+            dateRange: res.data.start_at
+              ? [new Date(res.data.start_at), new Date(res.data.end_at)]
+              : undefined,
+          };
+        }}
+      >
+        <ProForm.Group>
+          <ProFormUploadButton
+            name="cover"
+            label="封面"
+            max={1}
+            fieldProps={{
+              name: "file",
+              listType: "picture-card",
+              className: "avatar-uploader",
+            }}
+            action={`${API_HOST}/api/v2/attachments`}
+            extra="封面必须为正方形。最大512*512"
+          />
+        </ProForm.Group>
+        <ProForm.Group>
+          <ProFormText
+            width="md"
+            name="title"
+            required
+            label={intl.formatMessage({
+              id: "forms.fields.title.label",
+            })}
+            rules={[
+              {
+                required: true,
+              },
+            ]}
+          />
+          <ProFormText
+            width="md"
+            name="subtitle"
+            label={intl.formatMessage({
+              id: "forms.fields.subtitle.label",
+            })}
+          />
+        </ProForm.Group>
 
-                <Row>
-                  <Col flex="400px">
-                    <TeacherSelect groupId={groupid} />
-                  </Col>
-                </Row>
-                <Divider />
-                <Row>
-                  <Col flex="400px">
-                    <LessonSelect groupId={groupid} />
-                  </Col>
-                </Row>
-                <Divider />
-                <ProForm.Group>
-                  <ProFormTextArea
-                    name="summary"
-                    width="md"
-                    label={intl.formatMessage({
-                      id: "forms.fields.summary.label",
-                    })}
-                  />
-
-                  <p style={{ fontWeight: "bold", fontSize: 15 }}>
-                    <FormattedMessage id="forms.fields.markdown.label" />{" "}
-                  </p>
-                  <Row>
-                    <div
-                      dangerouslySetInnerHTML={{
-                        __html: marked.parse(
-                          "# 这是标题\n" +
-                            "[ **M** ] arkdown + E [ **ditor** ] = **Mditor**  \n" +
-                            "**这是加粗的文字**\n\n" +
-                            "*这是倾斜的文字*`\n\n" +
-                            "***这是斜体加粗的文字***\n\n" +
-                            "~~这是加删除线的文字~~ \n\n"
-                        ),
-                      }}
-                    ></div>
-                  </Row>
-                </ProForm.Group>
-              </ProForm>
-            </Card>
-          ),
-        },
-        {
-          label: `学生与助教选择 `,
-          key: "2",
-          children: (
-            <Card
-              title={
-                <GoBack
-                  to={`/studio/${studioname}/course/list`}
-                  title={title}
-                />
+        <ProForm.Group>
+          <ProFormSelect
+            options={teacherOption}
+            width="md"
+            name="teacherId"
+            label={intl.formatMessage({ id: "forms.fields.teacher.label" })}
+            showSearch
+            debounceTime={300}
+            request={async ({ keyWords }) => {
+              console.log("keyWord", keyWords);
+              if (typeof keyWords === "undefined") {
+                return currTeacher ? [currTeacher] : [];
               }
-            >
-              <ProForm<IFormData> onFinish={async (values: IFormData) => {}}>
-                <ProForm.Group>
-                  <LessonTreeShow />
-                </ProForm.Group>
-                <ProForm.Group></ProForm.Group>
+              const json = await get<IUserListResponse>(
+                `/v2/user?view=key&key=${keyWords}`
+              );
+              const userList = json.data.rows.map((item) => {
+                return {
+                  value: item.id,
+                  label: `${item.userName}-${item.nickName}`,
+                };
+              });
+              console.log("json", userList);
+              return userList;
+            }}
+            placeholder={intl.formatMessage({
+              id: "forms.fields.teacher.label",
+            })}
+          />
+          <ProFormDateRangePicker
+            width="md"
+            name="dateRange"
+            label="课程区间"
+          />
+        </ProForm.Group>
+
+        <ProForm.Group>
+          <PublicitySelect />
+        </ProForm.Group>
 
-                <Row>
-                  <Col flex="400px">
-                    <StudentsSelect groupId={groupid} />
-                  </Col>
-                </Row>
-              </ProForm>
-            </Card>
-          ),
-        },
-      ]}
-    />
+        <ProForm.Group>
+          <Form.Item
+            name="content"
+            label={intl.formatMessage({ id: "forms.fields.summary.label" })}
+          >
+            <MDEditor
+              value={contentValue}
+              onChange={(value: string | undefined) => {
+                if (value) {
+                  setContentValue(value);
+                }
+              }}
+            />
+          </Form.Item>
+        </ProForm.Group>
+      </ProForm>
+    </Card>
   );
 };
 

+ 66 - 83
dashboard/src/pages/studio/course/list.tsx

@@ -1,6 +1,6 @@
 import { useParams, Link } from "react-router-dom";
 import { useIntl } from "react-intl";
-import React, { useState } from "react";
+import React, { useEffect, useRef, useState } from "react";
 import {
   Space,
   Badge,
@@ -11,12 +11,15 @@ import {
   Menu,
   Table,
 } from "antd";
-import { ProTable, ProList } from "@ant-design/pro-components";
+import { ProTable, ActionType } from "@ant-design/pro-components";
 import { PlusOutlined, SearchOutlined } from "@ant-design/icons";
 
-import CourseCreate from "../../../components/library/course/CourseCreate";
+import CourseCreate from "../../../components/course/CourseCreate";
 import { get } from "../../../request";
-import { ICourseListResponse } from "../../../components/api/Course";
+import {
+  ICourseListResponse,
+  ICourseNumberResponse,
+} from "../../../components/api/Course";
 import { PublicityValueEnum } from "../../../components/studio/table";
 const onMenuClick: MenuProps["onClick"] = (e) => {
   console.log("click", e);
@@ -45,15 +48,15 @@ interface DataItem {
   title: string; //标题
   subtitle: string; //副标题
   teacher: string; //UserID
-  //course_count: number;//课程数
+  course_count?: number; //课程数
   type: number; //类型-公开/内部
   createdAt: number; //创建时间
-  //updated_at: number;//修改时间
-  //article_id: number;//文集ID
-  //course_start_at: string;//课程开始时间
-  //course_end_at: string;//课程结束时间
-  //intro_markdown: string;//简介
-  //cover_img_name: string;//封面图片文件名
+  updated_at?: number; //修改时间
+  article_id?: number; //文集ID
+  course_start_at?: string; //课程开始时间
+  course_end_at?: string; //课程结束时间
+  intro_markdown?: string; //简介
+  cover_img_name?: string; //封面图片文件名
 }
 
 const renderBadge = (count: number, active = false) => {
@@ -72,11 +75,40 @@ const renderBadge = (count: number, active = false) => {
 const Widget = () => {
   const intl = useIntl(); //i18n
   const { studioname } = useParams(); //url 参数
-  const courseCreate = <CourseCreate studio={studioname} />;
-  const [activeKey, setActiveKey] = useState<React.Key | undefined>("tab1");
+
+  const [activeKey, setActiveKey] = useState<React.Key | undefined>("create");
+  const [createNumber, setCreateNumber] = useState<number>(0);
+  const [teachNumber, setTeachNumber] = useState<number>(0);
+  const [studyNumber, setStudyNumber] = useState<number>(0);
+  const ref = useRef<ActionType>();
+  const [openCreate, setOpenCreate] = useState(false);
+
+  const courseCreate = (
+    <CourseCreate
+      studio={studioname}
+      onCreate={() => {
+        //新建课程成功后刷新
+        setActiveKey("create");
+        setCreateNumber(createNumber + 1);
+        ref.current?.reload();
+        setOpenCreate(false);
+      }}
+    />
+  );
+  useEffect(() => {
+    const url = `/v2/course-my-course?studio=${studioname}`;
+    get<ICourseNumberResponse>(url).then((json) => {
+      if (json.ok) {
+        setCreateNumber(json.data.create);
+        setTeachNumber(json.data.teach);
+        setStudyNumber(json.data.study);
+      }
+    });
+  }, [studioname]);
   return (
     <>
       <ProTable<DataItem>
+        actionRef={ref}
         columns={[
           {
             title: intl.formatMessage({
@@ -97,11 +129,7 @@ const Widget = () => {
             tip: "过长会自动收缩",
             ellipsis: true,
             render: (text, row, index, action) => {
-              return (
-                <Link to={`/studio/${studioname}/course/${row.id}/edit`}>
-                  {row.title}
-                </Link>
-              );
+              return <Link to={`/course/${row.id}`}>{row.title}</Link>;
             },
           },
           {
@@ -211,7 +239,8 @@ const Widget = () => {
         request={async (params = {}, sorter, filter) => {
           // TODO
           console.log(params, sorter, filter);
-          let url = `/v2/course?view=studio&name=${studioname}`;
+          console.log(activeKey);
+          let url = `/v2/course?view=${activeKey}&studio=${studioname}`;
           const offset =
             ((params.current ? params.current : 1) - 1) *
             (params.pageSize ? params.pageSize : 20);
@@ -220,49 +249,22 @@ const Widget = () => {
             url += "&search=" + (params.keyword ? params.keyword : "");
           }
 
-          /*const res = await get<ICourseListResponse>(url);
+          const res = await get<ICourseListResponse>(url);
           const items: DataItem[] = res.data.rows.map((item, id) => {
             const date = new Date(item.created_at);
             return {
               sn: id + 1,
-              id: item.uid,
+              id: item.id,
               title: item.title,
               subtitle: item.subtitle,
-              teacher: item.teacher,
+              teacher: item.teacher.nickName,
               type: item.type,
               createdAt: date.getTime(),
             };
-          });*/
+          });
 
-          //const items = Array.from({ length: 23 }).map((_, i) => ({
-          const items: DataItem[] = [
-            {
-              sn: 1,
-              id: "1",
-              title: "课程" + 1,
-              subtitle: "课程副标题" + 1,
-              teacher: "小僧善巧",
-              type: 30,
-              createdAt: 20020202,
-              //updated_at: 123,
-              //article_id: 123,
-              //course_start_at: 123,
-              //course_end_at: 123,
-              //intro_markdown: 123,
-              //cover_img_name: 123,
-            },
-            {
-              sn: 2,
-              id: "2",
-              title: "课程" + 2,
-              subtitle: "课程副标题" + 2,
-              teacher: "小僧善巧",
-              type: 30,
-              createdAt: 20020202,
-            },
-          ];
           return {
-            total: items.length, //res.data.count,
+            total: res.data.count,
             succcess: true,
             data: items,
           };
@@ -282,6 +284,11 @@ const Widget = () => {
             content={courseCreate}
             title="Create"
             placement="bottomRight"
+            trigger="click"
+            open={openCreate}
+            onOpenChange={(newOpen: boolean) => {
+              setOpenCreate(newOpen);
+            }}
           >
             <Button key="button" icon={<PlusOutlined />} type="primary">
               {intl.formatMessage({ id: "buttons.create" })}
@@ -296,7 +303,8 @@ const Widget = () => {
                 key: "create",
                 label: (
                   <span>
-                    我建立的课程{renderBadge(99, activeKey === "create")}
+                    我建立的课程
+                    {renderBadge(createNumber, activeKey === "create")}
                   </span>
                 ),
               },
@@ -304,7 +312,8 @@ const Widget = () => {
                 key: "study",
                 label: (
                   <span>
-                    我参加的课程{renderBadge(99, activeKey === "study")}
+                    我参加的课程
+                    {renderBadge(studyNumber, activeKey === "study")}
                   </span>
                 ),
               },
@@ -312,45 +321,19 @@ const Widget = () => {
                 key: "teach",
                 label: (
                   <span>
-                    我任教的课程{renderBadge(32, activeKey === "teach")}
+                    我任教的课程
+                    {renderBadge(teachNumber, activeKey === "teach")}
                   </span>
                 ),
               },
             ],
             onChange(key) {
+              console.log("show course", key);
               setActiveKey(key);
+              ref.current?.reload();
             },
           },
         }}
-        /*
-        toolbar={{
-          menu: {
-            activeKey,
-            items: [
-              {
-                key: 'tab1',
-                label: <span>全部实验室{renderBadge(99, activeKey === 'tab1')}</span>,
-              },
-              {
-                key: 'tab2',
-                label: <span>我创建的实验室{renderBadge(32, activeKey === 'tab2')}</span>,
-              },
-            ],
-            onChange(key) {
-              setActiveKey(key);
-            },
-          },
-          search: {
-            onSearch: (value: string) => {
-              alert(value);
-            },
-          },
-          actions: [
-            <Button type="primary" key="primary">
-              新建实验
-            </Button>,
-          ],
-        }}*/
       />
     </>
   );