Просмотр исходного кода

Merge pull request #2072 from visuddhinanda/agile

支持标准答案
visuddhinanda 1 год назад
Родитель
Сommit
7d87128ff9

+ 3 - 0
dashboard/src/components/template/SentEdit.tsx

@@ -83,6 +83,7 @@ export interface IWidgetSentEditInner {
   channels?: string[];
   origin?: ISentence[];
   translation?: ISentence[];
+  answer?: ISentence;
   path?: ITocPathNode[];
   layout?: "row" | "column";
   tranNum?: number;
@@ -103,6 +104,7 @@ export const SentEditInner = ({
   channels,
   origin,
   translation,
+  answer,
   path,
   layout = "column",
   tranNum,
@@ -196,6 +198,7 @@ export const SentEditInner = ({
         wordEnd={wordEnd}
         origin={origin}
         translation={translation}
+        answer={answer}
         layout={layout}
         magicDict={magicDict}
         compact={isCompact}

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

@@ -25,6 +25,7 @@ interface IWidgetSentContent {
   wordEnd: number;
   origin?: ISentence[];
   translation?: ISentence[];
+  answer?: ISentence;
   layout?: TDirection;
   magicDict?: string;
   compact?: boolean;
@@ -41,6 +42,7 @@ const SentContentWidget = ({
   wordEnd,
   origin,
   translation,
+  answer,
   layout = "column",
   compact = false,
   mode,
@@ -157,7 +159,8 @@ const SentContentWidget = ({
                 channelId={item.channel.id}
                 channelType={item.channel.type}
                 channelLang={item.channel.lang}
-                data={JSON.parse(item.content ? item.content : "")}
+                data={JSON.parse(item.content ?? "")}
+                answer={answer ? JSON.parse(answer.content ?? "") : undefined}
                 mode={mode}
                 wbwProgress={wbwProgress}
                 onChange={(data: IWbw[]) => {

+ 54 - 3
dashboard/src/components/template/SentEdit/SentWbw.tsx

@@ -1,12 +1,13 @@
-import { List, message } from "antd";
+import { List, Space, message } from "antd";
 import { useEffect, useState } from "react";
 
 import { get } from "../../../request";
 import { ISentenceWbwListResponse } from "../../api/Corpus";
-import { IWidgetSentEditInner, SentEditInner } from "../SentEdit";
+import { ISentence, IWidgetSentEditInner, SentEditInner } from "../SentEdit";
 import { useAppSelector } from "../../../hooks";
 import { courseInfo, memberInfo } from "../../../reducers/current-course";
 import { courseUser } from "../../../reducers/course-user";
+import User, { IUser } from "../../auth/User";
 
 interface IWidget {
   book: number;
@@ -30,6 +31,7 @@ const SentWbwWidget = ({
 }: IWidget) => {
   const [initLoading, setInitLoading] = useState(true);
   const [sentData, setSentData] = useState<IWidgetSentEditInner[]>([]);
+  const [answer, setAnswer] = useState<ISentence>();
   const course = useAppSelector(courseInfo);
   const courseMember = useAppSelector(memberInfo);
 
@@ -46,11 +48,14 @@ const SentWbwWidget = ({
         //学生,仅列出答案channel
         url += `&channels=${course.channelId}`;
       } else if (courseMember) {
+        //管理者,助教,列出学生作业
         console.debug("course member", courseMember);
+        /*
         const channels = courseMember
           .filter((value) => typeof value.channel_id === "string")
           .map((item) => item.channel_id);
         url += `&channels=${channels.join(",")}`;
+        */
       }
     } else {
       if (channelsId && channelsId.length > 0) {
@@ -64,6 +69,17 @@ const SentWbwWidget = ({
         if (json.ok) {
           console.log("sim load", json.data.count);
           setSentData(json.data.rows);
+          if (myCourse && course) {
+            const answerData = json.data.rows.find((value) =>
+              value.origin
+                ? value.origin[0].channel.id === course?.channelId
+                : false
+            );
+            if (answerData?.origin) {
+              setAnswer(answerData.origin[0]);
+              console.debug("answer", answerData.origin[0]);
+            }
+          }
         } else {
           message.error(json.message);
         }
@@ -75,10 +91,29 @@ const SentWbwWidget = ({
         }
       });
   };
+
   useEffect(() => {
     load();
   }, []);
 
+  //没交作业的人
+
+  let nonWbwUser: IUser[] = [];
+  const isCourseAnswer = myCourse && course && myCourse.role !== "student";
+  if (isCourseAnswer && courseMember) {
+    const hasWbwUsers = sentData.map((item) =>
+      item.translation ? item.translation[0].studio : undefined
+    );
+    courseMember
+      .filter((value) => value.role === "student")
+      .forEach((value) => {
+        const curr = hasWbwUsers.find((value1) => value1?.id === value.user_id);
+        if (!curr && value.user) {
+          nonWbwUser.push(value.user);
+        }
+      });
+  }
+  console.debug("没交作业", courseMember, sentData, nonWbwUser);
   return (
     <>
       <List
@@ -88,10 +123,26 @@ const SentWbwWidget = ({
         dataSource={sentData}
         renderItem={(item, index) => (
           <List.Item key={index}>
-            <SentEditInner {...item} wbwProgress={wbwProgress} />
+            <SentEditInner
+              {...item}
+              answer={answer}
+              wbwProgress={wbwProgress}
+            />
           </List.Item>
         )}
       />
+      <div>
+        {isCourseAnswer ? (
+          <Space>
+            {"无作业:"}
+            {nonWbwUser.length > 0
+              ? nonWbwUser.map((item, id) => {
+                  return <User {...item} />;
+                })
+              : "无"}
+          </Space>
+        ) : undefined}
+      </div>
     </>
   );
 };

+ 15 - 2
dashboard/src/components/template/Wbw/WbwCase.tsx

@@ -12,6 +12,7 @@ import { useAppSelector } from "../../../hooks";
 import { inlineDict as _inlineDict } from "../../../reducers/inline-dict";
 import WbwParent2 from "./WbwParent2";
 import { IApiResponseDictData } from "../../api/Dict";
+import { errorClass } from "./WbwMeaning";
 
 export interface ValueType {
   key: string;
@@ -94,11 +95,18 @@ export const caseInDict = (
 
 interface IWidget {
   data: IWbw;
+  answer?: IWbw;
   display?: TWbwDisplayMode;
   onSplit?: Function;
   onChange?: Function;
 }
-const WbwCaseWidget = ({ data, display, onSplit, onChange }: IWidget) => {
+const WbwCaseWidget = ({
+  data,
+  answer,
+  display,
+  onSplit,
+  onChange,
+}: IWidget) => {
   const intl = useIntl();
   const defaultMenu: MenuProps["items"] = [
     {
@@ -173,7 +181,12 @@ const WbwCaseWidget = ({ data, display, onSplit, onChange }: IWidget) => {
   ) {
     //非标点符号
     return (
-      <div className="wbw_word_item" style={{ display: "flex" }}>
+      <div
+        className={
+          "wbw_word_item" + errorClass(data.case?.value, answer?.case?.value)
+        }
+        style={{ display: "flex" }}
+      >
         <Text type="secondary">
           <div>
             <Dropdown

+ 9 - 1
dashboard/src/components/template/Wbw/WbwFactorMeaning.tsx

@@ -9,17 +9,20 @@ import { PaliReal } from "../../../utils";
 import { useAppSelector } from "../../../hooks";
 import { inlineDict as _inlineDict } from "../../../reducers/inline-dict";
 import { ItemType } from "antd/lib/menu/hooks/useItems";
+import { errorClass } from "./WbwMeaning";
 
 const { Text } = Typography;
 
 interface IWidget {
   data: IWbw;
+  answer?: IWbw;
   factors?: string;
   display?: TWbwDisplayMode;
   onChange?: Function;
 }
 const WbwFactorMeaningWidget = ({
   data,
+  answer,
   display,
   onChange,
   factors,
@@ -95,7 +98,12 @@ const WbwFactorMeaningWidget = ({
 
   if (typeof data.real !== "undefined" && PaliReal(data.real.value) !== "") {
     return (
-      <div>
+      <div
+        className={
+          "wbw_word_item" +
+          errorClass(data.factorMeaning?.value, answer?.factorMeaning?.value)
+        }
+      >
         <Text type="secondary">
           <Dropdown
             menu={{

+ 9 - 2
dashboard/src/components/template/Wbw/WbwFactors.tsx

@@ -8,6 +8,7 @@ import { IWbw, TWbwDisplayMode } from "./WbwWord";
 import { useAppSelector } from "../../../hooks";
 import { inlineDict as _inlineDict } from "../../../reducers/inline-dict";
 import { IApiResponseDictData } from "../../api/Dict";
+import { errorClass } from "./WbwMeaning";
 
 const { Text } = Typography;
 
@@ -38,11 +39,12 @@ export const getFactorsInDict = (
 
 interface IWidget {
   data: IWbw;
+  answer?: IWbw;
   display?: TWbwDisplayMode;
   onChange?: Function;
 }
 
-const WbwFactorsWidget = ({ data, display, onChange }: IWidget) => {
+const WbwFactorsWidget = ({ data, answer, display, onChange }: IWidget) => {
   const intl = useIntl();
   const defaultMenu: MenuProps["items"] = [
     {
@@ -124,7 +126,12 @@ const WbwFactorsWidget = ({ data, display, onChange }: IWidget) => {
       }
     }
     return (
-      <div>
+      <div
+        className={
+          "wbw_word_item" +
+          errorClass(data.factors?.value, answer?.factors?.value)
+        }
+      >
         <Text type="secondary">
           <Dropdown menu={{ items, onClick }} placement="bottomLeft">
             {factors}

+ 22 - 1
dashboard/src/components/template/Wbw/WbwMeaning.tsx

@@ -10,14 +10,29 @@ import EditableLabel from "../../general/EditableLabel";
 
 const { Text } = Typography;
 
+export const errorClass = (
+  data?: string | null,
+  answer?: string | null
+): string => {
+  let classError = "";
+  if (data && answer) {
+    if (answer !== data) {
+      classError = " wbw_error";
+    }
+  }
+  return classError;
+};
+
 interface IWidget {
   data: IWbw;
+  answer?: IWbw;
   display?: TWbwDisplayMode;
   mode?: ArticleMode;
   onChange?: Function;
 }
 const WbwMeaningWidget = ({
   data,
+  answer,
   display = "block",
   mode = "edit",
   onChange,
@@ -145,8 +160,14 @@ const WbwMeaningWidget = ({
     data.real.value.trim().length > 0
   ) {
     //非标点符号
+
     return (
-      <div className="wbw_word_item">
+      <div
+        className={
+          "wbw_word_item" +
+          errorClass(data.meaning?.value, answer?.meaning?.value)
+        }
+      >
         {editable || display === "list" ? (
           meaningInner
         ) : (

+ 6 - 0
dashboard/src/components/template/Wbw/WbwWord.tsx

@@ -104,6 +104,7 @@ export interface IWbwFields {
 export type TWbwDisplayMode = "block" | "inline" | "list";
 interface IWidget {
   data: IWbw;
+  answer?: IWbw;
   channelId: string;
   display?: TWbwDisplayMode;
   fields?: IWbwFields;
@@ -115,6 +116,7 @@ interface IWidget {
 }
 const WbwWordWidget = ({
   data,
+  answer,
   channelId,
   display,
   mode = "edit",
@@ -288,6 +290,7 @@ const WbwWordWidget = ({
               key="meaning"
               mode={mode}
               data={wordData}
+              answer={answer}
               display={display}
               onChange={(e: string) => {
                 const newData: IWbw = JSON.parse(JSON.stringify(wordData));
@@ -303,6 +306,7 @@ const WbwWordWidget = ({
             <WbwFactors
               key="factors"
               data={wordData}
+              answer={answer}
               display={display}
               onChange={(e: string) => {
                 console.log("factor change", e);
@@ -320,6 +324,7 @@ const WbwWordWidget = ({
             <WbwFactorMeaning
               key="fm"
               data={wordData}
+              answer={answer}
               display={display}
               factors={newFactors}
               onChange={(e: string) => {
@@ -361,6 +366,7 @@ const WbwWordWidget = ({
             <WbwCase
               key="case"
               data={wordData}
+              answer={answer}
               display={display}
               onSplit={(e: boolean) => {
                 console.log("onSplit", wordData.factors?.value);

+ 6 - 0
dashboard/src/components/template/Wbw/wbw.css

@@ -81,6 +81,12 @@
   padding: 0;
   cursor: pointer;
 }
+.wbw_error {
+  text-decoration: underline wavy;
+  text-decoration-color: red;
+  text-underline-offset: 2px;
+}
+
 .layout-v .wbw_word_item {
   margin-right: 8px;
   flex: 5;

+ 16 - 2
dashboard/src/components/template/WbwSent.tsx

@@ -136,6 +136,7 @@ interface IWbwRequest {
 }
 interface IWidget {
   data: IWbw[];
+  answer?: IWbw[];
   book: number;
   para: number;
   wordStart: number;
@@ -155,6 +156,7 @@ interface IWidget {
 }
 export const WbwSentCtl = ({
   data,
+  answer,
   channelId,
   channelType,
   channelLang,
@@ -531,10 +533,16 @@ export const WbwSentCtl = ({
         }
       });
   };
-  const wbwRender = (item: IWbw, id: number, studio?: IStudio) => {
+
+  interface wbwOptions {
+    studio?: IStudio;
+    answer?: IWbw;
+  }
+  const wbwRender = (item: IWbw, id: number, options?: wbwOptions) => {
     return (
       <WbwWord
         data={item}
+        answer={options?.answer}
         channelId={channelId}
         key={id}
         mode={displayMode}
@@ -776,7 +784,13 @@ export const WbwSentCtl = ({
               return newItem;
             })
             .map((item, id) => {
-              return wbwRender(item, id, studio);
+              const currAnswer = answer?.find(
+                (value) => value.sn.join() === item.sn.join()
+              );
+              return wbwRender(item, id, {
+                studio: studio,
+                answer: currAnswer,
+              });
             })
         ) : (
           <Tree

+ 11 - 10
dashboard/src/pages/studio/course/list.tsx

@@ -40,6 +40,7 @@ import {
 import { ISetStatus, setStatus } from "../../../components/course/UserAction";
 import { useAppSelector } from "../../../hooks";
 import { currentUser } from "../../../reducers/current-user";
+import User from "../../../components/auth/User";
 
 const renderBadge = (count: number, active = false) => {
   return (
@@ -172,19 +173,19 @@ const Widget = () => {
                       </Tag>
                     </div>
                     <div>{row.subtitle}</div>
+                    <div>
+                      <Space>
+                        {intl.formatMessage({
+                          id: "forms.fields.teacher.label",
+                        })}
+                        <User {...row.teacher} />
+                      </Space>
+                    </div>
                   </div>
                 </Space>
               );
             },
           },
-          {
-            //主讲人
-            title: intl.formatMessage({
-              id: "forms.fields.teacher.label",
-            }),
-            dataIndex: "teacher",
-            key: "teacher",
-          },
           {
             title: intl.formatMessage({
               id: "course.table.count.member.title",
@@ -197,8 +198,8 @@ const Widget = () => {
             title: intl.formatMessage({
               id: "course.table.count.progressing.title",
             }),
-            dataIndex: "countProgressing",
-            key: "countProgressing",
+            dataIndex: "count_progressing",
+            key: "count_progressing",
             width: 80,
             hideInTable: activeKey === "study" ? true : false,
           },