Przeglądaj źródła

Merge branch 'develop' of http://52.130.252.100:10000/TEAMMODEL/TEAMModelOS into develop

zhouj1203@hotmail.com 1 rok temu
rodzic
commit
171b3085a1
41 zmienionych plików z 10466 dodań i 1313 usunięć
  1. 4 4
      TEAMModelOS.SDK/Models/Cosmos/Common/GroupList.cs
  2. 1 0
      TEAMModelOS/ClientApp/package.json
  3. 118 0
      TEAMModelOS/ClientApp/public/lang/en-US.js
  4. 118 0
      TEAMModelOS/ClientApp/public/lang/zh-CN.js
  5. 121 0
      TEAMModelOS/ClientApp/public/lang/zh-TW.js
  6. 64 1
      TEAMModelOS/ClientApp/src/api/learnActivity.js
  7. 7 0
      TEAMModelOS/ClientApp/src/boot-app.js
  8. 60 0
      TEAMModelOS/ClientApp/src/common/BaseCountDown.vue
  9. 44 0
      TEAMModelOS/ClientApp/src/common/BaseLayout.vue
  10. 82 80
      TEAMModelOS/ClientApp/src/common/BaseSelectSchool.vue
  11. 7 3
      TEAMModelOS/ClientApp/src/common/QrcodeModal.vue
  12. 1 1
      TEAMModelOS/ClientApp/src/components/evaluation/ExerciseList.vue
  13. 109 1
      TEAMModelOS/ClientApp/src/router/routes.js
  14. 20 0
      TEAMModelOS/ClientApp/src/utils/loadmore.js
  15. 9 0
      TEAMModelOS/ClientApp/src/utils/plugins.js
  16. 44 0
      TEAMModelOS/ClientApp/src/view/areaMgmt/AreaLayout.vue
  17. 4 4
      TEAMModelOS/ClientApp/src/view/joinclass/JoinClass.vue
  18. 116 14
      TEAMModelOS/ClientApp/src/view/mycourse/MyCourse.vue
  19. 255 0
      TEAMModelOS/ClientApp/src/view/mycourse/score/AddProject.less
  20. 975 0
      TEAMModelOS/ClientApp/src/view/mycourse/score/AddProject.vue
  21. 126 0
      TEAMModelOS/ClientApp/src/view/mycourse/score/DialogBox.less
  22. 441 0
      TEAMModelOS/ClientApp/src/view/mycourse/score/DialogBoxAttend.vue
  23. 401 0
      TEAMModelOS/ClientApp/src/view/mycourse/score/DialogBoxScoreCont.vue
  24. 285 0
      TEAMModelOS/ClientApp/src/view/mycourse/score/DialogBoxStudentScore.vue
  25. 0 0
      TEAMModelOS/ClientApp/src/view/mycourse/score/Score.less
  26. 89 0
      TEAMModelOS/ClientApp/src/view/mycourse/score/Score.vue
  27. 180 0
      TEAMModelOS/ClientApp/src/view/mycourse/score/ScoreBody.less
  28. 2171 0
      TEAMModelOS/ClientApp/src/view/mycourse/score/ScoreBody.vue
  29. 23 0
      TEAMModelOS/ClientApp/src/view/mycourse/score/ScoreIndex.less
  30. 44 0
      TEAMModelOS/ClientApp/src/view/mycourse/score/ScoreIndex.vue
  31. 14 0
      TEAMModelOS/ClientApp/src/view/questionnaire/ManageQuestionnaire.vue
  32. 1423 0
      TEAMModelOS/ClientApp/src/view/signupActivity/createActivity.vue
  33. BIN
      TEAMModelOS/ClientApp/src/view/signupActivity/demo.jpeg
  34. 798 0
      TEAMModelOS/ClientApp/src/view/signupActivity/infoActivity copy.vue
  35. 182 0
      TEAMModelOS/ClientApp/src/view/signupActivity/infoActivity.vue
  36. 314 0
      TEAMModelOS/ClientApp/src/view/signupActivity/manageActivity.vue
  37. 279 0
      TEAMModelOS/ClientApp/src/view/signupActivity/processActivity.vue
  38. 18 1
      TEAMModelOS/ClientApp/src/view/syllabus/Syllabus.vue
  39. 1212 1193
      TEAMModelOS/ClientApp/src/view/vote/ManageVote.vue
  40. 249 9
      TEAMModelOS/Controllers/Both/CourseBaseController.cs
  41. 58 2
      TEAMModelOS/Controllers/Both/GroupListController.cs

+ 4 - 4
TEAMModelOS.SDK/Models/Cosmos/Common/GroupList.cs

@@ -58,11 +58,11 @@ namespace TEAMModelOS.SDK.Models
         /// <summary>
         /// 二维码 天数
         /// </summary>
-        public int qrcodeDays { get; set; }
+        public int qrcodeDays { get; set; } = 1;
         /// <summary>
         /// 二维码过期时间
         /// </summary>
-        public int qrcodeExpire { get; set; }
+        public long qrcodeExpire { get; set; }
         //名单中包含校内学生的入学年
         public HashSet<int> grades { get; set; } = new HashSet<int>();
     }
@@ -125,7 +125,7 @@ namespace TEAMModelOS.SDK.Models
         /// <summary>
         /// 二维码过期时间
         /// </summary>
-        public int qrcodeExpire { get; set; }
+        public long qrcodeExpire { get; set; }
         /// <summary>
         /// 二维码 天数
         /// </summary>
@@ -328,7 +328,7 @@ namespace TEAMModelOS.SDK.Models
         /// <summary>
         /// 二维码过期时间
         /// </summary>
-        public int qrcodeExpire { get; set; }
+        public long qrcodeExpire { get; set; }
         /// <summary>
         /// 二维码 天数
         /// </summary>

+ 1 - 0
TEAMModelOS/ClientApp/package.json

@@ -49,6 +49,7 @@
     "lodash": "^4.17.21",
     "node-fetch": "^2.6.1",
     "oidc-client": "^1.9.1",
+    "primevue": "^2.10.1",
     "qrcodejs2": "0.0.2",
     "snapsvg": "^0.5.1",
     "spark-md5": "^3.0.2",

+ 118 - 0
TEAMModelOS/ClientApp/public/lang/en-US.js

@@ -7547,5 +7547,123 @@ const LANG_EN_US = {
             classNum: 'Classrooms',
             totalTime: 'Total Time'
         }
+    },
+    scoreCalc:{
+        description1:'Underlined words',
+        description1_2:' can be clicked to edit detailed items',
+         blueWord:'Blue word',
+         canModifyable:' can be modified',
+         description2: ' the content can be modified, the number in (brackets) is the result of score x percentage%',
+         simple_complete: 'Simplified / Complete',
+         name:'Name',
+         id:'Student ID',
+         class:'Class',
+         th_homework:'Homework',
+         th_exam: 'Exam',
+         totalScore: 'Total score',
+         PRScore:'P-Rating',
+         gradesInClass: 'Classroom grades',
+         attend:'Attend',
+         point:'Scoreboard',
+         interactiveScore: 'Interactive score',
+         absence: ' Abs',//Absence
+         sickLeave:'SL',//Sick leave
+         personalLeave: 'PL',//Personal leave
+         publicLeave: 'PH',//Public holiday
+         absence2:'Absence',
+         sickLeave2:'Sick leave',
+         personalLeave2:'Personal leave',
+         publicLeave2:'Public holiday',
+         isSave: 'Archived',
+         notSave: 'Not archived',
+         delete:'Delete',
+         export:'Export',
+         print:'Print',
+         save:'Save',
+         scoreCalculation: 'Score Calculation',
+         simpleAttendanceCalculation:'Simple Attend. Calc.',//Simple Attendance Calculation
+         attendanceCalculationMethod: 'Attendance Calc. Method',//Attendance calculation method
+         customCalculation: 'Custom Calc.',//Custom calculation
+         percentageScore:'Normalization Score',
+         standardizedsScoring:'Percentage Scoring',
+         customScoring:'Custom Scoring',
+         calculationDescription1: 'The teacher gives the score directly according to the attendance of the students. Usually full attendance is the standard, and a certain point will be deducted for one absence. ',
+         calculationDescription2: 'Enter points for one absence',
+         marks:'Marks',
+         example:'Example:',
+         attendance:'Attendance results',
+         absences: 'Number of absences',
+         calculationDescription3: 'Absent a deduction ',
+         calculationFormula:'Calculation formula',
+         calculationDescription4: 'Check is absent:',
+         checkOk:'OK',
+         cancel:'Cancel',
+         calculationDescription5: 'Calculate the attendance rate based on the attendance times of the students, and then use it as the basis for the attendance score. The attendance rate is expressed as a percentage. For example, if the attendance rate exceeds 90%, a full score will be awarded, and a certain point will be deducted for each percentage point lower than the attendance rate. ',
+         calculationDescription6: '1% deduction for every absence',
+         attendanceOver: 'Attendance Over',
+         isFullMark: '% is full mark',
+         classGrades:'Class grades:',
+         totalNumberClasses: 'Total classes',
+         attendQwen:'Attendance',
+         formula:'Formula:',
+         calculationDescription7: 'Please fill in the calculation formula according to the following variables:',
+         numberOfSick: 'Number of sick leave',
+         numberOfPerson: 'Number of personal leave',
+         numberOfPublic: 'Number of public holidays',
+         calculationDescription8: 'Example of the formula (the following is directly based on the attendance rate of the attendance score):',
+         calculationDescription9:'※Excel formulas can be used to write! ',
+         instantPreview:'Preview Formula',
+         calculationDescription10: "Use the normalization method to convert the student's score to a standard score. ",
+         gravityCalculationScore:'Gravity calculation score',
+         averageScore: 'Average score',
+         standardDeviation: 'Standard deviation',
+         standardScoreZFormula: 'Standard score (Z) formula:',
+         derivedStandardScoreTFormula: 'Derived standard score (T) formula:',
+         calculationDescription11: '(Usually used to solve decimal and negative number problems)',
+         indicatesAverageScore: 'Represents the average score',
+         calculationDescription12: "Divides the student's score by the activity's highest score. This is then multiplied by 100 to calculate the student's percentile grade. ",
+         highestScore: 'Highest score',
+         calculationDescription13: 'Please fill in the calculation formula according to the following variables:',
+         classQwen:'Total number of classes',
+         totalScoreClass: 'The Class Total',
+         totalNumberStudents: 'The Class Size',
+         formulaExample:'Formula example:',
+         writtenas: 'written as:',
+         scoreboardScore: 'Scoreboard scores',
+         lessonRecordTitle: 'Classroom Record',
+         homeworkTitle: 'Job Activity',
+         examTitle: 'Assessment Activity',
+         totalRate: 'Total weighted',
+         itemName:'Item name',
+         percentage:'Percentage',
+         tickIncludedScore:'↓tick to be included in the score. ',
+         dropSort:'※Drag can be sorted',
+         itemRate: 'Proportion',
+         logStudentScore: 'Log student score',
+         average: 'Average',
+         login:'Login',
+         score: 'Score',
+         attendState:' ※ Attendance score: 1 attendance 2 absence 3 leave 4 sick leave 5 personal leave 6 public holiday',
+         pasteMultiple: 'Multiple paste',
+         pasteEXCEL:' ※It can be pasted directly from EXCEL',
+         back:'Return',
+         warringTotalRate: 'The total weight cannot exceed 100%',
+         dataIsSave: 'Data has been saved',
+         remind: 'Reminder',
+         remindMessage1:'<p>There are unsaved settings, do you want to continue switching active projects? </p>',
+         remindMessage2:'<p>There are unsaved settings, do you want to continue adding activity items? </p>',
+         deleteRemind1: 'Delete active sub-items',
+         remindMessage3:'<p>The data of the active sub-item will not be retrieved after deletion. Are you sure to delete the current active sub-item? </p>',
+         deleteRemind2: 'Delete active item',
+         remindMessage3:'<p>The data of the active item will not be retrieved after it is deleted. Are you sure to delete the current active item? </p>',
+         remindMessage4: 'The active item has been deleted',
+         remindMessage5: 'Score login complete',
+         scoreCalc:'Score Calculation',
+         modifyContent:'Modifyable content',   
+         ask:'Inquire',
+         DelTableQuestion:'<p>The data of the score sheet will not be retrieved after it is deleted. Are you sure to delete the current score sheet?<p>', 
+         saveAsk:'<p>Do you want to save all the content in this data table?</p>',
+         addAsk:'<p>Do you want to add a new activity item?</p>',
+         APIerr:'API request failed',          
     }
 }

+ 118 - 0
TEAMModelOS/ClientApp/public/lang/zh-CN.js

@@ -7538,5 +7538,123 @@ const LANG_ZH_CN = {
             classNum: '教室数',
             totalTime: '总时间数'
         }
+    },
+    scoreCalc:{
+        description1:'底线字',
+        description1_2:'可点击编辑细项',
+        blueWord:'蓝字',
+        canModifyable:'可修改',
+        description2:'可修改内容,(括弧)的数字为分数 x 百分比%的结果',
+        simple_complete:'精简模式/完整模式',        
+        name:'姓名',
+        id:'学号',
+        class:'课堂',
+        th_homework:'作业',
+        th_exam:'评量',
+        totalScore:'总评分',
+        PRScore:'PR值',
+        gradesInClass:'课堂细项成绩',        
+        attend:'出席',
+        point:'计分板',
+        interactiveScore:'互动分',
+        absence:'缺席',//英文版Abs
+        sickLeave:'病假',//英文版SL
+        personalLeave:'事假',//英文版PL
+        publicLeave:'公假',//英文版PH
+        absence2:'缺席',//英文版Absence
+        sickLeave2:'病假',//英文版Sick leave
+        personalLeave2:'事假',//英文版Personal leave
+        publicLeave2:'公假',//英文版Public holiday
+        isSave:'已存档',
+        notSave:'未存档',
+        delete:'删除',
+        export:'汇出',
+        print:'列印',
+        save:'储存',
+        scoreCalculation:'成绩计算',
+        simpleAttendanceCalculation:'简单出席计算法',
+        attendanceCalculationMethod:'出席率计算法',
+        customCalculation:'自订计算',
+        percentageScore:'标准化评分',
+        standardizedsScoring:'百分比评分',
+        customScoring:'自定义评分',
+        calculationDescription1:'教师根据学生的出席情况,直接给予分数。通常是以全勤为标准,缺席一次扣一定分数。 ',
+        calculationDescription2:'请输入一次缺席扣多少分数',
+        marks:'分',
+        example:'范例:',
+        attendance:'出席成绩',
+        absences:'缺席次数',
+        calculationDescription3:'缺席一次扣 ',
+        calculationFormula:'计算公式',
+        calculationDescription4:'勾选即为缺席状态:',                   
+        checkOk:'确定',
+        cancel:'取消',
+        calculationDescription5:'根据学生的出席次数计算出席率,再以此作为出席分数的依据。出席率以百分比表示,例如出席率超过90%可得满分,每低于一个百分点扣一定分数。 ',
+        calculationDescription6:'每缺席1% 扣',
+        attendanceOver:'出席率超过',
+        isFullMark:'% 为满分',
+        classGrades:'课堂成绩:',
+        totalNumberClasses:'课堂总次数',
+        attendQwen:'出席次数',
+        formula:'公式:',
+        calculationDescription7:'请依照下列变数填写计算公式:',
+        numberOfSick:'病假次数',
+        numberOfPerson:'事假次数',
+        numberOfPublic:'公假次数',
+        calculationDescription8:'公式范例(以下直接以出席率为出席分数):',
+        calculationDescription9:'※可使用excel公式写! ',
+        instantPreview:'算式写法即时预览',
+        calculationDescription10:'使用标准化方法将学生的得分转换为标准分数。 ',
+        gravityCalculationScore:'比重计算分数',
+        averageScore:'平均分数',
+        standardDeviation:'标准差',
+        standardScoreZFormula:'标准分数(Z)公式:',
+        derivedStandardScoreTFormula:'衍生标准分数(T)公式:',
+        calculationDescription11:'(通常用于解决小数及负数问题)',
+        indicatesAverageScore:'表示平均分数',
+        calculationDescription12:'将学生的得分除以活动的最高分数。然后乘以100,即可计算出学生的百分比评分。 ',
+        highestScore:'最高分数',
+        calculationDescription13:'请依照下列变数填写计算公式:',        
+        classQwen:'课堂总数',
+        totalScoreClass:'全班总分',        
+        totalNumberStudents:'学生总数',
+        formulaExample:'公式范例:',
+        writtenas:'写法为:',
+        scoreboardScore:'记分板成绩',          
+        lessonRecordTitle: '课堂纪录',
+        homeworkTitle: '作业活动',
+        examTitle: '评量活动',        
+        totalRate:'总加权',
+        itemName:'项目名称',
+        percentage:'百分比',
+        tickIncludedScore:'↓勾选则计入成绩。 ',
+        dropSort:'※拖曳可排序',
+        itemRate:'比重',
+        logStudentScore:'登录学生分数',
+        average:'平均',
+        loging:'登录',
+        score:'成绩',
+        attendState:' ※出席分数:1出席 2缺席 3请假 4病假 5事假 6公假',
+        pasteMultiple:' 多笔贴上',
+        pasteEXCEL:' ※可直接从EXCEL贴上',       
+        back:'返回',        
+        warringTotalRate:'总加权不可超过100%',
+        dataIsSave:'资料已储存',
+        remind:'提醒',
+        remindMessage1:'<p>有未储存的设定,要继续切换活动项目吗? </p>',
+        remindMessage2:'<p>有未储存的设定,要继续新增活动项目吗? </p>',
+        deleteRemind1:'删除活动子项目',
+        remindMessage3:'<p>活动子项目数据删除后将无法找回,确认删除当前活动子项目吗? </p>',
+        deleteRemind2:'删除活动项目',
+        remindMessage3:'<p>活动项目数据删除后将无法找回,确认删除当前活动项目吗? </p>',
+        remindMessage4:'活动项目已删除',
+        remindMessage5:'分数登录完成',
+        scoreCalc:'成绩计算',        
+        modifyContent:'可修改内容       ',
+        ask:'询问',
+        DelTableQuestion:'<p>成绩表数据删除后将无法找回,确定要删除此成绩表吗?<p>',
+        saveAsk:'<p>是否确定要保存此数据表的所有内容呢?</p>',  
+        addAsk:'<p>是否确定要新增活动项目呢?</p>',
+        APIerr:'API请求失败',            
     }
 }

+ 121 - 0
TEAMModelOS/ClientApp/public/lang/zh-TW.js

@@ -7534,5 +7534,126 @@ const LANG_ZH_TW = {
             classNum: '教室數',
             totalTime: '總時間數'
         }
+    },
+    scoreCalc:{
+        description1:'底線字',
+        description1_2:'可點擊編輯細項',
+        blueWord:'藍字',
+        canModifyable:'可修改',
+        description2:'可修改內容,(括弧)的數字為分數 x 百分比%的結果',
+        simple_complete:'精簡模式/完整模式',                
+        name:'姓名',
+        id:'學號',
+        class:'課堂',
+        th_homework:'作業',
+        th_exam:'評量',
+        totalScore:'總評分',
+        PRScore:'PR值',
+        gradesInClass:'課堂細項成績',        
+        attend:'出席',
+        point:'計分板',
+        interactiveScore:'互動分',
+        absence:'缺席',//英文版Abs
+        sickLeave:'病假',//英文版SL
+        personalLeave:'事假',//英文版PL
+        publicLeave:'公假',//英文版PH
+        absence2:'缺席',//英文版Absence
+        sickLeave2:'病假',//英文版Sick leave
+        personalLeave2:'事假',//英文版Personal leave
+        publicLeave2:'公假',//英文版Public holiday
+        isSave:'已存檔',
+        notSave:'未存檔',
+        delete:'刪除',
+        export:'匯出',
+        print:'列印',
+        save:'儲存',
+        scoreCalculation:'成績計算',
+        simpleAttendanceCalculation:'簡單出席計算法',
+        attendanceCalculationMethod:'出席率計算法',
+        customCalculation:'自訂計算',
+        percentageScore:'標準化評分',
+        standardizedsScoring:'百分比評分',
+        customScoring:'自訂評分',
+        calculationDescription1:'教師根據學生的出席情況,直接給予分數。通常是以全勤為標準,缺席一次扣一定分數。',
+        calculationDescription2:'請輸入一次缺席扣多少分數',
+        marks:'分',
+        example:'範例:',
+        attendance:'出席成績',
+        absences:'缺席次數',
+        calculationDescription3:'缺席一次扣 ',
+        calculationFormula:'計算公式',
+        calculationDescription4:'勾選即為缺席狀態:',                   
+        checkOk:'確定',
+        cancel:'取消',
+        calculationDescription5:'根據學生的出席次數計算出席率,再以此作為出席分數的依據。出席率以百分比表示,例如出席率超過90%可得滿分,每低於一個百分點扣一定分數。',
+        calculationDescription6:'每缺席1% 扣',
+        attendanceOver:'出席率超過',
+        isFullMark:'% 為滿分',
+        classGrades:'課堂成績:',
+        totalNumberClasses:'課堂總次數',
+        attendQwen:'出席次數', 
+        formula:'公式:',
+        calculationDescription7:'請依照下列變數填寫計算公式:',
+        numberOfSick:'病假次數',
+        numberOfPerson:'事假次數',
+        numberOfPublic:'公假次數',
+        calculationDescription8:'公式範例(以下直接以出席率為出席分數):',
+        calculationDescription9:'※可使用excel公式寫!',
+        instantPreview:'算式寫法即時預覽',
+        calculationDescription10:'使用標準化方法將學生的得分轉換為標準分數。',
+        gravityCalculationScore:'比重計算分數',
+        averageScore:'平均分數',
+        standardDeviation:'標準差',
+        standardScoreZFormula:'標準分數(Z)公式:',
+        derivedStandardScoreTFormula:'衍生標準分數(T)公式:',
+        calculationDescription11:'(通常用於解決小數及負數問題)',
+        indicatesAverageScore:'表示平均分數',
+        calculationDescription12:'將學生的得分除以活動的最高分數。然後乘以100,即可計算出學生的百分比評分。',
+        highestScore:'最高分數',
+        calculationDescription13:'請依照下列變數填寫計算公式:',        
+        classQwen:'課堂總數',
+        totalScoreClass:'全班總分',        
+        totalNumberStudents:'學生總數',
+        formulaExample:'公式範例:',
+        writtenas:'寫法為:',
+        scoreboardScore:'記分板成績',          
+        lessonRecordTitle: '課堂紀錄',
+        homeworkTitle: '作業活動',
+        examTitle: '評量活動',        
+        totalRate:'總加權',
+        itemName:'項目名稱',
+        percentage:'百分比',
+        tickIncludedScore:'↓勾選則計入成績。 ',
+        dropSort:'※拖曳可排序',
+        itemRate:'比重',
+        logStudentScore:'登錄學生分數',
+        average:'平均',
+        loging:'登錄',
+        score:'成績',
+        attendState:' ※出席分數:1出席 2缺席 3請假 4病假 5事假 6公假',
+        pasteMultiple:' 多筆貼上',
+        pasteEXCEL:' ※可直接從EXCEL貼上',       
+        back:'返回',        
+        warringTotalRate:'總加權不可超過100%',
+        dataIsSave:'資料已儲存',
+        remind:'提醒',
+        remindMessage1:'<p>有未儲存的設定,要繼續切換活動項目嗎?</p>',
+        remindMessage2:'<p>有未儲存的設定,要繼續新增活動項目嗎?</p>',
+        deleteRemind1:'刪除活動子項目',
+        remindMessage3:'<p>活動子項目數據刪除後將無法找回,確認刪除當前活動子項目嗎?</p>',
+        deleteRemind2:'刪除活動項目',
+        remindMessage3:'<p>活動項目數據刪除後將無法找回,確認刪除當前活動項目嗎?</p>',
+        remindMessage4:'活動項目已刪除',
+        remindMessage5:'分數登錄完成',
+        scoreCalc:'成績計算',        
+        modifyContent:'可修改內容       ',
+        ask:'詢問',
+        DelTableQuestion:'<p>成績表數據刪除後將無法找回,確認刪除此成績表嗎?<p>',
+        saveAsk:'<p>是否確定要儲存此資料表所有內容呢?</p>', 
+        addAsk:'<p>是否確定要新增活動項目呢?</p>',
+        APIerr:'API請求失敗',          
     }
+
+
+
 }

+ 64 - 1
TEAMModelOS/ClientApp/src/api/learnActivity.js

@@ -352,5 +352,68 @@ export default {
 	getGradeInfo: function (data) {
 	    return post('/common/exam/score', data)
 	},
-
+    /* 查詢班級歷次成績列表標題 */
+	getScoreCalctitle: function (data) {
+	    return post('/score/get-scorecalc-list', data)
+	},
+    /* 查詢班級歷次成績所有細項資料 */
+	getScoreCalcAll: function (data) {
+	    return post('/score/get-scorecalc-All', data)
+	},
+    /* 修改成績表排序 */
+	updateProjectListSort: function (data) {
+	    return post('/score/update-scorecalc-sort', data)
+	},
+    /* 修改成績表資料 */
+	updateProjectList: function (data) {
+	    return post('/score/update-scorecalc', data)
+	},  
+    /* 新增成績表 */
+	addScorecalc: function (data) {
+	    return post('/score/add-scorecalc', data)
+	},
+    /* 刪除成績表 */
+	deleteScorecalc: function (data) {
+	    return post('/score/delete-scorecalc', data)
+	},
+    /* 新增成績表子項目 */
+	addScorecalcact: function (data) {
+	    return post('/score/add-scorecalcact', data)
+	},
+    /* 更新成績表 */
+	updateScorecalc: function (data) {
+	    return post('/score/update-formula', data)
+    },
+    /* 新增自訂項目及其子項目資料 */
+	addScoreCalcAct: function (data) {
+	    return post('/score/add-scorecalcact', data)
+	},
+    /* 刪除自訂項目 */
+	deleteScoreCalcAct: function (data) {
+	    return post('/score/delete-scorecalcact', data)
+	},
+     /* 更新項目及子項目資料 */
+     updateScoreCalcAct: function (data) {
+	    return post('/score/update-scorecalcact', data)
+	},
+     /* 更新活動項目順序 */
+     updateScoreCalcActSort: function (data) {
+	    return post('/score/update-scorecalcact-sort', data)
+	},
+     /* 新增子項目 */
+     addScoreCalcActItem: function (data) {
+	    return post('/score/add-scorecalcact-item', data)
+	},
+     /* 刪除子項目 */
+     deleteScoreCalcActItem: function (data) {
+	    return post('/score/delete-scorecalcactitem', data)
+	},
+     /* 更新活動子項目順序 */
+     updateScoreCalcActItemSort: function (data) {
+	    return post('/score/update-scorecalcactitem-sort', data)
+	},
+     /* 登錄指定子項目成績 */
+     updateItemScore: function (data) {
+	    return post('/score/update-itemscore', data)
+	},
 }

+ 7 - 0
TEAMModelOS/ClientApp/src/boot-app.js

@@ -47,11 +47,18 @@ import fabric from 'fabric'
 import VideoPlayer from 'vue-video-player'
 import NewChooseContent from '@/components/selflearn/NewChooseContent'
 import VueWordcloud from 'vue-b2wordcloud'
+import plugins from './utils/plugins';
+import PrimeVue from 'primevue/config';
+
 Vue.use(VueWordcloud)
 
 jsFn.setLocalLang()
 require('video.js/dist/video-js.css')
 require('vue-video-player/src/custom-theme.css')
+//加入PrimeVue UI套件(主要為了使用Table)
+Vue.use(PrimeVue);
+//加入載入表單資料顯示的模組
+Vue.use(plugins)
 // 图片预览组件
 Vue.use(hevueImgPreview)
 // Dashboard边框组件

+ 60 - 0
TEAMModelOS/ClientApp/src/common/BaseCountDown.vue

@@ -0,0 +1,60 @@
+<template>
+	<div>
+		<span>{{ countdown }}</span>
+	</div>
+</template>
+<script>
+	export default {
+		props: {
+			timestamp: {
+				type: Number,
+				required: true
+			}
+		},
+		data() {
+			return {
+				remainingTime: 0,
+				timer: null
+			};
+		},
+		computed: {
+			countdown() {
+				if (this.remainingTime <= 0) {
+					return "已失效";
+				}
+				const hours = Math.floor(this.remainingTime / 3600);
+				const minutes = Math.floor((this.remainingTime % 3600) / 60);
+				const seconds = this.remainingTime % 60;
+				return `${this.pad(hours)} : ${this.pad(minutes)} : ${this.pad(seconds)} 后到期`;
+			}
+		},
+		beforeDestroy() {
+			this.clearTimer();
+		},
+		mounted() {
+			this.startTimer();
+		},
+
+		methods: {
+			clearTimer() {
+				clearInterval(this.timer);
+			},
+			startTimer() {
+				const currentTime = Math.floor(Date.now() / 1000);
+				this.remainingTime = Math.floor(this.timestamp / 1000) - currentTime;
+				this.timer = setInterval(() => {
+					this.remainingTime--;
+				}, 1000);
+			},
+			pad(value) {
+				return String(value).padStart(2, "0");
+			}
+		},
+        watch:{
+            timestamp(n,o){
+                this.clearTimer();
+                this.startTimer()
+            }
+        }
+	};
+</script>

+ 44 - 0
TEAMModelOS/ClientApp/src/common/BaseLayout.vue

@@ -1154,6 +1154,50 @@ export default {
               child: [],
               isShow: this.isGlobalSite,
             },
+            // 活动报名
+            {
+              icon: "iconfont icon-yishu",
+              name: '活动报名',
+              router: "",
+              tag: "",
+              role: "admin",
+              permission: "art-read|art-upd",
+              subName: "activitySystem",
+              menuName: "activitySystem",
+              isShow: false,
+              child: [
+                {
+                  icon: "iconfont icon-data-count",
+                  name: '活动管理',
+                  router: "/home/activityManage",
+                  tag: "",
+                  role: "admin",
+                  permission: "art-read|art-upd",
+                  menuName: "activityManage",
+                  isShow: true,
+                },
+                {
+                  icon: "iconfont icon-test",
+                  name: '活动审核',
+                  router: "/home/activityProcess",
+                  tag: "",
+                  role: "admin",
+                  permission: "art-read|art-upd",
+                  menuName: "activityProcess",
+                  isShow: true,
+                },
+                /* {
+                  icon: "iconfont icon-assess",
+                  name: '基础设置',
+                  router: "/home/activitySet",
+                  tag: "",
+                  role: "admin",
+                  permission: "art-read|art-upd",
+                  menuName: "activitySet",
+                  isShow: true,
+                }, */
+              ],
+            },
           ]
         : [];
       return data;

+ 82 - 80
TEAMModelOS/ClientApp/src/common/BaseSelectSchool.vue

@@ -37,26 +37,26 @@
 					</div>
 				</DropdownMenu>
 			</Dropdown>
-      <!-- 学期切换 -->
-			<Dropdown @on-click="onSemesterSelect" transfer  style="margin-left: 20px" v-if="curSemester">
+			<!-- 学期切换 -->
+			<Dropdown @on-click="onSemesterSelect" transfer style="margin-left: 20px" v-if="curSemester && showSemesterSelect">
 				<span class="period-select">
 					&nbsp;
-					{{ curSemester.year + $t('unit.year') + curSemester.name }}
+					{{ curSemester.year + $t("unit.year") + curSemester.name }}
 					<Icon type="md-arrow-dropdown" />
 				</span>
 				<DropdownMenu slot="list">
 					<div v-for="(item, index) in semesterList" :key="index">
 						<DropdownItem :name="index">
 							<div class="school-item">
-								<span :style="{ color: yearColorArr[getYearStatus(item.year)], fontWeight: getYearStatus(item.year) === 1 ? '600' : '400' }">{{ item.year + $t('unit.year') + item.name }}</span>
+								<span :style="{ color: yearColorArr[getYearStatus(item.year)], fontWeight: getYearStatus(item.year) === 1 ? '600' : '400' }">{{ item.year + $t("unit.year") + item.name }}</span>
 							</div>
 						</DropdownItem>
 					</div>
 				</DropdownMenu>
 			</Dropdown>
-      <span style="margin-left: 10px;cursor: pointer;" @click="goNowSemester()" v-if="!isNowSemester" title="回到当前学期">
-        <Icon type="md-sync" color="#16b1f3"/>
-      </span>
+			<span style="margin-left: 10px; cursor: pointer" @click="goNowSemester()" v-if="!isNowSemester" title="回到当前学期">
+				<Icon type="md-sync" color="#16b1f3" />
+			</span>
 		</div>
 	</div>
 </template>
@@ -68,7 +68,7 @@
 		inject: ["reload"],
 		data() {
 			return {
-        yearColorArr:['#969696','#16b1f3','#19be6b'],
+				yearColorArr: ["#969696", "#16b1f3", "#19be6b"],
 				curSchool: {
 					logo: ""
 				},
@@ -80,12 +80,12 @@
 				areaSchs: [],
 				areaList: [],
 				periods: [],
-        curYear:new Date().getFullYear(),
+				curYear: new Date().getFullYear(),
 				curPeriod: {
 					name: ""
 				},
-        curSemester:null,
-        nowSemesterIndex: -1
+				curSemester: null,
+				nowSemesterIndex: -1
 			};
 		},
 		created() {
@@ -129,20 +129,19 @@
 					// 保存Vuex
 					this.$store.commit("user/setCurPeriod", this.curPeriod);
 					// 保存Stroage
-          this.curSemester = this.getNowSemester()
-          console.error('默认设置学年期',this.curSemester)
+					this.curSemester = this.getNowSemester();
+					console.error("默认设置学年期", this.curSemester);
 					localStorage.setItem("curPeriod", JSON.stringify(this.curPeriod));
-          this.$store.commit("user/setCurSemester", this.curSemester);
+					this.$store.commit("user/setCurSemester", this.curSemester);
 				} else {
 					this.$store.commit("user/setCurPeriod", localPeriodInfo);
-          if(localSemester){
-            this.curSemester = localSemester
-            console.error('读取缓存学年期',localSemester)
-            this.$store.commit("user/setCurSemester", localSemester);
-          }
+					if (localSemester) {
+						this.curSemester = localSemester;
+						console.error("读取缓存学年期", localSemester);
+						this.$store.commit("user/setCurSemester", localSemester);
+					}
 				}
-        this.refreshNowSemesterIndex()
-
+				this.refreshNowSemesterIndex();
 			} else {
 				this.$Message.warning(this.$t("utils.noShoolTip"));
 			}
@@ -189,25 +188,25 @@
 				this.$store.commit("user/setCurPeriod", this.curPeriod);
 				// 保存当前学段的下标 提供给部分模块的select使用
 				this.curPeriod.periodIndex = val;
-        // 默认切换到当前学段下的当前学期
-        this.curSemester = this.getNowSemester();
-        this.$store.commit("user/setCurSemester", this.curSemester);
-        this.refreshNowSemesterIndex()
+				// 默认切换到当前学段下的当前学期
+				this.curSemester = this.getNowSemester();
+				this.$store.commit("user/setCurSemester", this.curSemester);
+				this.refreshNowSemesterIndex();
+			},
+			/* 学期选择 */
+			onSemesterSelect(val) {
+				this.curSemester = this.semesterList[val];
+				this.$store.commit("user/setCurSemester", this.curSemester);
+				console.error("xxxxxxxxxxxxxxxxxxx", this.$tools.getStTimeByYearAndSemester(this.curSemester.year, this.curSemester.index));
+			},
+			refreshNowSemesterIndex() {
+				let nowSemester = this.getNowSemester();
+				this.nowSemesterIndex = this.semesterList.findIndex((i) => i.year === nowSemester.year && i.id === nowSemester.id);
+			},
+			/* 切换到当前学期 */
+			goNowSemester() {
+				this.onSemesterSelect(this.nowSemesterIndex);
 			},
-      /* 学期选择 */
-      onSemesterSelect(val){
-        this.curSemester = this.semesterList[val];
-        this.$store.commit("user/setCurSemester", this.curSemester);
-        console.error('xxxxxxxxxxxxxxxxxxx',this.$tools.getStTimeByYearAndSemester(this.curSemester.year,this.curSemester.index))
-      },
-      refreshNowSemesterIndex(){
-        let nowSemester = this.getNowSemester()
-        this.nowSemesterIndex = this.semesterList.findIndex(i => i.year === nowSemester.year && i.id === nowSemester.id)
-      },
-      /* 切换到当前学期 */
-      goNowSemester(){
-        this.onSemesterSelect(this.nowSemesterIndex)
-      },
 			/* 学校切换后针对当前学校CODE进行操作 */
 			changeCurSchool(schoolCode, routerInfo) {
 				// this.$EventBus.$emit('onChangeSchool', {
@@ -256,15 +255,15 @@
 					}
 				});
 			},
-      getNowSemester(){
-        let semesterRange = this.$tools.getSemesterTimeRange();
-        return {
-          year:semesterRange.year,
-          name: semesterRange.name,
-          index:semesterRange.semesterIndex,
-          id:semesterRange.semesterId
-        }
-      },
+			getNowSemester() {
+				let semesterRange = this.$tools.getSemesterTimeRange();
+				return {
+					year: semesterRange.year,
+					name: semesterRange.name,
+					index: semesterRange.semesterIndex,
+					id: semesterRange.semesterId
+				};
+			}
 		},
 		mounted() {
 			// 解绑之前的事件 防止多次触发
@@ -300,39 +299,43 @@
 				// return requestSchools.length ? `${requestSchools[0].name}(${this.$t('settings.status3')})` : (inviteSchools.length ? `${inviteSchools[0].name}(${this.$t('settings.status2')})` : this.$t('utils.noJoinSchool'))
 				return requestSchools.length ? `${requestSchools[0].name}(${this.$t("settings.status3")})` : inviteSchools.length ? `${inviteSchools[0].name}(${this.$t("settings.status2")})` : "";
 			},
-      
-      /* 获取学期列表数据 */
-      semesterList(){
-        let curYear = new Date().getFullYear()
-        let semesterArr = this.curPeriod.semesters
-        let gradeCount = this.curPeriod.grades.length
-        let listYearCount = gradeCount + 1 // 可选年份范围是当前学段下年级数量 再往后推一年(可能会查看未来的排课表)
-				let oldYear = curYear - gradeCount
-				let arr = []
-				for (let i = 0; i <= listYearCount; i ++){
-          for(let j = 0; j < semesterArr.length; j++){
-            arr.push({
-              year:oldYear + i,
-              name: semesterArr[j].name,
-              index:j,
-              id:semesterArr[j].id
-            })
-          }
-				}
-        return arr
-      },
 
-      /* 判断过去、现在和未来 */
-      getYearStatus(){
-        return year => {
-          return year === this.curYear ? 1 : year < this.curYear ? 0 : 2
-        }
-      },
+			/* 获取学期列表数据 */
+			semesterList() {
+				let curYear = new Date().getFullYear();
+				let semesterArr = this.curPeriod.semesters;
+				let gradeCount = this.curPeriod.grades.length;
+				let listYearCount = gradeCount + 1; // 可选年份范围是当前学段下年级数量 再往后推一年(可能会查看未来的排课表)
+				let oldYear = curYear - gradeCount;
+				let arr = [];
+				for (let i = 0; i <= listYearCount; i++) {
+					for (let j = 0; j < semesterArr.length; j++) {
+						arr.push({
+							year: oldYear + i,
+							name: semesterArr[j].name,
+							index: j,
+							id: semesterArr[j].id
+						});
+					}
+				}
+				return arr;
+			},
 
-      /* 判断是否当前学期 */
-      isNowSemester(){
-        return this.nowSemesterIndex === this.semesterList.findIndex(i => i.year === this.curSemester.year && i.id === this.curSemester.id)
-      }
+			/* 判断过去、现在和未来 */
+			getYearStatus() {
+				return (year) => {
+					return year === this.curYear ? 1 : year < this.curYear ? 0 : 2;
+				};
+			},
+			/* 判断是否当前学期 */
+			isNowSemester() {
+				return this.nowSemesterIndex === this.semesterList.findIndex((i) => i.year === this.curSemester.year && i.id === this.curSemester.id);
+			},
+			/* 判断是否要显示学年期切换控件 */
+			showSemesterSelect() {
+				let routeArr = ["/home/CusMgt","/home/manageVote","/home/manageQuestionnaire"];
+				return routeArr.includes(this.$route.path);
+			}
 		}
 	};
 </script>
@@ -358,7 +361,6 @@
 			a {
 				color: var(--primary-textColor) !important;
 
-
 				&:hover {
 					color: var(--primary-textColor) !important;
 				}

+ 7 - 3
TEAMModelOS/ClientApp/src/common/QrcodeModal.vue

@@ -3,6 +3,8 @@
         <div class="qr-code-info" @click.stop>
             <p class="qr-code-title">
                 {{config.title}}
+            </p>
+            <p>
                 <slot name="switch"></slot>
             </p>
             <div id="qrcode" :class="showQrStatus ? 'animated fadeIn':'animated fadeOut'" ref="qrcode" style="padding:15px 25px 20px 25px;background-color:white;width:330px;margin:auto;">
@@ -132,8 +134,10 @@ export default {
     display: block;
     margin: auto;
     text-align: center;
-    color: black;
-    font-size: 18px;
+    color: #2d8cf0;
+    font-size: 20px;
+    font-weight: bold;
+    margin-bottom: 20px;
 }
 .qr-code-text {
     display: block;
@@ -142,7 +146,7 @@ export default {
     margin-top: -5px;
     margin-bottom: 5px;
     color: #0058e7;
-    font-size: 20px;
+    // font-size: 20px;
 }
 .invite-url-text {
     text-overflow: ellipsis;

+ 1 - 1
TEAMModelOS/ClientApp/src/components/evaluation/ExerciseList.vue

@@ -262,7 +262,7 @@ export default {
     this.pageSize = this.isAnalysis ? 999 : 5
     this.pageChange(1)
     this.getScatterData()
-    this.paperInfo = JSON.parse(localStorage.curExam)
+    this.paperInfo = localStorage.curExam ? JSON.parse(localStorage.curExam) : null
   },
   methods: {
     onPrintItem(e, index) {

+ 109 - 1
TEAMModelOS/ClientApp/src/router/routes.js

@@ -1426,6 +1426,60 @@ export const routes = [{
             activeName: 'ArtAssessment',
             middleware: ['login', 'ability:admin,art-upd'],
         }
+    },
+    // 报名系统:活动管理
+    {
+        path: 'activityManage',
+        name: 'activityManage',
+        component: () => import('@/view/signupActivity/manageActivity.vue'),
+        meta: {
+            activeName: 'activityManage',
+        },
+    },
+    // 报名系统:活动详情
+    {
+        path: 'infoActivity',
+        name: 'infoActivity',
+        component: () => import('@/view/signupActivity/infoActivity copy.vue'),
+        meta: {
+            activeName: 'infoActivity',
+        }
+    },
+    // 报名系统:创建活动
+    {
+        path: 'createActivity',
+        name: 'createActivity',
+        component: () => import('@/view/signupActivity/createActivity.vue'),
+        meta: {
+            activeName: 'createActivity',
+        }
+    },
+    // 报名系统:活动审核
+    {
+        path: 'activityProcess',
+        name: 'activityProcess',
+        component: () => import('@/view/signupActivity/processActivity.vue'),
+        meta: {
+            activeName: 'activityProcess',
+        }
+    },
+    // 报名系统:活动审核详情
+    {
+        path: 'infoProcess',
+        name: 'infoProcess',
+        component: () => import('@/view/signupActivity/infoActivity.vue'),
+        meta: {
+            activeName: 'infoProcess',
+        }
+    },
+    // 报名系统:基础设置
+    {
+        path: 'activitySet',
+        name: 'activitySet',
+        component: () => import('@/view/signupActivity/manageActivity.vue'),
+        meta: {
+            activeName: 'activityManage',
+        }
     }
     ]
 },
@@ -1532,7 +1586,7 @@ export const routes = [{
             // 错题本
             name: "practice",
             path: "practice",
-            // redirect: '/studentWeb/practice',
+            redirect: '/studentWeb/practice/WrongQues',
             component: () => import('@/components/student-web/WrongQusetion/Index'),
             children: [
                 {
@@ -1832,6 +1886,60 @@ export const routes = [{
                 activeName: 'mgtPlatform'
             }
         },
+        // 报名系统:活动管理
+        {
+            path: 'areaActivityManage',
+            name: 'areaActivityManage',
+            component: () => import('@/view/signupActivity/manageActivity.vue'),
+            meta: {
+                activeName: 'areaActivityManage',
+            },
+        },
+        // 报名系统:活动详情
+        {
+            path: 'areaInfoActivity',
+            name: 'areaInfoActivity',
+            component: () => import('@/view/signupActivity/infoActivity copy.vue'),
+            meta: {
+                activeName: 'areaInfoActivity',
+            }
+        },
+        // 报名系统:创建活动
+        {
+            path: 'areaCreateActivity',
+            name: 'areaCreateActivity',
+            component: () => import('@/view/signupActivity/createActivity.vue'),
+            meta: {
+                activeName: 'areaCreateActivity',
+            }
+        },
+        // 报名系统:活动审核
+        {
+            path: 'areaActivityProcess',
+            name: 'areaActivityProcess',
+            component: () => import('@/view/signupActivity/processActivity.vue'),
+            meta: {
+                activeName: 'areaActivityProcess',
+            }
+        },
+        // 报名系统:活动审核详情
+        {
+            path: 'areaInfoProcess',
+            name: 'areaInfoProcess',
+            component: () => import('@/view/signupActivity/infoActivity.vue'),
+            meta: {
+                activeName: 'areaInfoProcess',
+            }
+        },
+        // 报名系统:基础设置
+        {
+            path: 'areaActivitySet',
+            name: 'areaActivitySet',
+            component: () => import('@/view/signupActivity/manageActivity.vue'),
+            meta: {
+                activeName: 'areaActivityManage',
+            }
+        }
     ]
 },
 // 扫码签到

+ 20 - 0
TEAMModelOS/ClientApp/src/utils/loadmore.js

@@ -0,0 +1,20 @@
+export default {
+    bind(el, binding) {
+        // 获取iview-ui定义好的scroll盒子
+        const selectWrap = el.querySelector('.ivu-table-body')
+        selectWrap.addEventListener('scroll', function () {
+            /*
+             * scrollHeight 元素标签内容高度
+             * scrollTop 获取元素滚动的偏移值,
+             * clientHeight 元素标签的可见高度
+             * 如果元素滚动到底, 下面等式返回true, 没有则返回false:
+             * ele.scrollHeight - ele.scrollTop === ele.clientHeight;
+             */
+            let sign = 100
+            const scrollDistance = this.scrollHeight - this.scrollTop - this.clientHeight
+            if (scrollDistance <= sign) {
+                binding.value()
+            }
+        })
+    }
+}

+ 9 - 0
TEAMModelOS/ClientApp/src/utils/plugins.js

@@ -0,0 +1,9 @@
+//view-table加载更多
+import loadmore from './loadmore';
+
+export default {
+    async install (Vue, options) {
+        // 指令
+        Vue.directive('loadmore', loadmore);
+    }    
+}

+ 44 - 0
TEAMModelOS/ClientApp/src/view/areaMgmt/AreaLayout.vue

@@ -385,6 +385,50 @@ export default {
           isShow: !this.$jsFn.checkJinNiu() && !this.$jsFn.checkTrain(),
           to: 'privSokrate'
         },
+        // 活动报名
+        {
+          icon: "iconfont icon-yishu",
+          name: '活动报名',
+          router: "",
+          tag: "",
+          role: "admin",
+          permission: "art-read|art-upd",
+          subName: "activitySystem",
+          menuName: "activitySystem",
+          isShow: false,
+          child: [
+            {
+              icon: "iconfont icon-data-count",
+              name: '活动管理',
+              router: "/area/areaActivityManage",
+              tag: "",
+              role: "admin",
+              permission: "art-read|art-upd",
+              menuName: "areaActivityManage",
+              isShow: true,
+            },
+            {
+              icon: "iconfont icon-test",
+              name: '活动审核',
+              router: "/area/areaActivityProcess",
+              tag: "",
+              role: "admin",
+              permission: "art-read|art-upd",
+              menuName: "areaActivityProcess",
+              isShow: true,
+            },
+            /* {
+              icon: "iconfont icon-assess",
+              name: '基础设置',
+              router: "/area/areaActivitySet",
+              tag: "",
+              role: "admin",
+              permission: "art-read|art-upd",
+              menuName: "areaActivitySet",
+              isShow: true,
+            }, */
+          ],
+        },
         // 活动系统
         {
           icon: 'iconfont icon-myNote',

+ 4 - 4
TEAMModelOS/ClientApp/src/view/joinclass/JoinClass.vue

@@ -172,10 +172,10 @@ export default {
                             sessionStorage.setItem('identity', 'student')
                         } else if (res.status == 4) {// 个人名单未开放加入
                             this.$Message.warning(this.$t('cusMgt.join.joinLock'))
-                        } else if (res.status == 5) {// 没有开启审核模式
-                            this.$Message.warning('没有开启审核模式')
-                        } else if (res.status == 6) {// 人数已满,需要审核通过再加入
-                            this.$Message.warning('人数已满,需要审核通过再加入')
+                        } else if (res.status == 6) {// 需要审核通过再加入
+                            this.$Message.warning('需要审核通过再加入')
+                        } else if (res.status == 5) {// 人数已满,需要审核通过再加入
+                            this.$Message.warning('人数已满')
                         } else if (res.status == 7) {// 二维码设置已经过期
                             this.$Message.warning('二维码设置已经过期')
                         }

+ 116 - 14
TEAMModelOS/ClientApp/src/view/mycourse/MyCourse.vue

@@ -56,7 +56,7 @@
 						<vuescroll>
 							<div v-for="(item, index) in teaClassList" :key="index" @click="selectClass(index)" :class="['block-bg', 'tea-class-item', curClassIndex == index ? 'block-bg-active' : '']">
 								<p class="class-attr-item">
-									<span class="attr-label" v-show="listType === 'school'">{{ item.classId ? $t("cusMgt.listType1") : $t("cusMgt.listType2") }}: </span>
+									<span class="attr-label" v-show="listType === 'school'">{{ item.classId ? $t("cusMgt.listType1") : $t("cusMgt.listType2") }}:</span>
 									<span class="attr-label" v-show="listType === 'private'">{{ $t("cusMgt.nameList") }}:</span>
 									<span class="class-name">{{ item.classId ? item.classInfo.name : item.listName || "--" }}</span>
 									<Tag v-if="item.graduate === 1" color="gold">
@@ -68,10 +68,10 @@
 									<span class="attr-label">{{ $t("cusMgt.stuCount") }}</span>
 									<span class="class-name">{{ getStudenCount(item.classId || item.stulist) }}{{ $t("unit.text7") }}</span>
 								</p>
-								<Icon size="25" custom="iconfont icon-qr-code" :class="['qr-code-icon', { 'qr-code-icon-color': item.joinLock }]" @click="showQrCode(index)" v-if="listType == 'private'" :title="$t('cusMgt.qrCodeLabel')" />
+								<Icon size="25" custom="iconfont icon-qr-code" :class="['qr-code-icon', item.joinLock ? 'qr-code-icon-color' : '']" @click="showQrCode(index)" v-if="listType == 'private'" :title="$t('cusMgt.qrCodeLabel')" />
 								<span v-if="hasCCAuth" :class="['qr-code-icon', hasCCAuth ? 'cc-icon' : 'no-cc-auth']" @click="startCus(index)"> CC </span>
 							</div>
-							<EmptyData v-if="teaClassList.length == 0" :top="160" :textContent="$t('cusMgt.noClassList')"></EmptyData>
+							<EmptyData v-if="teaClassList.length == 0" :top="160" :textContent="$t('cusMgt.noClassList')"> </EmptyData>
 						</vuescroll>
 					</div>
 				</div>
@@ -102,24 +102,57 @@
 						<span @click="selectTab('discussion')" :class="tabName == 'discussion' ? 'course-classroom-label pane active' : 'course-classroom-label pane'">
 							{{ $t("studentWeb.type.discussionBoard") }}
 						</span>
+						<span @click="selectTab('score')" :class="tabName == 'score' ? 'course-classroom-label pane active':'course-classroom-label pane'">
+							{{$t('cusMgt.cusTab7')}}
+						</span>
 					</div>
 					<!-- 评测活动 -->
-					<Exam v-if="tabName == 'exam'" v-model="fIds" :gradeParams="gradeParams" :students="students" :classInfo="teaClassList[curClassIndex]" :courseInfo="courseInfo" :teaClassList="teaClassList"></Exam>
+					<Exam v-if="tabName == 'exam'" v-model="fIds" :gradeParams="gradeParams" :students="students" :classInfo="teaClassList[curClassIndex]" :courseInfo="courseInfo" :teaClassList="teaClassList"> </Exam>
 					<Homework v-else-if="tabName == 'hw'" v-model="fIds" :classInfo="teaClassList[curClassIndex]" :courseInfo="courseInfo"></Homework>
 					<Survey v-else-if="tabName == 'survey'" v-model="fIds" :classInfo="teaClassList[curClassIndex]" :courseInfo="courseInfo"></Survey>
 					<Vote v-else-if="tabName == 'vote'" v-model="fIds" :classInfo="teaClassList[curClassIndex]" :courseInfo="courseInfo"></Vote>
 					<Record v-else-if="tabName == 'record'" v-model="fIds" :rcdParams="rcdParams" :isFull="isFull"></Record>
-					<Student v-else-if="tabName == 'student'" :classInfo="teaClassList[curClassIndex]" :stuList="curStuList" @on-del-student="onDelStudent" @on-add-student="onAddStudent" @on-update-students="onSetIrs"></Student>
+					<Student v-else-if="tabName == 'student'" :classInfo="teaClassList[curClassIndex]" :stuList="curStuList" @on-del-student="onDelStudent" @on-add-student="onAddStudent" @on-update-students="onSetIrs"> </Student>
 					<Notice v-else-if="tabName == 'notice'" :classInfo="teaClassList[curClassIndex]" :classList="teaClassList" :courseInfo="courseInfo"></Notice>
 					<Discussion v-else-if="tabName == 'discussion'" :classInfo="teaClassList[curClassIndex]" :classList="teaClassList" :courseInfo="courseInfo"></Discussion>
+					<Score v-else-if="tabName == 'score'" :gradeParams="gradeParams" :grouplistId="teaClassList[curClassIndex]" :listType="listType"></Score>
 				</div>
 			</Split>
 		</div>
 		<QrcodeModal v-model="showQrStatus" :config="qrConfig">
-			<span style="margin-left: 10px" slot="switch">
+			<div style="font-size: 14px;padding: 0 20px" slot="switch" v-if="showQrStatus">
 				<!-- <span>{{ agreeJoin ? '已同意扫码加入' : '禁止扫码加入' }}</span> -->
-				<i-switch v-model="agreeJoin" size="small" @on-change="agreeJoinClass" />
-			</span>
+				<!-- <i-switch v-model="agreeJoin" size="small" @on-change="agreeJoinClass" /> -->
+				<p style="display: flex;justify-content:space-between;margin:15px 0">
+					<span>
+						允许加入
+						<i-switch v-model="agreeJoin" size="small" @on-change="agreeJoinClass" />
+					</span>
+					
+					<span v-if="agreeJoin">
+						最多人数限制
+						<InputNumber :max="999" :min="0" size="small" v-model="qrSetting.limitCount" style="width:70px;margin-left: 5px;" @on-blur="onSettingChange"></InputNumber>
+					</span>
+				</p>
+				<p style="display: flex;justify-content:space-between;margin:15px 0" v-if="agreeJoin">
+					<span>
+						开启审核
+						<i-switch v-model="qrSetting.review" size="small" @on-change="onSettingChange" />
+					</span>
+					<span v-if="qrSetting.review">
+						二维码有效期
+						<Select v-model="qrSetting.qrcodeDays" size="small" style="width:70px;margin-left: 5px;" @on-change="onSettingChange">
+							<Option :value="1" :key="1">1天</Option>
+							<Option :value="3" :key="3">3天</Option>
+							<Option :value="7" :key="7">7天</Option>
+						</Select>
+					</span>
+				</p>
+				<p style="display:flex;color:rgba(236, 69, 69, 0.945);font-weight: bold;font-size: 14px" v-if="qrSetting.review && qrSetting.qrcodeDays > 0 && agreeJoin">
+					<span>二维码状态:</span>
+					<BaseCountDown :timestamp="qrSetting.qrcodeExpire"></BaseCountDown>
+				</p>
+			</div>
 			<!-- 支持自定义内容 -->
 			<p class="qr-code-text" slot="tips-content">
 				(<span style="font-size: 17px">{{ $t("cusMgt.qrCodeText") }}</span> {{ stuListNo }})
@@ -209,7 +242,7 @@
 					</FormItem>
 					<FormItem :label="$t('cusMgt.listLabel')" v-else-if="type == 'select'">
 						<Select v-model="listId">
-							<Option v-for="(item, index) in groupList" :value="item.id" :key="index">{{ item.name }}</Option>
+							<Option v-for="(item, index) in groupList" :value="item.id" :key="index">{{ item.name }} </Option>
 						</Select>
 					</FormItem>
 					<FormItem :label="$t('cusMgt.listLabel')" v-else-if="type == 'school'">
@@ -254,6 +287,7 @@
 	import Student from "./student/Student.vue";
 	import Notice from "./notice/Notice.vue";
 	import Discussion from "./discussion/Discussion.vue";
+	import Score from "./score/Score.vue"
 	export default {
 		components: {
 			Exam,
@@ -263,7 +297,8 @@
 			Record,
 			Student,
 			Notice,
-			Discussion
+			Discussion,
+			Score
 		},
 		data() {
 			// 验证只能是字母和数字
@@ -276,6 +311,13 @@
 				}
 			};
 			return {
+				expireTime:1595656565,
+				qrSetting:{
+					review:false,
+					limitCount:200,
+					qrcodeDays: 1,
+					qrcodeExpire:0
+				},
 				schoolClassInfo: [],
 				isGraduate: false,
 				pdfList: [
@@ -412,6 +454,7 @@
 			//课程名单 —— 去重处理后的数据
 			teaClassList() {
 				if (this.courseInfo && this.courseInfo.schedule) {
+					console.error(this.courseInfo.schedule)
 					//一个名单被安排到不同教室上课会出现重复数据,这里需要去重
 					let list = [];
 					this.courseInfo.schedule.forEach((item) => {
@@ -458,6 +501,46 @@
 			}, 100);
 		},
 		methods: {
+			onSettingChange(){
+				let stulist = this.courseGroupList.find((item) => {
+					return item.id == this.teaClassList[this.curClassIndex].stulist;
+				});
+				let groupIndex = this.courseGroupList.findIndex((item) => {
+					return item.id == this.teaClassList[this.curClassIndex].stulist;
+				});
+				if (stulist) {
+					console.log(stulist)
+					let params = stulist;
+					params.review = this.qrSetting.review ? 1 : 0;
+					params.qrcodeDays = this.qrSetting.qrcodeDays || 1;
+					params.limitCount = this.qrSetting.limitCount;
+					this.listLoading = true;
+					this.$api.common
+						.upsertGroupInfo(params)
+						.then(
+							(res) => {
+								this.qrSetting = {
+									review: res.list.review ? true : false,
+									limitCount: res.list.limitCount,
+									qrcodeDays: res.list.qrcodeDays || 1,
+									qrcodeExpire:res.list.qrcodeExpire
+								}
+								this.teaClassList[this.curClassIndex].review = res.list.review
+								this.teaClassList[this.curClassIndex].limitCount = res.list.limitCount
+								this.teaClassList[this.curClassIndex].qrcodeDays = res.list.qrcodeDays
+								this.teaClassList[this.curClassIndex].qrcodeExpire = res.list.qrcodeExpire
+								console.log(this.qrSetting)
+								this.$Message.info(this.$t("cusMgt.updOk"));
+							},
+							(err) => {
+								this.$Message.error(this.$t("cusMgt.updOk"));
+							}
+						)
+						.finally(() => {
+							this.listLoading = false;
+						});
+				}
+			},
 			handleSelectClass(data) {
 				console.log(data);
 				this.schoolClassInfo = data;
@@ -598,6 +681,10 @@
 				if (stulist) {
 					stulist.joinLock = status ? 1 : 0;
 					let params = stulist;
+					params.review = 0
+					params.qrcodeDays = 1
+					params.qrcodeExpire = 0
+					params.limitCount = 200
 					this.listLoading = true;
 					this.$api.common
 						.upsertGroupInfo(params)
@@ -616,13 +703,20 @@
 				}
 			},
 			async showQrCode(index) {
-				console.log(this.courseInfo)
+				console.log(this.courseInfo);
+				console.log(this.teaClassList[index]);
+				this.qrSetting = {
+					review: this.teaClassList[index].review ? true : false,
+					limitCount: this.teaClassList[index].limitCount,
+					qrcodeDays: this.teaClassList[index].qrcodeDays || 1,
+					qrcodeExpire:this.teaClassList[index].qrcodeExpire
+				}
 				let loginUrl = window.location.host;
 				let tName = this.$store.state.userInfo.name;
 				let listId = this.teaClassList[index].stulist;
 				let listName = this.teaClassList[index].listName;
 				let cusName = this.courseInfo.name;
-        		let cusDesc = this.courseInfo.desc;
+				let cusDesc = this.courseInfo.desc || this.$t('evaluation.noData');
 				let cusId = this.courseInfo.id;
 				let stulistInfo = this.courseGroupList.find((item) => {
 					return item.id == listId;
@@ -638,7 +732,7 @@
 					// 处理分享内容
 					try {
 						let shortUrl = await this.$api.getShortUrl(encodeURI(this.inviteUrl));
-						let shareText = `${this.$t("cusMgt.inviteInfo1")}\n\n${this.$t("cusMgt.inviteInfo2")}${cusName}\n${this.$t("cusMgt.inviteInfo3")}${listName}\n${this.$t("cusMgt.inviteInfo4")}${tName}\n${this.$t('cusMgt.cusDesc')}:${cusDesc}\n\n${this.$t("cusMgt.inviteInfo5")}\n${shortUrl.result || encodeURI(this.inviteUrl)}\n\n${this.$t("cusMgt.inviteInfo6")}\nURL:https://${loginUrl}/login/student\n${this.$t("cusMgt.inviteInfo7")}${this.stuListNo}`;
+						let shareText = `${this.$t("cusMgt.inviteInfo1")}\n\n${this.$t("cusMgt.inviteInfo2")}${cusName}\n${this.$t("cusMgt.inviteInfo3")}${listName}\n${this.$t("cusMgt.inviteInfo4")}${tName}\n${this.$t("cusMgt.cusDesc")}:${cusDesc}\n\n${this.$t("cusMgt.inviteInfo5")}\n${shortUrl.result || encodeURI(this.inviteUrl)}\n\n${this.$t("cusMgt.inviteInfo6")}\nURL:https://${loginUrl}/login/student\n${this.$t("cusMgt.inviteInfo7")}${this.stuListNo}`;
 						this.qrConfig.shareContent = shareText;
 					} catch (e) {
 						this.qrConfig.shareContent = "";
@@ -1134,7 +1228,10 @@
 												item.listName = listInfo ? listInfo.name : this.$t("cusMgt.hasDelClass");
 												item.listSchool = listInfo ? listInfo.school : undefined;
 												item.joinLock = listInfo?.joinLock;
-												item.graduate = listInfo?.graduate;
+												item.review = listInfo?.review;
+												item.qrcodeDays = listInfo?.qrcodeDays;
+												item.qrcodeExpire = listInfo?.qrcodeExpire;
+												item.limitCount = listInfo?.limitCount;
 											}
 											//统一数据格式
 											item.classId = item.classId || undefined;
@@ -1273,13 +1370,16 @@
 		.ivu-radio-group-button .ivu-radio-wrapper:first-child {
 			border: none;
 		}
+
 		.ivu-radio-group-button .ivu-radio-wrapper:after {
 			display: none;
 		}
+
 		.ivu-radio-wrapper {
 			margin-right: 5px;
 			border: none;
 			box-shadow: none;
+
 			.ivu-icon {
 				margin-bottom: 2px;
 			}
@@ -1295,11 +1395,13 @@
 			box-shadow: none !important;
 			color: white !important;
 		}
+
 		.ivu-radio-group-button .ivu-radio-wrapper {
 			height: 28px;
 			line-height: 26px;
 		}
 	}
+
 	.ivu-radio-wrapper-checked .cus-action-icon-wrap {
 		display: inline-block;
 	}

+ 255 - 0
TEAMModelOS/ClientApp/src/view/mycourse/score/AddProject.less

@@ -0,0 +1,255 @@
+.score-container {
+    position: absolute;
+    top: 45px;
+    bottom: 0;
+    left: 0;
+    right: 0;
+    display: flex;
+}
+
+.absolute {
+    /* 設置為絕對定位 */
+    position: absolute;
+    top: 0;
+    bottom: 0;
+    left: 0;
+    right: 0;
+}
+
+.center {
+    text-align: center;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+}
+
+.left {
+    width: 250px;
+    //border: 1px solid #d7dde4;   
+    padding: 10px;
+    background: #f2f2f2;
+    position: relative;
+    border-radius: 4px;
+
+    .row {
+        overflow: auto;
+        top: 10px;
+        bottom: 10px;
+        padding: 10px;
+        // height: 75vh;
+
+        .card {
+            cursor: pointer;
+        }
+
+        .tableNameClass {
+            font-weight: bold;
+            //font-size: 16px;
+            //display: grid;
+            //place-items: center;
+            margin: 0px 10px 10px 10px;
+        }
+
+        .card,
+        .card0 {
+            margin: 5px;
+            background-color: white;
+            padding: 15px 10px;
+            border-radius: 10px;
+            display: flex;
+            justify-content: space-between;
+            font-weight: 900;
+        }
+
+        .card:hover,
+        .icon-card:hover,
+        .card.active {
+            border: 2px solid #2b85e4;
+        }
+
+        .icon-card {
+            margin: 20px 5px;
+            background-color: white;
+            padding: 5px;
+            border-radius: 10px;
+            display: flex;
+            justify-content: center;
+            align-items: center;
+            cursor: pointer;
+        }
+
+        .icon-card2 {
+            margin: 20px 5px;
+            border-radius: 10px;
+            display: flex;
+            justify-content: center;
+            align-items: center;
+        }
+    }
+}
+
+.save-info-ok {
+    text-align: right;
+    color: #808695;
+    margin-bottom: 10px;
+}
+
+.save-info-erro {
+    text-align: right;
+    color: #ed4014;
+    margin-bottom: 10px;
+}
+
+.right {
+    flex: 1;
+    background: hsl(0, 0%, 95%);
+    display: flex;
+    padding: 10px;
+
+    .content {
+        background: white;
+        padding: 10px;
+        margin: 10px 0;
+        position: relative;
+        width: 100%;
+
+        /* 加入絕對定位 */
+        //overflow:scroll;
+        .head {
+            //height: 10%;            
+            display: flex;
+
+            .txtInput-div {
+                flex: 2;
+                display: flex;
+                align-items: center;
+                margin-bottom: 10px;
+
+                .label {
+                    text-align: left;
+                    margin-right: 6px;
+                }
+
+                input {
+                    color: #2d8cf0;
+                    margin: 0px 5px;
+                    padding: 2px;
+                    border: 1px solid #e8eaec;
+                    box-shadow: none;
+                    text-align: right;
+                    width: 200px;
+                }
+
+                .content-input {
+                    text-align: left;
+                    margin-left: auto;
+                }
+
+                .none-input {
+                    border: none;
+                    pointer-events: none;
+                    color: black;
+                }
+
+                .input-container {
+                    white-space: nowrap;
+                }
+
+                .input2 {
+                    width: 40px;
+                }
+            }
+
+            .down {
+                display: flex;
+                flex-direction: column;
+                justify-content: flex-end;
+
+                button {
+                    position: relative;
+                    z-index: 9999;
+                }
+            }
+
+            .button-div {
+                display: flex;
+                justify-content: flex-start;
+            }
+        }
+
+        .body {
+            top: 55px;
+            padding: 10px;
+
+            .title {
+                background-color: #f2f2f2;
+                padding: 2px;
+            }
+
+            .felx1{
+                flex: 1 1 0%;
+            }
+            .felx2{
+                flex: 2 1 0%;
+            }
+            .felxAuto{
+                flex: 1 1 auto;
+            }
+
+            .content-in {
+                top: 60px;
+                right: 10px;
+                bottom: 10px;
+                padding: 10px 10px;
+                overflow: auto;
+
+                .add-card {
+                    margin-top: 15px;
+                    justify-content: center;
+                    cursor: pointer;
+                }
+
+                .card {
+                    border: 1px solid #e8eaec;
+                    display: flex;
+                    border-radius: 5px;
+                    padding: 5px;
+                    margin-bottom: 5px;
+
+                    input,
+                    .ivu-input {
+                        color: #2d8cf0;
+                        border: 1px solid #e8eaec;
+                        box-shadow: none;
+                        text-align: center;
+                        width: 100%;
+                        border-radius: 5px;
+                    }
+
+                    .average-div {
+                        text-align: center;
+                        height: 100%;
+
+                        .average {
+                            background-color: rgb(255, 217, 160);
+                            padding: 0px 10px;
+                            border-radius: 2px;
+                            font-size: 5px;
+                        }
+                    }
+
+                    .btn-left {
+                        display: flex;
+                        justify-content: start;
+                        margin-left: 5px;
+                    }
+
+                    .ivu-btn {
+                        height: auto;
+                    }                    
+                }
+            }
+        }
+    }
+
+}

+ 975 - 0
TEAMModelOS/ClientApp/src/view/mycourse/score/AddProject.vue

@@ -0,0 +1,975 @@
+<template>
+  <div class="score-container">
+    <div class="left">
+      <div class="row absolute">
+        <div class="tableNameClass">{{ tableInfo.tableName }}</div>
+        <div class="card" @click="changeItem(0)" :class="{ active: getProjectDatasClick(0) }">
+          <div>{{ getProjectName(0) }}</div>
+          <div style="margin-right: 20px">{{ getPercentage(0) }}%</div>
+        </div>
+        <div class="card" @click="changeItem(1)" :class="{ active: getProjectDatasClick(1) }">
+          <div>{{ getProjectName(1) }}</div>
+          <div style="margin-right: 20px">{{ getPercentage(1) }}%</div>
+        </div>
+        <div class="card" @click="changeItem(2)" :class="{ active: getProjectDatasClick(2) }">
+          <div>{{ getProjectName(2) }}</div>
+          <div style="margin-right: 20px">{{ getPercentage(2) }}%</div>
+        </div>
+        <div v-if="projectDatas.length > 3" style="display: flex; justify-content: center; align-items: center">
+          <Icon type="md-add" :size="20" />
+        </div>
+        <div class="card" v-for="(projectData, index) in projectDatas.slice(3)" :class="{
+          active: projectDatas[index + 3].click ? projectDatas[index + 3].click : false,
+        }" draggable="true" @dragstart="dragStart($event, index + 3)" @click="changeItem(index + 3)"
+          @dragover="allowDrop" @drop="drop($event, index + 3, 'projectDatas')" v-bind:key="index + 3" @dragend="dragEnd">
+          <!--這邊待新增程式判斷-->
+          <div>{{ projectData.projectName }}</div>
+          <div style="display: flex">
+            <div>{{ projectData.percentage }}%</div>
+            <div>
+              <Icon type="ios-trash" size="20" style="cursor: pointer" @click.stop="delProject(index)" />
+            </div>
+          </div>
+        </div>
+        <span v-if="projectDatas.length > 4" style="font-size: 6px">
+          {{ $t("scoreCalc.dropSort") }}
+        </span>
+
+        <div class="icon-card" @click="addProject">
+          <div class="add">
+            <Icon type="md-add-circle" :size="25" />
+          </div>
+        </div>
+        <div class="icon-card2">
+          <Icon type="md-pause" :size="25" color="#2b85e4" />
+        </div>
+        <div class="card0">
+          <div>
+            {{ $t("scoreCalc.totalRate") }}
+          </div>
+          <div v-if="totalNum == 100">{{ totalNum }}%</div>
+          <div v-if="totalNum != 100" style="color: #ed4014">
+            <Icon type="md-alert" />{{ totalNum }}%
+          </div>
+        </div>
+      </div>
+    </div>
+    <div class="right">
+      <div class="content">
+        <div class="head">
+          <div class="txtInput-div">
+            <div class="input-container">
+              <div class="label">{{ $t("scoreCalc.itemName") }}</div>
+              <input v-if="thisClassData.index <= 2" type="text" v-model="thisClassData.projectName" maxlength="20"
+                class="content-input none-input" />
+              <input v-if="thisClassData.index > 2" type="text" v-model="thisClassData.projectName" maxlength="20"
+                class="content-input" @input="updateInputprojectName" />
+            </div>
+            <div class="input-container">
+              <div class="label" style="margin-right: 26px">
+                {{ $t("scoreCalc.percentage") }}
+              </div>
+              <div class="input-container" style="margin-right: 10px">
+                <input type="text" v-model="thisClassData.percentage" maxlength="2" class="input2"
+                  @input="updateInputpercentage" />
+                <span>%</span>
+              </div>
+            </div>
+          </div>
+          <div class="down">
+            <div v-if="isSave" class="save-info-ok">
+              <Icon type="md-checkmark-circle" />{{ $t("scoreCalc.isSave") }}
+            </div>
+            <div v-if="!isSave" class="save-info-erro">
+              <Icon type="md-alert" />{{ $t("scoreCalc.notSave") }}
+            </div>
+            <div class="button-div">
+              <Button type="text" style="z-index: 500" @click="ProjectGoback('back')">{{
+                $t("scoreCalc.back")
+              }}</Button>
+              <Button type="primary" style="z-index: 500" @click="ProjectGoback('save')">{{ $t("scoreCalc.save")
+              }}</Button>
+            </div>
+          </div>
+        </div>
+        <div class="body absolute">
+          <span style="font-size: 6px"> {{ $t("scoreCalc.tickIncludedScore") }} {{
+            $t("scoreCalc.dropSort")
+          }}</span>
+          <div>
+            <Row class="title">
+              <Col style="flex: 0 0 40px;">
+              </Col>
+              <Col class="felx1">{{ $t("scoreCalc.itemName") }}</Col>
+              <Col class="felx1">{{ $t("scoreCalc.itemRate") }}</Col>
+            </Row>
+            <div class="content-in absolute">
+              <div v-for="(classData, index) in classDatas" draggable="true" @dragstart="dragStart($event, index)"
+                @dragover="allowDrop" @drop="drop($event, index, 'classDatas')" v-bind:key="index" @dragend="dragEnd">
+                <Row class="card">
+                  <Col flex="40px" style="flex: 0 0 40px;" class="center">
+                  <Checkbox v-model="classData.single"></Checkbox>
+                  </Col>
+                  <Col flex="1"  class="felx1">
+                  <Row>
+                    <Col flex="2" class="felx2" style="display: flex; align-items: center;">
+                    <div v-if="!classData.custom" class="center">
+                      {{ classData.className }}
+                    </div>
+                    <!-- <Input type="textarea" :value="classData.className" :autosize="{ minRows: 1, maxRows: 2 }" -->
+                    <Input type="textarea" v-model="classData.className" :autosize="{ minRows: 1, maxRows: 2 }"
+                      v-if="classData.custom" maxlength="60" @input="isInput" />
+                    <!-- <input type="text" :value="classData.className" v-if="classData.custom" maxlength="40"
+                        @input="isInput" /> -->
+                    </Col>
+                    <Col flex="1" class="felx1" v-if="!classData.islessonrecord">
+                    <div class="average-div center">
+                      <span class="average">{{ $t("scoreCalc.average") }}{{ classData.score }}分</span>
+                    </div>
+                    </Col>
+                  </Row>
+                  </Col>
+                  <Col flex="1" class="felx1" style="display: flex; align-items: center;">
+                  <Row :wrap="false" >
+                    <Col flex="50px" style="flex: 0 0 50px;">
+                    <input type="text" v-model="classData.proportion" maxlength="1" @input="isInput" />
+                    </Col>
+                    <Col flex="auto" class="btn-left center felxAuto">
+                    <Button type="primary" @click="
+                      keyScore(
+                        classData.id,
+                        classData.islessonrecord,
+                        classData.className
+                      )
+                      ">{{ $t("scoreCalc.logStudentScore") }}</Button>
+                    </Col>
+                    <Col flex="auto" class="center">
+                    <Icon type="ios-trash" size="20" v-if="classData.custom" style="cursor: pointer"
+                      @click="delClass(classData.id)" />
+                    </Col>
+                  </Row>
+                  </Col>
+                </Row>
+              </div>
+              <div class="card add-card" @click="addClass">
+                <Icon type="md-add-circle" :size="25" />
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+    <DialogBoxStudentScore :modalVisible="modalVisibleStudentScore" :className="className"
+      @closeModal="modalVisibleStudentScoreFun" :class_type="class_type" :classDataprop="classDataprop"
+      @updateItemScore="updateItemScore">
+    </DialogBoxStudentScore>
+    <Loading :top="100" v-show="dataLoading" type="1"></Loading>
+
+    <!-- <div :top="100" v-show="dataLoading" style="background: red; width: 300px;height: 300px;" ></div>  -->
+    <!-- <div :top="100"  style="background: red; width: 800px;height: 300px;display: none;" ></div>  -->
+  </div>
+</template>
+<script>
+import DialogBoxStudentScore from "./DialogBoxStudentScore.vue";
+import Loading from "@/common/Loading.vue";
+export default {
+  components: {
+    DialogBoxStudentScore,
+  },
+  props: {
+    //projectName: String, //課堂classRecord  作業homeWork 評量evaluate
+    tableInfo: Object, //
+    /*
+      tableInfo.tableName 表單名稱
+      tableInfo.tableId 表單ID
+      tableInfo.listId 表單項目ID
+    */
+    value: {
+      default: () => {
+        return [];
+      },
+    },
+  },
+  data() {
+    return {
+      isSave: true,
+      tempNum: 0,
+      filterExpire: false,
+      modalVisibleStudentScore: false,
+      class_type: "", //兩種型態:課堂紀錄 非課堂紀錄
+      thisClassData: { index: 0, projectName: "", percentage: 0 }, //當前顯示的資料
+      className: "", //課程名稱
+      projectDatas: [], // 左邊項目欄繫結用的資料
+      classDatas: [], // 右邊子項目欄繫結用的資料
+      calcData: {}, // 原始資料
+      selectIndex: 0, // 選中的項目索引
+      classData: [],
+      classDataprop: [],
+      inputValFromChild: "",
+      selectClassId: "",
+      selectClassIndex: "",
+      dataLoading: false,
+    };
+  },
+  computed: {
+    totalNum() {
+      let sum = 0;
+      this.projectDatas.forEach((element) => {
+        sum += parseInt(element.percentage, 10);
+      });
+      return sum;
+    },
+  },
+  mounted() {
+    //↓根據點進來的按鈕去撈資料庫,並改變顯示的資料。
+    //請改用tableInfo接資料
+    console.log("tableInfo=" + JSON.stringify(this.tableInfo));
+    this.getActList(this.tableInfo.listId);
+  },
+  methods: {
+    ProjectGoback(vule) {
+      if (vule == "save") {
+        if (this.totalNum > 100) {
+          this.$Message.warning(this.$t("scoreCalc.warringTotalRate"));
+        } else {
+          //#region 更新項目及子項目資料
+          let items = [];
+          let sort = 1;
+          this.classDatas.forEach((element) => {
+            let item = {
+              id: element.id,
+              name: element.className,
+              rate: element.proportion,
+              use: element.single,
+              sort: sort,
+            };
+            items.push(item);
+            sort = sort + 1;
+          });
+
+          let postData = {
+            scoreCalcActId: this.projectDatas[this.selectIndex].id,
+            teammodelId: this.$store.state.userInfo.TEAMModelId,
+            name: this.projectDatas[this.selectIndex].projectName,
+            rate: this.projectDatas[this.selectIndex].percentage,
+            items: items,
+          };
+          this.$api.learnActivity.updateScoreCalcAct(postData).then(
+            (res) => {
+              if (res) {
+                this.getActList(this.projectDatas[this.selectIndex].id);
+                this.$Message.info(this.$t("scoreCalc.dataIsSave"));
+              }
+            },
+            (err) => {
+              this.$Message.error(this.$t("cusMgt.gradeErr"));
+            }
+          );
+          this.isSave = true;
+          //#endregion
+        }
+      } else if (vule == "back") {
+        //直接返回
+        this.$emit("tableInfo", this.tableInfo);
+        this.$emit("changePage", "AllScore");
+      }
+    },
+    // 切換項目動作
+    changeItem(index) {
+      this.dataLoading = true;
+      // 要先判斷是否有修改
+      if (!this.isSave && index !== this.selectIndex) {
+        // 有修改的話要跳提示 會還原
+        this.$Modal.confirm({
+          title: this.$t("scoreCalc.remind"),
+          content: this.$t("scoreCalc.remindMessage1"),
+          onOk: async () => {
+            // 確定要不儲存切換
+            let listId = this.projectDatas[index].id;
+            this.projectDatas = [];
+            this.calcData.scoreCalcAct.forEach((element) => {
+              // 比對是否為選中的活動
+              if (listId === element.id) {
+                this.projectDatas.push({
+                  click: true,
+                  projectName: element.name,
+                  percentage: element.rate,
+                  id: element.id,
+                });
+
+                this.reloadProject(index);
+              } else {
+                this.projectDatas.push({
+                  click: false,
+                  projectName: element.name,
+                  percentage: element.rate,
+                  id: element.id,
+                });
+              }
+            });
+            this.isSave = true;
+          },
+          onCancel: () => {
+            // 不切換
+            //this.$Message.info('Clicked cancel');
+          },
+        });
+      } else {
+        this.reloadProject(index);
+      }
+      this.dataLoading = false;
+    },
+    // 重新整理選中的項目資料
+    reloadProject(index) {
+      this.selectIndex = index;
+      this.classDatas = [];
+      for (let i = 0; i < this.projectDatas.length; i++) {
+        this.projectDatas[i].click = false;
+        if (i === index) {
+          this.projectDatas[i].click = true;
+        }
+      }
+      // 設定右邊欄上方資料
+      this.thisClassData.index = index;
+      this.thisClassData.projectName = this.projectDatas[index].projectName;
+      this.thisClassData.percentage = this.projectDatas[index].percentage;
+
+      // 根據選中的項目刷新右邊欄資料
+      this.calcData.scoreCalcAct.forEach((element) => {
+        if (this.projectDatas[index].id === element.id) {
+          this.setSubListData(element);
+        }
+      });
+    },
+    // 增加一個自訂項目
+    addProject() {
+      // 要先判斷是否有修改
+      if (!this.isSave) {
+        // 有修改的話要跳提示 會還原
+        this.$Modal.confirm({
+          title: this.$t("scoreCalc.remind"),
+          content: this.$t("scoreCalc.remindMessage2"),
+          onOk: async () => {
+            // 確定要不儲存
+            this.dataLoading = true;
+            let postData = {
+              scorecalcId: this.tableInfo.tableId,
+              teammodelId: this.$store.state.userInfo.TEAMModelId,
+            };
+            this.$api.learnActivity.addScoreCalcAct(postData).then(
+              (res) => {
+                if (res) {
+                  this.calcData.scoreCalcAct.push(res);
+                  // 新增完成 清空左邊欄 重新塞資料
+                  this.projectDatas = [];
+                  this.calcData.scoreCalcAct.forEach((element) => {
+                    this.projectDatas.push({
+                      click: true,
+                      projectName: element.name,
+                      percentage: element.rate,
+                      id: element.id,
+                    });
+                  });
+                  this.reloadProject(this.projectDatas.length - 1);
+                }
+              },
+              (err) => {
+                this.$Message.error(this.$t("cusMgt.gradeErr"));
+              }
+            );
+            this.isSave = true;
+            this.dataLoading = false;
+          },
+          onCancel: () => {
+            // 不切換
+            //this.$Message.info('Clicked cancel');
+          },
+        });
+      } else {
+        this.dataLoading = true;
+        let postData = {
+          scorecalcId: this.tableInfo.tableId,
+          teammodelId: this.$store.state.userInfo.TEAMModelId,
+        };
+        this.$api.learnActivity.addScoreCalcAct(postData).then(
+          (res) => {
+            if (res) {
+              this.calcData.scoreCalcAct.push(res);
+              this.projectDatas.push({
+                click: true,
+                projectName: res.name,
+                percentage: res.rate,
+                id: res.id,
+              });
+              this.reloadProject(this.projectDatas.length - 1);
+            }
+          },
+          (err) => {
+            this.$Message.error(this.$t("cusMgt.gradeErr"));
+          }
+        );
+        this.dataLoading = false;
+      }
+    },
+    // 刪除一個自訂項目
+    delProject(index) {
+      this.dataLoading = true;
+      this.confirmDeleteAct(index + 3);
+      //this.projectDatas.splice(index + 3, 1);
+      //this.isSave = false;
+      this.dataLoading = false;
+    },
+    //拖曳相關--str---↓↓--
+    allowDrop(e) {
+      //取消默認行為
+      console.log("allowDrop");
+      e.preventDefault();
+    },
+    dragStart(e, index) {
+      console.log("dragStart");
+      let tar = e.target;
+      e.dataTransfer.setData("Text", index);
+      if (tar.tagName.toLowerCase() == "li") {
+      }
+    },
+    drop(e, index, whatData) {
+      //放置
+      console.log("drop");
+
+      this.allowDrop(e);
+      if (whatData == "classDatas") {
+        let arr = this.classDatas.concat([]),
+          dragIndex = e.dataTransfer.getData("Text");
+        let temp = arr.splice(dragIndex, 1);
+
+        arr.splice(index, 0, temp[0]);
+        this.classDatas = arr;
+
+        //#region 更新子項目順序
+        let sortItems = [];
+        for (let i = 0; i < this.classDatas.length; i++) {
+          let id = this.classDatas[i].id;
+          sortItems.push({
+            id: this.classDatas[i].id,
+            sort: i + 1,
+          });
+        }
+        let postData = {
+          scoreCalcActId: this.projectDatas[this.selectIndex].id,
+          teammodelId: this.$store.state.userInfo.TEAMModelId,
+          sortItems: sortItems,
+        };
+        this.dataLoading = true;
+
+        this.$api.learnActivity.updateScoreCalcActItemSort(postData).then(
+          (res) => {
+            if (res) {
+              // 重新排序原始資料
+              this.calcData.scoreCalcAct.forEach((element) => {
+                if (this.projectDatas[this.selectIndex].id === element.id) {
+                  // 先找到要排序的子項目資料
+                  for (let i = 0; i < element.items.length; i++) {
+                    for (let j = 0; j < this.classDatas.length; j++) {
+                      if (this.classDatas[j].id === element.items[i].id) {
+                        // 重新設定原始資料的排序
+                        element.items[i].sort = j + 1;
+                      }
+                    }
+                  }
+                  element.items = element.items.sort((a, b) => a.sort - b.sort); // 設定好排序後  進行升冪排序
+                }
+              });
+            }
+          },
+          (err) => {
+            this.$Message.error(this.$t("cusMgt.gradeErr"));
+          }
+        );
+        this.dataLoading = false;
+        //#endregion
+      } else if (whatData == "projectDatas") {
+        let arr = this.projectDatas.concat([]),
+          dragIndex = e.dataTransfer.getData("Text");
+        let temp = arr.splice(dragIndex, 1);
+
+        arr.splice(index, 0, temp[0]);
+        this.projectDatas = arr;
+        this.selectIndex = index;
+
+        //#region 更新活動項目順序
+        let sortItems = [];
+        for (let i = 0; i < this.projectDatas.length; i++) {
+          let id = this.projectDatas[i].id;
+          sortItems.push({
+            id: this.projectDatas[i].id,
+            sort: i + 1,
+          });
+        }
+        let postData = {
+          scoreCalcId: this.tableInfo.tableId,
+          teammodelId: this.$store.state.userInfo.TEAMModelId,
+          sortItems: sortItems,
+        };
+        this.dataLoading = true;
+        this.$api.learnActivity.updateScoreCalcActSort(postData).then(
+          (res) => {
+            if (res) {
+              // 重新排序原始資料
+              this.calcData.scoreCalcAct.forEach((element) => {
+                for (let i = 0; i < this.projectDatas.length; i++) {
+                  if (this.projectDatas[i].id === element.id) {
+                    element.sort = i + 1;
+                  }
+                }
+              });
+              this.calcData.scoreCalcAct = this.calcData.scoreCalcAct.sort(
+                (a, b) => a.sort - b.sort
+              );
+            }
+          },
+          (err) => {
+            this.$Message.error(this.$t("cusMgt.gradeErr"));
+          }
+        );
+        this.dataLoading = false;
+        //#endregion
+      }
+    },
+    dragEnd() {
+      console.log("dragEnd");
+      //this.isSave = false;
+    },
+    //拖曳相關--end---↑↑--
+    // 新增一個子項目
+    addClass() {
+      this.dataLoading = true;
+      let postData = {
+        scoreCalcActId: this.projectDatas[this.selectIndex].id,
+        teammodelId: this.$store.state.userInfo.TEAMModelId,
+      };
+      this.$api.learnActivity.addScoreCalcActItem(postData).then(
+        (res) => {
+          if (res) {
+            this.calcData.scoreCalcAct.forEach((element) => {
+              // 找到目前選中的項目 並更新其子項目資料
+              if (this.projectDatas[this.selectIndex].id === element.id) {
+                element.items.push(res);
+
+                let islessonrecord = false;
+                if (element.type === "lessonrecord") {
+                  islessonrecord = true;
+                }
+                this.classDatas.push({
+                  id: res.id,
+                  single: res.use,
+                  custom: true,
+                  className: res.name,
+                  proportion: res.rate,
+                  score: 0,
+                  islessonrecord: islessonrecord,
+                });
+              }
+            });
+          }
+        },
+        (err) => {
+          this.$Message.error(this.$t("cusMgt.gradeErr"));
+        }
+      );
+      this.dataLoading = false;
+    },
+    // 刪除一個子項目
+    delClass(id) {
+      this.$Modal.confirm({
+        title: this.$t("scoreCalc.deleteRemind1"),
+        content: this.$t("scoreCalc.remindMessage3"),
+        onOk: async () => {
+          this.dataLoading = true;
+          let postData = {
+            scoreCalcActId: this.projectDatas[this.selectIndex].id,
+            id: id,
+            teammodelId: this.$store.state.userInfo.TEAMModelId,
+          };
+          this.$api.learnActivity.deleteScoreCalcActItem(postData).then(
+            (res) => {
+              if (res) {
+                this.calcData.scoreCalcAct.forEach((element) => {
+                  // 找到刪除的項目 並移除其子項目資料
+                  if (this.projectDatas[this.selectIndex].id === element.id) {
+                    for (let i = 0; i < element.items.length; i++) {
+                      if (id === element.items[i].id) {
+                        element.items.splice(i, 1);
+                      }
+                    }
+                  }
+                });
+                // 找到刪除的項目 並移除其子項目資料
+                for (let i = 0; i < this.classDatas.length; i++) {
+                  if (id === this.classDatas[i].id) {
+                    this.classDatas.splice(i, 1);
+                  }
+                }
+              }
+            },
+            (err) => {
+              this.$Message.error(this.$t("cusMgt.gradeErr"));
+            }
+          );
+          this.dataLoading = false;
+        },
+        onCancel: () => {
+          //this.$Message.info('Clicked cancel');
+        },
+      });
+    },
+    // 登錄分數
+    keyScore(id, type, className) {
+      //(類型)
+      this.dataLoading = true;
+      this.classDataprop = [];
+      //debugger;
+      setTimeout(() => {
+        this.$nextTick(() => {
+          let dt = new Date();
+
+          this.classData = [];
+          this.selectClassId = id;
+          if (type) this.class_type = "lessonrecord";
+          else this.class_type = "activity";
+          this.className = className;
+          this.modalVisibleStudentScore = true;
+
+          this.calcData.scoreCalcAct.forEach((element) => {
+            if (element.id === this.projectDatas[this.selectIndex].id) {
+              // 先選中項目
+              element.items.forEach((item) => {
+                if (item.id === id) {
+                  // 再選中子項目
+                  if (element.type === "lessonrecord") {
+                    // 如果是課堂紀錄要登記三種分數
+                    for (let i = 0; i < item.stuActAttendScores.length; i++) {
+                      // for (let i = 0; i < 20; i++) {
+                      this.classData.push({
+                        name: this.calcData.members[i].name,
+                        id: this.calcData.members[i].id,
+                        attendance: item.stuActAttendScores[i],
+                        score: item.stuActPointScores[i],
+                        interactiveScore: item.stuActItactScores[i],
+                      });
+                    }
+                  } else {
+                    // 如果不是課堂紀錄登記一種分數
+                    for (let i = 0; i < item.scores.length; i++) {
+                      //  for (let i = 0; i < 20; i++) {
+                      this.classData.push({
+                        name: this.calcData.members[i].name,
+                        id: this.calcData.members[i].id,
+                        attendance: 0,
+                        score: item.scores[i],
+                        interactiveScore: 0,
+                      });
+                    }
+                  }
+                }
+              });
+            }
+          });
+          this.classDataprop = this.classData;
+        });
+      }, 1);
+      let that = this;
+      setTimeout(() => {
+        that.dataLoading = false;
+      }, 1000);
+      //this.dataLoading = false;
+    },
+    modalVisibleStudentScoreFun(value) {
+      this.modalVisibleStudentScore = value;
+      this.classDataprop = [];
+    },
+    updateInputprojectName(event) {
+      //debugger;
+      this.isSave = false;
+      const newProjectName = event.target.value;
+      this.projectDatas[this.thisClassData.index].projectName = newProjectName;
+    },
+    updateInputpercentage(event) {
+      this.isSave = false;
+      const newPercentage = event.target.value;
+      this.projectDatas[this.thisClassData.index].percentage = newPercentage;
+    },
+    isInput() {
+      this.isSave = false;
+    },
+    getProjectDatasClick(index) {
+      // 檢查選中項目click防呆
+      if (this.projectDatas[index] !== undefined) {
+        return this.projectDatas[index].click ? this.projectDatas[index].click : false;
+      } else {
+        return false;
+      }
+    },
+    getProjectName(index) {
+      // 檢查選中項目ProjectName防呆
+      if (this.projectDatas[index] !== undefined) {
+        // 轉換多語系
+        switch (this.projectDatas[index].projectName) {
+          case "課堂紀錄":
+            return this.$t("scoreCalc.lessonRecordTitle");
+          case "作業活動":
+            return this.$t("scoreCalc.homeworkTitle");
+          case "評量活動":
+            return this.$t("scoreCalc.examTitle");
+          default:
+            return "";
+        }
+      } else {
+        return "";
+      }
+    },
+    getPercentage(index) {
+      // 檢查選中項目Percentage防呆
+      if (this.projectDatas[index] !== undefined) {
+        return this.projectDatas[index].percentage
+          ? this.projectDatas[index].percentage
+          : "";
+      } else {
+        return "";
+      }
+    },
+    getActList(listId) {
+      //取項目資料
+      this.projectDatas = [];
+      let acts;
+      let postData = { id: this.tableInfo.tableId };
+      this.dataLoading = true;
+      this.$api.learnActivity.getScoreCalcAll(postData).then(
+        (res) => {
+          if (res) {
+            this.calcData = res;
+            if (res.scoreCalcAct) acts = res.scoreCalcAct.sort((a, b) => a.sort - b.sort); //title根據sort進行降冪排序
+
+            acts.forEach((element) => {
+              // 比對是否為選中的活動
+              if (listId === element.id) {
+                this.projectDatas.push({
+                  click: true,
+                  projectName: element.name,
+                  percentage: element.rate,
+                  id: element.id,
+                });
+
+                this.reloadProject(this.projectDatas.length - 1);
+              } else {
+                this.projectDatas.push({
+                  click: false,
+                  projectName: element.name,
+                  percentage: element.rate,
+                  id: element.id,
+                });
+              }
+            });
+          }
+        },
+        (err) => {
+          this.$Message.error(this.$t("cusMgt.gradeErr"));
+        }
+      );
+      this.dataLoading = false;
+    },
+    setSubListData(element) {
+      // 子項目資料填入
+      element.items.forEach((subelement) => {
+        if (element.type !== "lessonrecord") {
+          let sum = 0;
+          subelement.scores.forEach((scoreselement) => {
+            sum = sum + scoreselement;
+          });
+
+          this.classDatas.push({
+            id: subelement.id,
+            single: subelement.use,
+            custom: subelement.custom,
+            className: subelement.name,
+            proportion: subelement.rate,
+            score: Math.round((sum / subelement.scores.length) * 10) / 10,
+            islessonrecord: false,
+          });
+        } else {
+          this.classDatas.push({
+            id: subelement.id,
+            single: subelement.use,
+            custom: subelement.custom,
+            className: subelement.name,
+            proportion: subelement.rate,
+            score: 0,
+            islessonrecord: true,
+          });
+        }
+      });
+    },
+    confirmDeleteAct(index) {
+      // 刪除項目
+      this.$Modal.confirm({
+        title: this.$t("scoreCalc.deleteRemind2"),
+        content: this.$t("scoreCalc.remindMessage3"),
+        onOk: async () => {
+          if (index === this.selectIndex) {
+            // 如果要刪除的跟目前選中的活動向相同 焦點回到第一個項目
+            this.reloadProject(0);
+          }
+          let postData = {
+            id: this.projectDatas[index].id,
+            teammodelId: this.$store.state.userInfo.TEAMModelId,
+            scorecalcId: this.tableInfo.tableId,
+          };
+          this.dataLoading = true;
+          await this.$api.learnActivity.deleteScoreCalcAct(postData).then(
+            (res) => {
+              if (res) {
+                for (let i = 0; i < this.calcData.scoreCalcAct.length; i++) {
+                  // 移除已刪除項目
+                  // 比對是否為選中的活動
+                  if (this.projectDatas[index].id === this.calcData.scoreCalcAct[i].id) {
+                    this.calcData.scoreCalcAct.splice(i, 1);
+                  }
+                }
+                this.projectDatas.splice(index, 1); // 移除已刪除項目
+
+                if (this.projectDatas.length === 3) {
+                  this.reloadProject(0);
+                }
+
+                this.$Message.info(this.$t("scoreCalc.remindMessage4"));
+              }
+            },
+            (err) => {
+              this.$Message.error(this.$t("cusMgt.gradeErr"));
+            }
+          );
+          this.dataLoading = false;
+        },
+        onCancel: () => {
+          //this.$Message.info('Clicked cancel');
+        },
+      });
+    },
+    updateItemScore(val) {
+      // 接取子項目傳的值
+      this.inputValFromChild = val;
+
+      if (val) {
+        let postData = {};
+        let stuActAttendScores = [];
+        let stuActPointScores = [];
+        let stuActItactScores = [];
+        let scores = [];
+        // 根據目前選中的類別設定欄位更新分數
+        if (this.class_type === "lessonrecord") {
+          for (let i = 0; i < val.length; i++) {
+            stuActAttendScores.push(val[i].attendance);
+            stuActPointScores.push(val[i].score);
+            stuActItactScores.push(val[i].interactiveScore);
+          }
+
+          postData = {
+            scoreCalcActId: this.projectDatas[this.selectIndex].id,
+            teammodelId: this.$store.state.userInfo.TEAMModelId,
+            id: this.selectClassId,
+            stuActAttendScores: stuActAttendScores,
+            stuActPointScores: stuActPointScores,
+            stuActItactScores: stuActItactScores,
+          };
+        } else {
+          for (let i = 0; i < val.length; i++) {
+            scores.push(val[i].score);
+          }
+          postData = {
+            scoreCalcActId: this.projectDatas[this.selectIndex].id,
+            teammodelId: this.$store.state.userInfo.TEAMModelId,
+            id: this.selectClassId,
+            scores: scores,
+          };
+        }
+        this.dataLoading = true;
+        this.$api.learnActivity.updateItemScore(postData).then(
+          (res) => {
+            // 更新原始資料的那一個子項目的成績
+            this.calcData.scoreCalcAct.forEach((element) => {
+              if (element.id === this.projectDatas[this.selectIndex].id) {
+                // 先選中項目
+                element.items.forEach((item) => {
+                  if (item.id === this.selectClassId) {
+                    // 再選中子項目
+                    if (element.type === "lessonrecord") {
+                      // 如果是課堂紀錄要登記三種分數
+                      item.stuActAttendScores = stuActAttendScores;
+                      item.stuActPointScores = stuActPointScores;
+                      item.stuActItactScores = stuActItactScores;
+                    } else {
+                      // 如果不是課堂紀錄登記一種分數
+                      item.scores = scores;
+                    }
+                  }
+                });
+              }
+            });
+            // 登錄分數後平均分數要重算   先取得要修改的子項目索引
+            for (let i = 0; i < this.classDatas.length; i++) {
+              if (this.classDatas[i].id === this.selectClassId) {
+                this.selectClassIndex = i;
+              }
+            }
+            this.calcData.scoreCalcAct.forEach((element) => {
+              if (this.projectDatas[this.selectIndex].id === element.id) {
+                // 子項目資料填入
+                element.items.forEach((subelement) => {
+                  if (subelement.id === this.selectClassId) {
+                    if (element.type !== "lessonrecord") {
+                      let sum = 0;
+                      subelement.scores.forEach((scoreselement) => {
+                        sum = sum + scoreselement;
+                      });
+                      this.classDatas[this.selectClassIndex].score =
+                        Math.round((sum / subelement.scores.length) * 10) / 10;
+                    }
+                  }
+                });
+              }
+            });
+            this.$Message.info(this.$t("scoreCalc.remindMessage5"));
+          },
+          (err) => {
+            this.$Message.error(this.$t("cusMgt.gradeErr"));
+          }
+        );
+        this.dataLoading = false;
+      }
+      this.classDataprop = [];
+    },
+  },
+  watch: {},
+};
+</script>
+<style lang="less" scoped>
+@import "./AddProject.less";
+</style>
+<style lang="less">
+//.ivu-table.ivu-table-stripe .ivu-table-cell.demo-table-info-column
+.score-container {
+  .right {
+    .content {
+      .body {
+        .card {
+          .ivu-input {
+            color: #2d8cf0;
+            text-align: center;
+            width: 100%;
+            resize: none;
+          }
+        }
+      }
+    }
+  }
+}
+</style>

+ 126 - 0
TEAMModelOS/ClientApp/src/view/mycourse/score/DialogBox.less

@@ -0,0 +1,126 @@
+.center {
+  display: flex;
+  justify-content: space-between;
+}
+
+.y-center {
+  display: flex;
+  flex-direction: column;
+  justify-content: center;
+}
+
+.hr-color {
+  height: 1px;
+  border: none;
+  background-color: #dcdee2;
+}
+
+.save-info-ok {
+  text-align: right;
+  color: #808695;
+}
+.save-info-erro {
+  text-align: right;
+  color: #ed4014;
+}
+.marage-y {
+  margin: 0px 5px;
+}
+.dialog-box-div {
+  padding: 10px;
+
+  .div2 {
+    display: flex;
+    align-items: center;
+
+    .enter-box {
+      margin: 1px 5px;
+      border: 1px solid #dcdee2;
+    }
+    .txt-box {
+      text-align: left;
+      width: 400px;
+    }
+    .txt-box2 {
+      text-align: left;
+      width: 350px;
+    }
+  }
+  .div3 {
+    display: flex;
+    align-items: center;
+    .enter-box {
+      margin: 1px 0px;
+      border: 0px solid #dcdee2;
+    }
+  }
+  .info-icon{
+    cursor: pointer;
+    font-size: 16px;
+  }
+  .info-box{
+    padding: 5px;
+    //border: 1px solid #dcdee2;
+    background-color: #f8f8f9;
+    color: #808695;
+    top:0px;
+    .content{
+      font-size: 10px;
+      color: #808695;
+    }
+  }
+  .div-studentScore {
+    .enter-box {
+      margin: 1px 5px;
+      border: 1px solid #dcdee2;
+    }
+    .txt-box {
+      text-align: left;
+      width: 100%;
+      height: 400px;
+    }
+  }
+  .s-text {
+    font-size: 8px;
+    color: #808695;
+  }
+
+  .boxline {
+    border: 1px solid #dcdee2;
+    padding: 10px;
+  }
+  .enter-box {
+    width: 30px;
+    color: #2d8cf0 !important;
+    padding: 1px;
+    text-align: center;
+    border: 1px solid gray;
+  }
+  .column-input {
+    width: 30px;
+    height: 20px;
+    box-sizing: border-box;
+    color: #2d8cf0;
+    margin: 0px 5px;
+    border: none;
+    background: none;
+    text-align: center;
+  }
+}
+/*
+.attend-button{
+  background-color: #fff;
+  padding: 2px 15px;
+  border-color: #dcdee2;
+  border-radius: 4px;
+  color: #515a6e;
+  border: 1px solid transparent;
+  border-color: #dcdee2;
+  cursor: inherit;
+}*/
+.attend-button{
+  white-space:normal ;
+  height: auto;
+  margin: 0 2px;
+  padding: 2px 15px;
+}

+ 441 - 0
TEAMModelOS/ClientApp/src/view/mycourse/score/DialogBoxAttend.vue

@@ -0,0 +1,441 @@
+<template>
+  <div class="score-dialog-box-attend">
+    <Modal :width="800" v-model="modalVisibleView" :title="`${titleName} `+$t('scoreCalc.scoreCalculation')" :class-name="class_name" @on-ok="ok_fun"
+      @on-cancel="cancel">
+      <div class="center">
+        <Button class="attend-button" :type="buttonType[0]" ref="simple_attend" 
+          @click="dialog_box_menu('simple_attend', 0)">{{ $t('scoreCalc.simpleAttendanceCalculation') }}</Button>
+        <Button class="attend-button" :type="buttonType[1]" ref="attend_calculate"
+          @click="dialog_box_menu('attend_calculate', 0)">{{ $t('scoreCalc.attendanceCalculationMethod') }}</Button>
+        <Button class="attend-button" :type="buttonType[2]" ref="custom_calculate1"
+          @click="dialog_box_menu('custom_calculate', 1)">{{ $t('scoreCalc.customCalculation') }}1</Button>
+        <Button class="attend-button" :type="buttonType[3]" ref="custom_calculate2"
+          @click="dialog_box_menu('custom_calculate', 2)">{{ $t('scoreCalc.customCalculation') }}2</Button>
+        <Button class="attend-button" :type="buttonType[4]" ref="custom_calculate3"
+          @click="dialog_box_menu('custom_calculate', 3)">{{ $t('scoreCalc.customCalculation') }}3</Button>
+      </div>
+      <div v-if="showContent === 'simple_attend'" class="dialog-box-div"><!--簡單出席法-->
+        <div>
+          <div>
+            <span>{{$t('scoreCalc.calculationDescription1')}}</span>
+          </div>
+          <br>
+          <Row>
+            <Col span="17">
+            <div class="div2">
+              <span>{{$t('scoreCalc.calculationDescription2')}}</span>
+              <input type="text" v-model="Count_attend_Temp.simple.score" class="enter-box" maxlength="2" />
+              <span>{{$t('scoreCalc.marks')}}</span>
+            </div>
+            <br>
+            <div>
+              <div>{{$t('scoreCalc.example')}}</div>
+              <div>{{$t('scoreCalc.attendance')}}: 100</div>
+              <div>{{$t('scoreCalc.absences')}}: 2</div>
+              <div> {{$t('scoreCalc.calculationDescription3')}} 5 {{$t('scoreCalc.marks')}}</div>
+              <div style="color:#808695;"> {{$t('scoreCalc.calculationFormula')}} 100 – ( 2 * <span style="color:#ed4014;">5</span> ) = 90 {{$t('scoreCalc.marks')}}({{$t('scoreCalc.attendance')}})</div>
+            </div>
+            </Col>
+            <Col span="7" class="y-center">
+            <div class="boxline">
+              <div>{{$t('scoreCalc.calculationDescription4')}}</div>
+              <CheckboxGroup v-model="Count_attend_Temp.simple.situation" class="">
+                <div>
+                  <Checkbox label="absent">{{$t('scoreCalc.absence2')}}</Checkbox>
+                </div>
+                <div>
+                  <Checkbox label="SL">{{$t('scoreCalc.sickLeave2')}}</Checkbox>
+                </div>
+                <div>
+                  <Checkbox label="PL">{{$t('scoreCalc.personalLeave2')}}</Checkbox>
+                </div>
+                <div>
+                  <Checkbox label="OL">{{$t('scoreCalc.publicLeave2')}}</Checkbox>
+                </div>
+              </CheckboxGroup>
+            </div>
+            </Col>
+          </Row>
+        </div>
+      </div>
+      <div v-if="showContent === 'attend_calculate'" class="dialog-box-div"><!--出席計算法-->
+        <div>
+          <div>
+            <span>{{$t('scoreCalc.calculationDescription5')}}</span>
+          </div>
+          <br>
+          <Row>
+            <Col span="17">
+            <div class="div2">
+              <span>{{$t('scoreCalc.calculationDescription6')}}</span>
+              <input type="text" v-model="Count_attend_Temp.attend.score" class="enter-box" maxlength="3" />
+              <span>{{$t('scoreCalc.marks')}}</span>
+            </div>
+            <div class="div2">
+              <span>{{$t('scoreCalc.attendanceOver')}}</span>
+              <input type="text" v-model="Count_attend_Temp.attend.percent" class="enter-box" maxlength="3" />
+              <span>{{$t('scoreCalc.isFullMark')}}</span>
+            </div>
+            <br>
+            <div>
+              <div>{{$t('scoreCalc.example')}}</div>
+              <div>{{$t('scoreCalc.classGrades')}}100</div>
+              <div>{{$t('scoreCalc.totalNumberClasses')}}:10</div>
+              <div>{{$t('scoreCalc.attendQwen')}}:8</div>
+              <div>{{$t('scoreCalc.calculationDescription6')}} <span style="color:#ed4014;">1</span> {{$t('scoreCalc.marks')}}</div>
+              <div>{{$t('scoreCalc.attendanceOver')}} <span style="color:#ed4014;">90</span> {{$t('scoreCalc.isFullMark')}}</div>              
+              <div style="color:#808695;">{{$t('scoreCalc.formula')}}
+              <div>8/ 10 * 100 = 80 / 100 - ( <span style="color:#ed4014;">90</span> – 80 ) * <span style="color:#ed4014;">1</span> = 90
+                {{$t('scoreCalc.marks')}}({{$t('scoreCalc.attendance')}})</div></div>
+            </div>
+            </Col>
+            <Col span="7" class="y-center">
+            <div class="boxline">
+              <div>{{$t('scoreCalc.calculationDescription4')}}</div>
+              <CheckboxGroup v-model="Count_attend_Temp.attend.situation">
+                <div>
+                  <Checkbox label="absent">{{$t('scoreCalc.absence2')}}</Checkbox>
+                </div>
+                <div>
+                  <Checkbox label="SL">{{$t('scoreCalc.sickLeave2')}}</Checkbox>
+                </div>
+                <div>
+                  <Checkbox label="PL">{{$t('scoreCalc.personalLeave2')}}</Checkbox>
+                </div>
+                <div>
+                  <Checkbox label="OL">{{$t('scoreCalc.publicLeave2')}}</Checkbox>
+                </div>
+              </CheckboxGroup>
+            </div>
+            </Col>
+          </Row>
+        </div>
+      </div>
+      <div v-if="showContent === 'custom_calculate'" class="dialog-box-div"><!--自訂計算-->
+        <div>
+          <div>
+            {{$t('scoreCalc.calculationDescription7')}}
+          </div>
+          <br>
+          <Row>
+            <Col span="6">
+            <div>X:{{$t('scoreCalc.totalNumberClasses')}}</div>
+            <div>Y:{{$t('scoreCalc.attendQwen')}}</div>
+            </Col>
+            <Col span="8">
+              <div>A:{{$t('scoreCalc.absences')}}</div>
+              <div>B:{{$t('scoreCalc.numberOfSick')}}</div>
+              <div>C:{{$t('scoreCalc.numberOfPerson')}}</div>
+              <div>D:{{$t('scoreCalc.numberOfPublic')}}</div>
+              <br>
+            </Col>
+            <Col v-if="infoShow" span="10" class="info-box">
+              <div style="text-decoration: underline;">{{$t('scoreCalc.formulaExample')}}ROUND(X)</div>
+              <div class="content">ROUND、MAX、MIN、SQRT、ABS、LN、EXP、POWER、INT、CEILING、FLOOR、LOG10、LOG2、RAND、SIGN、SIN、COS、TAN</div>
+            </Col>
+          </Row>          
+          <div>
+            <div><u>{{$t('scoreCalc.calculationFormula')}}</u>
+              <Icon type="md-alert" class="info-icon" @click="infoShow = !infoShow;"/>
+            </div>
+            <div class="div2">
+              <textarea type="text" rows="4" cols="30" datatype="*" class="enter-box txt-box"
+                :placeholder="custom_calculate1_data" required @change="textareafun" v-model="custom_data"></textarea>
+              <span>={{$t('scoreCalc.attendance')}}</span>
+            </div>
+            <!-- <div class="s-text"> {{$t('scoreCalc.calculationDescription9')}}</div> -->
+          </div>
+          <div style="  padding:5px;margin-top:10px;">
+            <div style=""><u>{{$t('scoreCalc.instantPreview')}}</u>: 
+            <span style="font-size:8px;"> ※<span style="color: #2d8cf0;">{{$t('scoreCalc.blueWord')}}</span>{{$t('scoreCalc.canModifyable')}}</span>
+            </div>
+            <div class="div3">
+              <span>{{$t('scoreCalc.totalNumberClasses')}}</span> <input type="text" v-model="checkNum.classTatol" class="enter-box" maxlength="3" @change="numberChanteFun"/>
+              <span> {{$t('scoreCalc.attendQwen')}}</span> <input type="text" v-model="checkNum.attend" class="enter-box" maxlength="3" @change="numberChanteFun"/>
+              <span> {{$t('scoreCalc.absence')}}</span> <input type="text" v-model="checkNum.absent" class="enter-box" maxlength="3" @change="numberChanteFun"/>
+              <span> {{$t('scoreCalc.sickLeave')}}</span> <input type="text" v-model="checkNum.SL" class="enter-box" maxlength="3" @change="numberChanteFun"/>
+              <span> {{$t('scoreCalc.personalLeave')}}</span> <input type="text" v-model="checkNum.PL" class="enter-box" maxlength="3" @change="numberChanteFun"/>
+              <span> {{$t('scoreCalc.publicLeave')}}</span> <input type="text" v-model="checkNum.OL" class="enter-box" maxlength="3" @change="numberChanteFun"/>
+            </div>
+            <div>{{$t('scoreCalc.score')}}:<span style="color: #ed4014;">{{ checkNum.Result }}</span></div>
+          </div>
+        </div>
+      </div>
+    </Modal>
+  </div>
+</template>
+<script>
+export default {
+  components: {
+
+  },
+  props: {
+    modalVisible: Boolean,
+    titleName: String,
+    class_name: String,
+    Count_attend: Object,
+    scoreCalcActId: String,
+    scoreCalcId: String,//成績表ID
+    evaluateExcelFormula: Function,//傳入轉換EXCEL的公式
+    evenlyShow:Function,//傳入//整除顯示的公式
+  },
+  data() {
+    return {
+      modalVisibleView: false,
+      showContent: 'simple_attend',
+      buttonType: ['primary', 'default', 'default', 'default', 'default'],
+      custom_calculate1_data: this.$t('scoreCalc.calculationDescription8')+'\nY / X*100',
+      custom_data: '',//顯示出席法的內容
+      Count_attend_Temp: {},
+      checkNum: { classTatol: 10, attend: 8, absent: 1, SL: 1, PL: 0, OL: 0, Result: 0 },
+      //modalVisible: false,
+      infoShow:false,
+    }
+  },
+  computed: {
+
+  },
+  methods: {
+    ok_fun() {
+      //this.$Message.info('Clicked ok');
+      //this.Count_attend = JSON.parse(JSON.stringify(this.Count_attend_Temp));
+      this.$api.learnActivity.getScoreCalcAll({ "id": this.scoreCalcId }).then(
+        res => {
+          if (res) {
+            let typeId = {
+              simple: { id: "", name: "", data: {}, use: false },
+              attend: { id: "", name: "", data: {}, use: false },
+              custom1: { id: "", name: "", data: {}, use: false },
+              custom2: { id: "", name: "", data: {}, use: false },
+              custom3: { id: "", name: "", data: {}, use: false }
+            };
+            if (this.Count_attend_Temp.way === 'simple') {
+              typeId.simple.use = true;
+            } else if (this.Count_attend_Temp.way === 'attend') {
+              typeId.attend.use = true;
+            } else if (this.Count_attend_Temp.way === 'custom1') {
+              typeId.custom1.use = true;
+            } else if (this.Count_attend_Temp.way === 'custom2') {
+              typeId.custom2.use = true;
+            } else if (this.Count_attend_Temp.way === 'custom3') {
+              typeId.custom3.use = true;
+            }
+
+            let filteredData = res.scoreCalcFunc.filter(item => item.method === "attend");//過濾標籤資料。
+            let customCount = 1;
+            for (let i = 0; i < filteredData.length; i++) {
+              if (filteredData[i].template === "simpleAttend") {
+                typeId.simple.data = filteredData[i];
+                typeId.simple.id = filteredData[i].id;
+                typeId.simple.name = filteredData[i].name;
+              } else if (filteredData[i].template === "attendRate") {
+                typeId.attend.data = filteredData[i];
+                typeId.attend.id = filteredData[i].id;
+                typeId.attend.name = filteredData[i].name;
+              } else if (filteredData[i].template === "custom") {
+                let typeName = "custom" + customCount;
+                typeId[typeName].data = filteredData[i];
+                typeId[typeName].id = filteredData[i].id;
+                typeId[typeName].name = filteredData[i].name;
+                customCount++;
+              }
+            }
+            this.giveData_API(typeId);
+          }
+        },
+        err => {
+          //this.$Message.error(this.$t('learnActivity.updateProjectListSort.Err'))
+        }
+      )
+      this.$emit('closeModal', { value1: false, value2: JSON.parse(JSON.stringify(this.Count_attend_Temp)) });
+    },
+    giveData_API(typeId) {
+      let giveData = {
+        "scoreCalcActId": this.scoreCalcActId,
+        "teammodelId": this.$store.state.userInfo.TEAMModelId,
+        "method": "attend",
+        "scoreCalcFunc": [
+          {
+            "id": typeId.simple.id,
+            "name": typeId.simple.name,
+            "use": typeId.simple.use,
+            "template": "simpleAttend",
+            "keyvals": [
+              {
+                "key": typeId.simple.data.keyvals.find(item => item.key === '一次扣幾分')?.key,
+                "val": this.Count_attend_Temp.simple.score
+              },
+              {
+                "key": typeId.simple.data.keyvals.find(item => item.key === 'Absent_Sick')?.key,
+                "val": this.Count_attend_Temp.simple.situation.includes('SL').toString()
+              },
+              {
+                "key": typeId.simple.data.keyvals.find(item => item.key === 'Absent')?.key,
+                "val": this.Count_attend_Temp.simple.situation.includes('absent').toString()
+              },
+              {
+                "key": typeId.simple.data.keyvals.find(item => item.key === 'Absent_Personal')?.key,
+                "val": this.Count_attend_Temp.simple.situation.includes('PL').toString()
+              },
+              {
+                "key": typeId.simple.data.keyvals.find(item => item.key === 'Absent_Official')?.key,
+                "val": this.Count_attend_Temp.simple.situation.includes('OL').toString()
+              }
+            ],
+            "content": typeId.simple.data.content
+          },
+          {
+            "id": typeId.attend.id,
+            "name": typeId.attend.name,
+            "use": typeId.attend.use,
+            /* simpleAttend:簡單出席計算法 | attendRate:出席率計算法 | custom:自訂 | standard:標準化評分 | percent:百分比評分  */
+            "template": "attendRate",
+            "keyvals": [
+              {
+                "key": typeId.attend.data.keyvals.find(item => item.key === '每缺席1%扣幾分')?.key,
+                "val": this.Count_attend_Temp.attend.score
+              },
+              {
+                "key": typeId.attend.data.keyvals.find(item => item.key === '出席率超過%為滿分')?.key,
+                "val": this.Count_attend_Temp.attend.percent
+              },
+              {
+                "key": "課堂總次數",
+                "val": "X"
+              },
+              {
+                "key": typeId.attend.data.keyvals.find(item => item.key === 'Absent_Sick')?.key,
+                "val": this.Count_attend_Temp.attend.situation.includes('SL').toString()
+              },
+              {
+                "key": typeId.attend.data.keyvals.find(item => item.key === 'Absent')?.key,
+                "val": this.Count_attend_Temp.attend.situation.includes('absent').toString()
+              },
+              {
+                "key": typeId.attend.data.keyvals.find(item => item.key === 'Absent_Personal')?.key,
+                "val": this.Count_attend_Temp.attend.situation.includes('PL').toString()
+              },
+              {
+                "key": typeId.attend.data.keyvals.find(item => item.key === 'Absent_Official')?.key,
+                "val": this.Count_attend_Temp.attend.situation.includes('OL').toString()
+              }
+            ],
+            /* (病假/缺席/事假/公假) 必須先判斷是否為true,才決定是否要帶入公式 */
+            "content": typeId.attend.data.content
+          },
+        ]
+      };
+      for (let i = 1; i <= 3; i++) {//還需判斷是否有ID
+        const customKey = `custom${i}`;
+        giveData.scoreCalcFunc.push({
+          "id": typeId[customKey].id,
+          "name": typeId[customKey].name,
+          "use": typeId[customKey].use,
+          "template": "custom",
+          "keyvals": typeId[customKey].data.keyvals,
+          "content": this.Count_attend_Temp[customKey].val
+        });
+      }
+      this.$api.learnActivity.updateScorecalc(giveData).then(
+        res => {
+          //console.log("res=" + JSON.stringify(res));
+          //this.$Message.success(this.$t('save Ok'))
+          setTimeout(() => {
+          })
+        },
+        err => {
+          //this.$Message.error(this.$t('Err'))
+        }
+      )
+    },
+    cancel() {
+      //this.$Message.info('Clicked cancel');
+      this.$emit('closeModal', { value1: false, value2: null });
+    },
+    dialog_box_menu(type, num) {
+      this.showContent = type;
+      if (type == 'simple_attend') {
+        this.buttonType = ['primary', 'default', 'default', 'default', 'default'];
+        this.Count_attend_Temp.way = 'simple';
+      } else if (type === 'attend_calculate') {
+        this.buttonType = ['default', 'primary', 'default', 'default', 'default'];
+        this.Count_attend_Temp.way = 'attend';
+      } else if (type === 'custom_calculate' && num === 1) {
+        this.Count_attend_Temp.way = 'custom1';
+        this.custom_data = this.Count_attend_Temp.custom1.val;
+        this.buttonType = ['default', 'default', 'primary', 'default', 'default'];
+      } else if (type === 'custom_calculate' && num === 2) {
+        this.custom_data = this.Count_attend_Temp.custom2.val;
+        this.Count_attend_Temp.way = 'custom2';
+        this.buttonType = ['default', 'default', 'default', 'primary', 'default'];
+      } else if (type === 'custom_calculate' && num === 3) {
+        this.Count_attend_Temp.way = 'custom3';
+        this.custom_data = this.Count_attend_Temp.custom3.val;
+        this.buttonType = ['default', 'default', 'default', 'default', 'primary'];
+      }
+      this.numberChanteFun();
+    },
+    textareafun() {
+      if (this.Count_attend_Temp.way == 'custom1') {
+        this.Count_attend_Temp.custom1.val = this.custom_data;
+      } else if (this.Count_attend_Temp.way == 'custom2') {
+        this.Count_attend_Temp.custom2.val = this.custom_data;
+      } else if (this.Count_attend_Temp.way == 'custom3') {
+        this.Count_attend_Temp.custom3.val = this.custom_data;
+      }
+      this.numberChanteFun();
+    },
+    numberChanteFun() {//輸入改變就執行此程式
+      const values = {
+        X: this.checkNum.classTatol,//課堂總次數
+        Y: this.checkNum.attend,//出席次數
+        A: this.checkNum.absent,//缺席
+        B: this.checkNum.SL,//病假
+        C: this.checkNum.PL,//事假
+        D: this.checkNum.OL,//公假
+      };
+      let formula = this.custom_data;
+      if (typeof formula === 'undefined' || formula === null || formula === '') {
+        this.checkNum.Result = 100;
+      } else {
+        let scoreCount = Number(this.evaluateExcelFormula(formula, values));
+        if (isNaN(scoreCount)) {
+          this.checkNum.Result = scoreCount;
+        } else {
+          this.checkNum.Result = this.evenlyShow(this.evaluateExcelFormula(formula, values), 1);
+        }
+      }
+    }
+  },
+  created() {
+
+  },
+  watch: {
+    modalVisible: {
+      immediate: true,
+      handler(newVal) {
+        this.modalVisibleView = newVal;
+        this.Count_attend_Temp = JSON.parse(JSON.stringify(this.Count_attend));
+        if (this.Count_attend_Temp.way == 'simple') {
+          this.dialog_box_menu('simple_attend', 0);
+        } else if (this.Count_attend_Temp.way == 'attend') {
+          this.dialog_box_menu('attend_calculate', 0);
+        } else if (this.Count_attend_Temp.way == 'custom1') {
+          this.dialog_box_menu('custom_calculate', 1);
+        } else if (this.Count_attend_Temp.way == 'custom2') {
+          this.dialog_box_menu('custom_calculate', 2);
+        } else if (this.Count_attend_Temp.way == 'custom3') {
+          this.dialog_box_menu('custom_calculate', 3);
+        }
+      }
+    }
+  }
+
+}
+</script>
+<style lang="less" scoped>
+@import "./DialogBox.less";
+</style>
+<style lang="less">
+//.ivu-table.ivu-table-stripe .ivu-table-cell.demo-table-info-column
+</style>

+ 401 - 0
TEAMModelOS/ClientApp/src/view/mycourse/score/DialogBoxScoreCont.vue

@@ -0,0 +1,401 @@
+<template>
+  <div class="score-dialog-box-score-cont">
+    <Modal v-model="modalVisibleView" :title="`${titleName} `+$t('scoreCalc.scoreCalculation')" :class-name="class_name" @on-ok="ok_fun"
+      @on-cancel="cancel">
+      <div class="center">
+        <Button class="attend-button" :type="buttonType[0]" ref="standard_score" @click="dialog_box_menu('standard_score')">{{ $t('scoreCalc.percentageScore') }}</Button>
+        <Button class="attend-button" :type="buttonType[1]" ref="percentage_score" @click="dialog_box_menu('percentage_score')">{{ $t('scoreCalc.standardizedsScoring') }}</Button>
+        <Button class="attend-button" :type="buttonType[2]" ref="custom_score1" @click="dialog_box_menu('custom_score', 1)">{{ $t('scoreCalc.customScoring') }}1</Button>
+        <Button class="attend-button" :type="buttonType[3]" ref="custom_score2" @click="dialog_box_menu('custom_score', 2)">{{ $t('scoreCalc.customScoring') }}2</Button>
+        <Button class="attend-button" :type="buttonType[4]" ref="custom_score3" @click="dialog_box_menu('custom_score', 3)">{{ $t('scoreCalc.customScoring') }}3</Button>
+      </div>
+      <div v-if="showContent === 'standard_score'" class="dialog-box-div">
+        <div>
+          <div class="div2">
+            {{ $t('scoreCalc.calculationDescription10') }}
+          </div>
+          <Row>
+            <Col span="14">
+            <br>
+            <div>
+              <div>x:{{ $t('scoreCalc.gravityCalculationScore') }}</div>
+              <div>y:{{ $t('scoreCalc.averageScore') }}</div>
+              <div> SD:{{ $t('scoreCalc.standardDeviation') }}</div>
+              <div> {{ $t('scoreCalc.standardScoreZFormula') }}
+                <math>
+                  <mfrac>
+                    <mrow>
+                      <mi>x</mi>
+                      <mo>-</mo>
+                      <mi>y</mi>
+                    </mrow>
+                    <mrow>
+                      <mi>S</mi>
+                      <mi>D</mi>
+                    </mrow>
+                  </mfrac>
+                </math>
+              </div>
+              <div class="div2">
+                <span>{{ $t('scoreCalc.derivedStandardScoreTFormula') }} T = 10Z+</span>
+                <input type="text" v-model="Count_Temp.standard.score" class="enter-box" maxlength="2" />
+              </div>
+              <div>{{ $t('scoreCalc.calculationDescription11') }}</div>
+            </div>
+            </Col>
+            <Col span="10" class="y-center">
+            <div class="boxline">
+              <div>{{ $t('scoreCalc.example') }}</div>
+              <div>{{ $t('scoreCalc.gravityCalculationScore') }}:18 </div>
+              <div>{{ $t('scoreCalc.indicatesAverageScore') }}:14</div>
+              <div>{{ $t('scoreCalc.standardDeviation') }}:3</div>
+              <div style="color:#808695; margin:5px 0px;">
+                <math>
+                  <mfrac>
+                    <mrow>
+                      <mn>18</mn>
+                      <mo>-</mo>
+                      <mn>14</mn>
+                    </mrow>
+                    <mn>3</mn>
+                  </mfrac>
+                </math>
+                = 1.33
+              </div>
+              <div style="color:#808695;"> 10× 1.33+ <span style="color:#ed4014;">50</span>= 63.3{{ $t('scoreCalc.marks') }}</div>
+            </div>
+            </Col>
+          </Row>
+        </div>
+      </div>
+      <div v-if="showContent === 'percentage_score'" class="dialog-box-div">
+        <div>
+          <div>
+            <span>{{ $t('scoreCalc.calculationDescription12') }}</span>
+          </div>
+          <br>
+          <div style="color:#808695; ">
+            <div>{{ $t('scoreCalc.example') }}</div>
+            <div>{{ $t('scoreCalc.gravityCalculationScore') }}:12</div>
+            <div>{{ $t('scoreCalc.highestScore') }}:20</div>
+            <div style="margin-top: 5px;">
+              {{ $t('scoreCalc.formulaExample') }}100 x
+              <math>
+                <mfrac>
+                  <mn>12</mn>
+                  <mn>20</mn>
+                </mfrac>
+              </math>
+              = 60 {{ $t('scoreCalc.marks') }}
+            </div>
+          </div>
+        </div>
+      </div>
+      <div v-if="showContent === 'custom_score'" class="dialog-box-div">
+        <div>
+          <div>
+            {{ $t('scoreCalc.calculationDescription13') }}
+          </div>          
+          <Row>
+            <Col span="12">
+              <div>A:{{ $t('scoreCalc.gravityCalculationScore') }}</div>
+              <div>B:{{ $t('scoreCalc.classQwen') }}</div>
+              <div>C:{{ $t('scoreCalc.totalScoreClass') }}</div>
+              <div>D:{{ $t('scoreCalc.totalNumberStudents') }}</div>              
+              <br>
+            </Col>
+            <Col v-if="infoShow" span="10" class="info-box">
+              <div style="text-decoration: underline;">{{$t('scoreCalc.formulaExample')}}ROUND(X)</div>
+              <div class="content">ROUND、MAX、MIN、SQRT、ABS、LN、EXP、POWER、INT、CEILING、FLOOR、LOG10、LOG2、RAND、SIGN、SIN、COS、TAN</div>
+            </Col>
+          </Row>
+          <div>
+            <div><u>{{ $t('scoreCalc.calculationFormula') }}</u>
+              <Icon type="md-alert" class="info-icon" @click="infoShow = !infoShow;"/>
+            </div>
+            <div class="div2">
+              <textarea type="text" rows="4" cols="30" datatype="*" class="enter-box txt-box2"
+                :placeholder="custom_score1_data" required v-model="custom_data" @change="textareafun"></textarea>
+              <span>={{ titleName }} {{ $t('scoreCalc.score') }}</span>
+            </div>
+            <!-- <div class="s-text"> {{ $t('scoreCalc.calculationDescription9') }}</div> -->
+          </div>
+          <div>
+            <div style="margin-top:20px;"><u>{{ $t('scoreCalc.instantPreview') }}</u>:
+              <span style="font-size:8px;"> ※<span style="color: #2d8cf0;">{{ $t('scoreCalc.blueWord') }}</span>{{ $t('scoreCalc.canModifyable') }}</span>
+            </div>
+            <div class="div3">
+              <span>{{ $t('scoreCalc.gravityCalculationScore') }}<input type="text" v-model="checkNum.Gravityscore" class="enter-box" maxlength="3" @change="numberChanteFun"/></span> 
+              <span> {{ $t('scoreCalc.totalNumberClasses') }}<input type="text" v-model="checkNum.classTatol" class="enter-box" maxlength="3" @change="numberChanteFun"/></span> 
+              <span> {{ $t('scoreCalc.totalScoreClass') }}<input type="text" v-model="checkNum.allScore" class="enter-box" maxlength="3" @change="numberChanteFun"/></span> 
+              <span> {{ $t('scoreCalc.totalNumberStudents') }}<input type="text" v-model="checkNum.studentNum" class="enter-box" maxlength="3" @change="numberChanteFun"/></span> 
+            </div>            
+            <div>{{ $t('scoreCalc.score') }}:<span style="color: #ed4014;">{{ checkNum.Result }}</span></div>
+          </div>
+        </div>
+      </div>
+    </Modal>
+</div>
+</template>
+<script>
+export default {
+  components: {
+
+  },
+  props: {
+    modalVisible: Boolean,
+    titleName: String,
+    class_name: String,
+    Count_point: Object,
+    Count_interact: Object,
+    scoreCalcActId: String,
+    scoreCalcId: String,//成績表ID
+    evaluateExcelFormula:  Function,//傳入轉換EXCEL的公式
+    evenlyShow:Function,//傳入//整除顯示的公式
+  },
+  data() {
+    return {
+      infoShow:false,
+      modalVisibleView: false,
+      showContent: 'standard_score',
+      buttonType: ['primary', 'default', 'default', 'default', 'default'],
+      custom_score1_data: this.$t('scoreCalc.formulaExample')+'10×√A  '+this.$t('scoreCalc.writtenas')+'\n10*SQRT(A) ',
+      custom_data: '',//顯示出席法的內容
+      Count_Temp: {},
+      checkNum : {Gravityscore:80,classTatol:10,allScore:10,studentNum:50,Result:0}
+    }
+  },
+  computed: {
+
+  },
+  methods: {
+    ok_fun() {
+      //this.$Message.info('Clicked ok');  
+      this.$api.learnActivity.getScoreCalcAll({ "id": this.scoreCalcId }).then(
+        res => {
+          if (res) {
+            let typeId = {
+              method : "",
+              standard: { id: "", name: "", data: {}, use: false },
+              percent: { id: "", name: "", data: {}, use: false },
+              custom1: { id: "", name: "", data: {}, use: false },
+              custom2: { id: "", name: "", data: {}, use: false },
+              custom3: { id: "", name: "", data: {}, use: false }
+            };
+            if (this.Count_Temp.way === 'standard') {
+              typeId.standard.use = true;
+            } else if (this.Count_Temp.way === 'percent') {
+              typeId.percent.use = true;
+            } else if (this.Count_Temp.way === 'custom1') {
+              typeId.custom1.use = true;
+            } else if (this.Count_Temp.way === 'custom2') {
+              typeId.custom2.use = true;
+            } else if (this.Count_Temp.way === 'custom3') {
+              typeId.custom3.use = true;
+            }
+
+            let filteredData = {};
+            if (this.titleName == this.$t('scoreCalc.point')) {//記分板
+              typeId.method = "point";
+              filteredData = res.scoreCalcFunc.filter(item => item.method === "point");//過濾標籤資料。
+              this.$emit('closeModal', { value1: false, value2: '記分板', value3: JSON.parse(JSON.stringify(this.Count_Temp)) });
+            } else if (this.titleName == this.$t('scoreCalc.interactiveScore')) {//互動分
+              typeId.method = "interaction";
+              filteredData = res.scoreCalcFunc.filter(item => item.method === "interaction");//過濾標籤資料。
+              this.$emit('closeModal', { value1: false, value2: '互動分', value3: JSON.parse(JSON.stringify(this.Count_Temp)) });
+            }
+
+            let customCount = 1;
+            for (let i = 0; i < filteredData.length; i++) {
+              if (filteredData[i].template === "standard") {
+                typeId.standard.data = filteredData[i];
+                typeId.standard.id = filteredData[i].id;
+                typeId.standard.name = filteredData[i].name;
+              } else if (filteredData[i].template === "percent") {
+                typeId.percent.data = filteredData[i];
+                typeId.percent.id = filteredData[i].id;
+                typeId.percent.name = filteredData[i].name;
+              } else if (filteredData[i].template === "custom") {
+                let typeName = "custom" + customCount;
+                typeId[typeName].data = filteredData[i];
+                typeId[typeName].id = filteredData[i].id;
+                typeId[typeName].name = filteredData[i].name;
+                customCount++;
+              }
+            }
+            this.giveData_API(typeId);
+          }
+        },
+        err => {
+          //this.$Message.error(this.$t('learnActivity.updateProjectListSort.Err'))
+        }
+      )
+    },
+    giveData_API(typeId) {
+      let giveData = {
+        "scoreCalcActId": this.scoreCalcActId,
+        "teammodelId": this.$store.state.userInfo.TEAMModelId,
+        "method": typeId.method,//point / interaction
+        "scoreCalcFunc": [
+          {
+            "id": typeId.standard.id,
+            "name": typeId.standard.name,
+            "use": typeId.standard.use,
+            "template": "standard",
+            "keyvals": [
+              {
+                "key": "原始分數",
+                "val": "X"
+              },
+              {
+                "key": "平均分數",
+                "val": "Y"
+              },
+              {
+                "key": typeId.standard.data.keyvals.find(item => item.key === '平均數')?.key,
+                "val": this.Count_Temp.standard.score
+              },
+              {
+                "key": "標準差",
+                "val": "50"
+              },
+            ],
+            "content": typeId.standard.data.content
+          },
+          {
+            "id": typeId.percent.id,
+            "name": typeId.percent.name,
+            "use": typeId.percent.use,
+            /* simpleAttend:簡單出席計算法 | attendRate:出席率計算法 | custom:自訂 | standard:標準化評分 | percent:百分比評分  */
+            "template": "percent",
+            "keyvals": [
+              {
+                "key": "原始分數",
+                "val": "X"
+              },
+              {
+                "key": "最高分數",
+                "val": "Y"
+              }
+            ],
+            /* (病假/缺席/事假/公假) 必須先判斷是否為true,才決定是否要帶入公式 */
+            "content": typeId.percent.data.content
+          },
+        ]
+      };
+      for (let i = 1; i <= 3; i++) {//還需判斷是否有ID
+        const customKey = `custom${i}`;
+        giveData.scoreCalcFunc.push({
+          "id": typeId[customKey].id,
+          "name": typeId[customKey].name,
+          "use": typeId[customKey].use,
+          "template": "custom",
+          "keyvals": typeId[customKey].data.keyvals,
+          "content": this.Count_Temp[customKey].val
+        });
+      }
+      this.$api.learnActivity.updateScorecalc(giveData).then(
+        res => {
+          //console.log("res=" + JSON.stringify(res));
+          //this.$Message.success(this.$t('save Ok'))
+          setTimeout(() => {
+          })
+        },
+        err => {
+          //this.$Message.error(this.$t('Err'))
+        }
+      )
+    },
+    cancel() {
+      //this.$Message.info('Clicked cancel');
+      this.$emit('closeModal', { value1: false, value2: null, value3: null });
+    },
+    dialog_box_menu(showContent, num) {
+      this.showContent = showContent;
+      if (showContent == 'standard_score') {
+        this.buttonType = ['primary', 'default', 'default', 'default', 'default'];
+        this.Count_Temp.way = 'standard';
+      } else if (showContent == 'percentage_score') {
+        this.buttonType = ['default', 'primary', 'default', 'default', 'default'];
+        this.Count_Temp.way = 'percent';
+      } else if (showContent == 'custom_score' && num == 1) {
+        this.buttonType = ['default', 'default', 'primary', 'default', 'default'];
+        this.custom_data = this.Count_Temp.custom1.val;
+        this.Count_Temp.way = 'custom1';
+      } else if (showContent == 'custom_score' && num == 2) {
+        this.buttonType = ['default', 'default', 'default', 'primary', 'default'];
+        this.custom_data = this.Count_Temp.custom2.val;
+        this.Count_Temp.way = 'custom2';
+      } else if (showContent == 'custom_score' && num == 3) {
+        this.buttonType = ['default', 'default', 'default', 'default', 'primary'];
+        this.custom_data = this.Count_Temp.custom3.val;
+        this.Count_Temp.way = 'custom3';
+      }
+      this.numberChanteFun();
+    },
+    textareafun() {
+      if (this.Count_Temp.way == 'custom1') {
+        this.Count_Temp.custom1.val = this.custom_data;
+      } else if (this.Count_Temp.way == 'custom2') {
+        this.Count_Temp.custom2.val = this.custom_data;
+      } else if (this.Count_Temp.way == 'custom3') {
+        this.Count_Temp.custom3.val = this.custom_data;
+      }
+      this.numberChanteFun();
+    },
+    numberChanteFun() {//輸入改變就執行此程式
+      const values = {
+        A: this.checkNum.Gravityscore,//比重計算分數
+        B: this.checkNum.classTatol,//課堂總數
+        C: this.checkNum.allScore,//全班總分
+        D: this.checkNum.studentNum,//學生總數
+      };
+      let formula = this.custom_data;
+      if (typeof formula === 'undefined' || formula === null || formula === '') {
+        this.checkNum.Result = 100;
+      } else {
+        let scoreCount = Number(this.evaluateExcelFormula(formula, values));
+        if (isNaN(scoreCount)) {
+          this.checkNum.Result = scoreCount;
+        } else {
+          this.checkNum.Result = this.evenlyShow(this.evaluateExcelFormula(formula, values), 1);
+        }
+      }
+    }
+  },
+  watch: {
+    modalVisible: {
+      immediate: true,
+      handler(newVal) {
+        this.modalVisibleView = newVal;        
+      if (this.titleName == this.$t('scoreCalc.point')) {//記分板
+        this.Count_Temp = JSON.parse(JSON.stringify(this.Count_point));
+      } else if (this.titleName == this.$t('scoreCalc.interactiveScore')) {//互動分
+        this.Count_Temp = JSON.parse(JSON.stringify(this.Count_interact));
+      }
+      //確認按鈕位置
+      if (this.Count_Temp.way == 'standard') {
+        this.dialog_box_menu('standard_score', 0);
+      } else if (this.Count_Temp.way == 'percent') {
+        this.dialog_box_menu('percentage_score', 0);
+      } else if (this.Count_Temp.way == 'custom1') {
+        this.dialog_box_menu('custom_score', 1);
+      } else if (this.Count_Temp.way == 'custom2') {
+        this.dialog_box_menu('custom_score', 2);
+      } else if (this.Count_Temp.way == 'custom3') {
+        this.dialog_box_menu('custom_score', 3);
+      }
+      }
+    }
+  }
+}
+</script>
+<style lang="less" scoped>
+@import "./DialogBox.less";
+</style>
+<style lang="less">
+
+
+
+//.ivu-table.ivu-table-stripe .ivu-table-cell.demo-table-info-column
+</style>

+ 285 - 0
TEAMModelOS/ClientApp/src/view/mycourse/score/DialogBoxStudentScore.vue

@@ -0,0 +1,285 @@
+<template>
+  <div class="score-dialog-box-student-score">
+
+    <Modal v-model="modalVisibleView" :title="className + $t('scoreCalc.scoreCalc')" class-name="titleClassStyle" :class_type="class_type"
+      @on-ok="ok" @on-cancel="cancel" :width="modalWidth">
+      <div class="dialog-box-div center" style="padding: 0px;">
+        <div class="left">
+          <div>
+            <span class="s-text"> ※<span style="color: #2d8cf0;">{{$t('scoreCalc.blueWord')}}</span>{{$t('scoreCalc.modifyContent')}}</span>
+            <span v-if="class_type == 'lessonrecord'" class="s-text">{{$t('scoreCalc.attendState')}}</span>
+          </div>
+          <div>
+            <DataTable :value="classData" showGridlines v-if="class_type == 'lessonrecord'" :scrollable="true"
+              scrollHeight="400px" editMode="cell" class="editable-cells-table" responsiveLayout="scroll" >
+              <Column field="name" :header="$t('scoreCalc.name')" :styles="{ 'min-width': '120px', 'text-align': 'center', 'display': 'block' }" >
+                <template #body="rowData">                  
+                    <div >
+                    {{ rowData.data.name }}
+                  </div>
+                </template>
+              </Column>
+              <Column field="id" :header="$t('scoreCalc.id')"
+                :styles="{ 'min-width': '120px', 'text-align': 'center', 'display': 'block' }">
+                <template #body="rowData">
+                  <div>
+                    {{ rowData.data.id }}
+                  </div>
+                </template>
+              </Column>
+              <Column field="attendance" :header="$t('scoreCalc.attend')"        
+              :styles="{ 'min-width': '120px', 'color': 'rgb(45, 140, 240)', 'text-align': 'center', 'display': 'block' }"
+              :headerStyle="{ 'color': '#515a6e' }">
+                <template #body="rowData">
+                  <div >
+                    <input type="text" v-model.number="rowData.data[rowData.column.field]" @input="falseIsSave()"
+                      maxlength="1" class="column-input" />
+                  </div>
+                </template>
+              </Column>
+              <Column field="score" :header="$t('scoreCalc.point')"       
+                :styles="{ 'min-width': '120px', 'color': 'rgb(45, 140, 240)', 'text-align': 'center', 'display': 'block' }"
+                :headerStyle="{ 'color': '#515a6e' }">
+                <template #body="rowData">
+                  <div >
+                    <input type="text" v-model.number="rowData.data[rowData.column.field]" @input="falseIsSave()"
+                      maxlength="3" class="column-input" />
+                  </div>
+                </template>
+              </Column>
+              <Column field="interactiveScore" :header="$t('scoreCalc.interactiveScore')"
+                :styles="{ 'min-width': '120px', 'color': 'rgb(45, 140, 240)', 'text-align': 'center', 'display': 'block' }"
+                :headerStyle="{ 'color': '#515a6e' }">
+                <template #body="rowData">
+                  <div >
+                    <input type="text" v-model.number="rowData.data[rowData.column.field]" @input="falseIsSave()"
+                      maxlength="3" class="column-input" />
+                  </div>
+                </template>
+              </Column>
+            </DataTable>
+
+            <DataTable :value="classData" showGridlines v-if="class_type == 'activity'" :scrollable="true"
+              scrollHeight="400px" editMode="cell" class="editable-cells-table" responsiveLayout="scroll">
+              <Column field="name" :header="$t('scoreCalc.name')"
+                :styles="{ 'min-width': '120px', 'text-align': 'center', 'display': 'block' }">
+                <template #body="rowData">
+                  <div style="text-align: center;">
+                    {{ rowData.data.name }}
+                  </div>
+                </template>
+              </Column>
+              <Column field="id" :header="$t('scoreCalc.id')"
+                :styles="{ 'min-width': '120px', 'text-align': 'center', 'display': 'block' }">
+                <template #body="rowData">
+                  <div>
+                    {{ rowData.data.id }}
+                  </div>
+                </template>
+              </Column>
+              <Column field="score" :header="$t('scoreCalc.score')"
+                :styles="{ 'min-width': '120px', 'color': 'rgb(45, 140, 240)', 'text-align': 'center', 'display': 'block' }"
+                :headerStyle="{ 'color': '#515a6e' }">
+                <template #body="rowData">
+                  <div style="width: 100%">
+                    <input type="text" v-model.number="rowData.data[rowData.column.field]" @input="falseIsSave()"
+                      maxlength="3" class="column-input" />
+                  </div>
+                </template>
+              </Column>
+            </DataTable>
+
+
+
+          </div>
+        </div>
+        <div class="right">
+          <div>
+            <span style="font-weight: bold;">{{$t('scoreCalc.pasteMultiple')}}</span>
+            <span class="s-text">{{$t('scoreCalc.pasteEXCEL')}}</span>
+          </div>
+
+          <div class="div-studentScore">
+            <textarea v-if="class_type == 'lessonrecord'" v-model="textareaValue_system" type="text" rows="4" cols="30"
+              datatype="*" class="enter-box txt-box" :placeholder="custom_score_data" required
+              @input="isTextareaInput = true, TextareaInput_system"></textarea>
+            <textarea v-if="class_type == 'activity'" v-model="textareaValue_system" type="text" rows="4" cols="30"
+              datatype="*" class="enter-box txt-box" :placeholder="custom_score_data2" required
+              @input="isTextareaInput = true, TextareaInput_custom"></textarea>
+          </div>
+          <div v-if="isSave" class="save-info-ok">
+            <Icon type="md-checkmark-circle" />{{$t('scoreCalc.isSave')}}
+          </div>
+          <div v-if="!isSave" class="save-info-erro">
+            <Icon type="md-alert" />{{$t('scoreCalc.notSave')}}
+          </div>
+        </div>
+      </div>
+      <Loading :top="100" v-show="dataLoading" type="1"></Loading>
+    </Modal>
+  </div>
+</template>
+<script>
+import DataTable from 'primevue/datatable';
+import Column from 'primevue/column';
+import Loading from '@/common/Loading.vue';
+import InputText from 'primevue/inputtext';
+
+
+export default {
+  components: {
+    DataTable,
+    Column,
+    InputText
+  },
+  props: {
+    modalVisible: Boolean,
+    className: String,
+    class_type: String,
+    classDataprop: Array
+  },
+  data() {
+    return {
+      isSave: true,
+      modalVisibleView: false,
+      textareaValue_system: '',
+      isTextareaInput: false,
+      // custom_score_data: '1\t3\t2\n57\t86\t75\n62\t81\t93\n75\t77\t63\n75\t68\t83\n92\t93\t89',
+      // custom_score_data2: '76\n82\n86\n65\n77\n68',
+      custom_score_data: '',
+      custom_score_data2: '',
+      classData: [],
+      dataLoading: false,      
+    }
+  },
+  computed: {
+    TextareaInput_system() {
+      this.isSave = false;
+      if (this.isTextareaInput && this.textareaValue_system.trim() !== '') {
+        const data = this.textareaValue_system.split('\n').map((line) => line.split('\t'));
+        const lastLine = data[data.length - 1];
+        const isLastLineEmpty = lastLine.length === 1 && lastLine[0].trim() === '';
+
+        for (let i = 0; i < data.length - 1; i++) { // 迭代到倒數第二行
+          if (this.classData[i]) {
+            const [attendance, score, interactiveScore] = data[i];
+            this.classData[i].attendance = parseInt(attendance, 10) || 0;
+            this.classData[i].score = parseInt(score, 10) || 0;
+            this.classData[i].interactiveScore = parseInt(interactiveScore, 10) || 0;
+          }
+        }
+        // 更新最後一行的值,除非只包含換行符
+        if (!isLastLineEmpty && this.classData[data.length - 1]) {
+          const [attendance, score, interactiveScore] = data[data.length - 1];
+          this.classData[data.length - 1].attendance = parseInt(attendance, 10) || 0;
+          this.classData[data.length - 1].score = parseInt(score, 10) || 0;
+          this.classData[data.length - 1].interactiveScore = parseInt(interactiveScore, 10) || 0;
+        }
+        this.isTextareaInput = false;
+      }
+    },
+    TextareaInput_custom() {
+      this.isSave = false;
+      if (this.isTextareaInput && this.textareaValue_system.trim() !== '') {
+        const data = this.textareaValue_system.split('\n').map((line) => line.split('\t'));
+        const lastLine = data[data.length - 1];
+        const isLastLineEmpty = lastLine.length === 1 && lastLine[0].trim() === '';
+
+        for (let i = 0; i < data.length - 1; i++) { // 迭代到倒數第二行
+          if (this.classData[i]) {
+            const [score] = data[i];
+            this.classData[i].score = parseInt(score, 10) || 0;
+          }
+        }
+        // 更新最後一行的值,除非只包含換行符
+        if (!isLastLineEmpty && this.classData[data.length - 1]) {
+          const [score] = data[data.length - 1];
+          this.classData[data.length - 1].score = parseInt(score, 10) || 0;
+        }
+        this.isTextareaInput = false;
+      }
+    },
+    modalWidth() {
+      if (this.class_type === 'lessonrecord') {
+        return '900px';
+      } else if (this.class_type === 'activity') {
+        return '700px';
+      } else {
+        return '900px'; // 預設的 modal 寬度
+      }
+    },
+  },
+  mounted() {
+  },
+  methods: {
+    ok() {
+      if (!this.isSave) {
+        this.isSave = true;
+        //this.$Message.info('Clicked ok');      
+        this.textareaValue_system = '';
+        this.$emit("updateItemScore", this.classData);
+        this.$emit('closeModal');
+      } else {
+        this.$emit('closeModal');
+      }
+    },
+    cancel() {
+      this.dataLoading = true;
+
+      this.$emit('closeModal');
+      this.textareaValue_system = '';
+      this.isSave = true;
+     
+      this.dataLoading = false;
+    },
+    falseIsSave() {
+      this.isSave = false;
+    },
+    modelclick() {
+      //debugger;
+      console.log('modelclick');
+      this.modalVisibleView = true;
+    }
+  },
+  watch: {
+    modalVisible(newVal) {
+      //debugger;
+      this.modalVisibleView = newVal;
+    },
+    classDataprop(newVal) {
+      // const time = new Date();
+      // console.log("子視窗---classDataprop----S" + time);      
+      this.classData = JSON.parse(JSON.stringify(this.classDataprop));
+      this.custom_score_data = this.classData.map(item => `${item.attendance}\t${item.score}\t${item.interactiveScore}`).join('\n');
+      this.custom_score_data2 = this.classData.map(item => `${item.score}`).join('\n');
+
+      //console.log("子視窗---classDataprop----E" + time);         
+    }
+  }
+}
+</script>
+<style lang="less" scoped>
+@import "./DialogBox.less";
+</style>
+<style lang="less">
+.titleClassStyle {
+
+  .ivu-modal-header-inner {
+    font-size: 18px;
+    font-weight: bold;
+  }
+}
+
+thead tr th div {  
+  display: block !important;  
+}
+
+.tbthcolor{
+  color: #515a6e;
+  min-width: 120px;
+  color: rgb(45, 140, 240);
+  text-align: center; 
+  display: block;
+}
+
+</style>

+ 0 - 0
TEAMModelOS/ClientApp/src/view/mycourse/score/Score.less


+ 89 - 0
TEAMModelOS/ClientApp/src/view/mycourse/score/Score.vue

@@ -0,0 +1,89 @@
+<template>
+  <div class="score-container">
+    <div v-if="changePage == 'AllScore'">
+      <ScoreBody @changePage="changePagefun" @projectName="projectNamefun" :enter_tableInfo="tableInfo"
+        :paramsInfo="gradeParams" @tableInfo="tableInfofun" :grouplistId="grouplistId" :listType="listType"></ScoreBody>
+    </div>
+    <div v-else-if="changePage == 'AddProject'">
+      <AddProject  @changePage="changePagefun" :tableInfo="tableInfo"></AddProject>
+    </div>
+  </div>
+</template>
+<script>
+import ScoreBody from "./ScoreBody.vue"
+import AddProject from "./AddProject.vue"
+export default {
+  components: {
+    ScoreBody, AddProject
+  },
+  props: {
+    //收藏id,双向绑定
+    value: {
+      default: () => {
+        return []
+      },
+    },
+    gradeParams: {
+      type: Object,
+      default: () => {
+        return []
+      }
+    },
+    grouplistId: {
+      type: Object,
+      default: () => {
+        return {}
+      }
+    },
+    listType: {
+      type: String,
+      default: () => {
+        return {}
+      }
+    },
+  },
+  data() {
+    return {
+      filterExpire: false,
+      fIds: [],
+      changePage: "AllScore",
+      projectName: "",
+      tableInfo:{},
+    }
+  },
+  computed: {
+
+  },
+  created() {
+
+  },
+  methods: {
+    changePagefun(newValue) {
+      this.changePage = newValue;
+    },
+    projectNamefun(newValue) {
+      this.projectName = newValue;
+    },
+    tableInfofun(newValue) {
+      this.tableInfo = newValue;
+    }
+  },
+  watch: {
+    grouplistId: {
+      deep: true,
+      immediate: true,
+      handler(n, o) {
+        this.tableInfo ={};
+        this.changePage = "AllScore";//若在第二頁去,只要重新點選課程,一律跳回第一頁
+      }
+    }
+
+  }
+}
+</script>
+<style lang="less" scoped>
+@import "./Score.less";
+</style>
+<style lang="less">
+//.ivu-table.ivu-table-stripe .ivu-table-cell.demo-table-info-column
+</style>

+ 180 - 0
TEAMModelOS/ClientApp/src/view/mycourse/score/ScoreBody.less

@@ -0,0 +1,180 @@
+.record-container {
+    width: 100%;
+    height: ~"calc(100% - 50px)";
+}
+
+.text-size20{
+    font-size: 20px; 
+}
+
+// .custom-datatable::before{//奇數行的背景顏色f6f6f6
+//     background-color: #f6f6f6!important; 
+// }
+// .custom-datatable{//偶數行的背景顏色ffffff
+//     background-color: #c87878;
+// }
+
+.div-center{//水平置中
+    display: flex; 
+    justify-content: center; 
+    text-align:center;
+    width:100%;
+}
+.div-centerY{//垂直置中
+    display: flex; 
+    align-items: center;  
+    justify-content: center;
+}
+
+.table-none-background{    
+    background-color: 'white';
+    color: 'white';
+    border: '1px solid white';
+}
+.menu-list {
+    padding: 10px;
+    display: flex;
+    overflow-x: auto;
+    align-items: center;
+
+    .ivu-icon {
+        margin: 10px;
+    }
+
+    .ivu-card-body {
+        width: 160px !important;
+    }
+
+    .ivu-card {
+        background: #eee;
+        text-align: center;
+        overflow-wrap: break-word;
+        margin-right: 10px;
+        width: 160px;
+        flex: 0 0 160px;
+    }
+    .ivu-card:hover {
+        background: #ffffff;
+    }
+
+    .ivu-card.active{
+        background: #ffffff;
+        border-color:#dcdee2;
+        border-style:solid;
+    }
+}
+
+
+.rcd-item {
+    cursor: pointer;
+    display: flex;
+    border-bottom: 1px solid #e8eaec;
+    padding: 20px 30px 20px 15px;
+
+    &:hover .common-item-icon {
+        display: inline-block;
+    }
+
+    &:hover {
+        background: var(--active-item-start);
+    }
+}
+
+.body-list {
+    height: 100vh;
+    display: flex;
+    flex-direction: column;
+}
+
+.custom-table .ivu-table-cell {
+    padding-top: 0px;
+    /* 自定义上内边距 */
+    padding-bottom: 0px;
+    /* 自定义下内边距 */
+}
+
+
+
+.body-content {
+    padding: 15px;
+    flex-grow: 1;
+
+    .head {
+        display: flex;
+        align-items: center;
+        margin-bottom: 5px;
+
+        .list-name {
+            width: 250px;
+            color: #2d8cf0;
+            padding: 2px 5px;
+            border: 1px solid #e8eaec;
+        }       
+        .switch-location {
+            margin-left: auto; /* 使靠右方 */
+            margin-top: auto; /* 使自動靠下 */
+            display: flex;
+            align-items: center; //垂直置中
+        }
+
+        p {
+            margin: 0px 5px;
+            font-size: 6pt;
+        }
+    }
+
+    .body {
+
+        //height: 500px;
+        //overflow-y: auto;
+        .body-center {
+            align-items: center;
+            display: flex;
+
+            .table-container {
+                width: 100%;
+            }
+
+        }
+
+        .body-showIcon {
+            display: flex;
+            flex-direction: column;
+            margin-top: 40px;
+
+            .ivu-icon {
+                margin: 30px 10px;
+            }
+        }
+
+        .ivu-icon {
+            margin: 10px;
+        }
+    }
+    .save-info-ok{
+        text-align:right;
+        display: flex;
+        align-items: flex-end;
+        color: #808695;
+    }
+    .save-info-erro{
+        text-align:right;
+        display: flex;
+        align-items: flex-end;
+        color: #ed4014;
+    }
+    .foot {
+        display: flex;
+        justify-content: space-between;
+        padding: 5px 0px;
+        
+        .button-group {
+            margin-left: auto;
+            display: flex;
+        }
+
+        .button-space {
+            margin-left: 5px;
+        }
+    }
+}

Plik diff jest za duży
+ 2171 - 0
TEAMModelOS/ClientApp/src/view/mycourse/score/ScoreBody.vue


+ 23 - 0
TEAMModelOS/ClientApp/src/view/mycourse/score/ScoreIndex.less

@@ -0,0 +1,23 @@
+.menu-list {
+    padding: 10px;
+    display: flex;
+    overflow-x: auto;
+    align-items: center;
+
+    .ivu-icon {
+        margin: 10px;
+    }
+
+    .ivu-card-body {
+        width: 160px !important;
+    }
+
+    .ivu-card {
+        background: #eee;
+        text-align: center;
+        overflow-wrap: break-word;
+        margin-right: 10px;
+        width: 160px;
+        flex: 0 0 160px;
+    }
+}

+ 44 - 0
TEAMModelOS/ClientApp/src/view/mycourse/score/ScoreIndex.vue

@@ -0,0 +1,44 @@
+<template>
+  <div class="score-container">
+    <div class="menu-list">
+      <Icon type="md-add-circle" :size="25" @click="addNewScoreFile" />
+      <Card :bordered="false" v-for="n in 5">
+        <p><b>2023年上學期期中成績</b></p>
+        <!-- <p>Mid-term results of last semester</p> -->
+      </Card>
+    </div>
+  </div>
+</template>
+<script>
+export default {
+  components: {
+  },
+  props: {
+    //收藏id,双向绑定
+    value: {
+      default: () => {
+        return []
+      },
+    },
+  },
+  data() {
+    return {
+      filterExpire: false,
+      fIds: [],
+      list: {
+        name: '2023年上學期期中成績2',
+      },     
+    };
+  },
+  methods: {
+    addNewScoreFile() {
+      // 在這裡處理點擊事件的邏輯
+      console.log("Icon 被點擊了!");
+    },
+  },
+}
+</script>
+<style lang="less" scoped>
+@import "./ScoreIndex.less";
+</style>
+<style lang="less"></style>

+ 14 - 0
TEAMModelOS/ClientApp/src/view/questionnaire/ManageQuestionnaire.vue

@@ -573,6 +573,13 @@ export default {
             }
           })
         } else {
+          // 如果是学校活动 则需要带上查询的学期起止时间
+						if (!this.isPrivate) {
+							let curSemester = this.$store.state.user.curSemester;
+							let semesterRange = this.$tools.getStTimeByYearAndSemester(curSemester.year, curSemester.index);
+							data.stime = semesterRange.stime;
+							data.etime = semesterRange.etime;
+						}
           this.$api.questionnaire.FindSurveys(data).then(res => {
             if (!res.error) {
               r(res)
@@ -1567,6 +1574,13 @@ export default {
       },
       immediate: true,
     },
+    "$store.state.user.curSemester": {
+				deep: true,
+				handler(n, old) {
+          this.continueToken = null
+					this.handleTabClick(this.$route.name === "manageQuestionnaire" ? 0 : 1);
+				}
+			}
   },
 };
 </script>

Plik diff jest za duży
+ 1423 - 0
TEAMModelOS/ClientApp/src/view/signupActivity/createActivity.vue


BIN
TEAMModelOS/ClientApp/src/view/signupActivity/demo.jpeg


+ 798 - 0
TEAMModelOS/ClientApp/src/view/signupActivity/infoActivity copy.vue

@@ -0,0 +1,798 @@
+<template>
+    <div class="infos-box">
+        <div class="list-header">
+            <div @click="$router.go(-1)">
+                <Icon type="ios-arrow-back" />
+                返回列表
+            </div>
+            <p class="title" v-if="actInfo">
+                <span class="type-box">校级</span>
+                {{ actInfo.name }}
+            </p>
+        </div>
+        <Tabs value="0">
+            <TabPane label="活动信息" name="0" style="height: 100%;">
+                <vuescroll>
+                    <div v-if="actInfo">
+                        <img :src="actInfo.img" alt="">
+                        <div class="sk-info">
+                            <div class="info-title">基本信息</div>
+                            <p>主题:{{ actInfo.title }}</p>
+                            <p>简介:{{ actInfo.jianjie }}</p>
+                            <p>地点:{{ actInfo.address }}</p>
+                        </div>
+                        <div class="sk-info">
+                            <div class="info-title">活动单位</div>
+                            <p>主办:{{ actInfo.host }}</p>
+                            <p>承办:XXXX有限公司</p>
+                            <p>协办:XXXX有限公司</p>
+                            <p>支持:XXXX有限公司</p>
+                            <p>指导:XXXX有限公司</p>
+                        </div>
+                        <div class="sk-info">
+                            <div class="info-title">活动时间</div>
+                            <p>
+                                活动时间:<span>2023.08.01</span>-<span>2023.09.10</span>
+                            </p>
+                            <p>
+                                报名时间:<span>2023.08.01</span>-<span>2023.09.10</span>
+                            </p>
+                            <p>
+                                上传作品:<span>2023.08.01</span>-<span>2023.09.10</span>
+                            </p>
+                            <p>
+                                作品评审:<span>2023.08.01</span>-<span>2023.09.10</span>
+                            </p>
+                            <p>
+                                成绩公示:<span>2023.08.01</span>-<span>2023.09.10</span>
+                            </p>
+                        </div>
+                    </div>
+                </vuescroll>
+            </TabPane>
+            <TabPane label="报名数据" name="1">
+                <div class="tab-header join-data">
+                    <div>
+                        <p>报名制</p>
+                        <p>报名方式</p>
+                    </div>
+                    <div>
+                        <p>团队赛</p>
+                        <p>参赛方式</p>
+                    </div>
+                    <div>
+                        <p>34/50</p>
+                        <p>已报名</p>
+                    </div>
+                    <div>
+                        <p>27</p>
+                        <p>已上传</p>
+                    </div>
+                    <div>
+                        <p>10</p>
+                        <p>已评审</p>
+                    </div>
+                </div>
+                <div>
+                    <Table :columns="applicationColumns" :data="applicationList">
+                        <template #action="{row, index}">
+                            <Button type="error" size="small" @click="deleteApplica(row, index, 'join')">删除</Button>
+                        </template>
+                    </Table>
+                </div>
+            </TabPane>
+            <TabPane label="评审管理" name="2">
+                <div class="tab-header">
+                    <Button>添加评审专家</Button>
+                    <Button style="float: right;" @click="ruleDrawer = true">评审规则</Button>
+                </div>
+                <div>
+                    <Table :columns="processColumns" :data="processList">
+                        <template #process="{row, index}">
+                            <Progress :percent="25" :stroke-width="10" />
+                        </template>
+                        <template #actions="{row, index}">
+                            <Button type="error" size="small" @click="deleteApplica(row, index, 'process')">删除</Button>
+                        </template>
+                    </Table>
+                </div>
+            </TabPane>
+            <TabPane label="成绩统计" name="3">
+                <div class="tab-header">
+                    <Button>公示成绩</Button>
+                </div>
+                <div>
+                    <Table :columns="scoreColumns" :data="scoreList">
+                        <!-- <template #actions="{row, index}">
+                            <Button size="small" @click="deleteApplica(row, index, 'score')">修改</Button>
+                        </template> -->
+                    </Table>
+                </div>
+            </TabPane>
+        </Tabs>
+        <Drawer title="评审规则" :width="50" :closable="false" v-model="ruleDrawer">
+            <p style="font-size: 18px; font-weight: bold;">{{ ruleInfo.name }}</p>
+            <p>描述:{{ ruleInfo.describe }}</p>
+            <p>总分:{{ ruleInfo.score }}</p>
+            <div>
+                <Table row-key="id" :columns="ruleColumns" :data="ruleInfo.children"></Table>
+            </div>
+        </Drawer>
+        <!-- <Drawer title="评审规则" :width="50" :closable="false" v-model="ruleDrawer">
+            <p>名称:{{ ruleInfo.name }}</p>
+            <p>描述:{{ ruleInfo.describe }}</p>
+            <p>总分:{{ ruleInfo.score }}</p>
+            <div>
+                <Tree :data="ruleTree" :render="renderContent" @on-contextmenu="handleContextMenu"></Tree>
+            </div>
+        </Drawer> -->
+        <!-- <Modal v-model="modal" title="修改成绩" @on-ok="ok" @on-cancel="cancel">
+            <p></p>
+        </Modal> -->
+    </div>
+</template>
+
+<script>
+import { resolveComponent } from 'vue'
+export default {
+    data () {
+        return {
+            actInfo: undefined,
+            applicationColumns: [
+                {
+                    title: '姓名',
+                    key: 'name'
+                },
+                {
+                    title: '性别',
+                    key: 'sex'
+                },
+                {
+                    title: '手机号码',
+                    key: 'phone'
+                },
+                {
+                    title: '电子邮箱',
+                    key: 'email'
+                },
+                {
+                    title: '学校',
+                    key: 'school',
+                    filters: [
+                        {
+                            label: '醍摩豆学校',
+                            value: '醍摩豆学校'
+                        },
+                        {
+                            label: '研发学校',
+                            value: '研发学校'
+                        },
+                    ],
+                    filterMultiple: false,
+                    filterMethod (value, row) {
+                        return row.school === value
+                    }
+                },
+                {
+                    title: '职务',
+                    key: 'zhiwu'
+                },
+                {
+                    title: '学段',
+                    key: 'period'
+                },
+                {
+                    title: '学科',
+                    key: 'subject'
+                },
+                {
+                    title: '报名时间',
+                    key: 'time'
+                },
+                {
+                    title: '组名',
+                    key: 'team',
+                    filters: [
+                        {
+                            label: '数学组',
+                            value: '数学组'
+                        },
+                        {
+                            label: '语文组',
+                            value: '语文组'
+                        },
+                    ],
+                    filterMultiple: false,
+                    filterMethod (value, row) {
+                        return row.team === value
+                    }
+                },
+                {
+                    title: '组内身份',
+                    key: 'identity',
+                    filters: [
+                        {
+                            label: '组长',
+                            value: '组长'
+                        },
+                        {
+                            label: '组员',
+                            value: '组员'
+                        },
+                    ],
+                    filterMultiple: false,
+                    filterMethod (value, row) {
+                        return row.identity === value
+                    }
+                },
+                {
+                    title: '作品',
+                    key: 'work'
+                },
+                {
+                    title: '操作',
+                    slot: 'action',
+                    width: 150,
+                    align: 'center',
+                },
+            ],
+            processColumns: [
+                {
+                    title: '姓名',
+                    key: 'name'
+                },
+                {
+                    title: '性别',
+                    key: 'sex'
+                },
+                {
+                    title: '手机号码',
+                    key: 'phone'
+                },
+                {
+                    title: '电子邮箱',
+                    key: 'email'
+                },
+                /* {
+                    title: '擅长学段',
+                    key: 'period'
+                },
+                {
+                    title: '擅长学科',
+                    key: 'subject'
+                }, */
+                {
+                    title: '评审数量',
+                    key: 'num'
+                },
+                {
+                    title: '评审进度',
+                    slot: 'process',
+                    align: 'center',
+                },
+                {
+                    title: '操作',
+                    slot: 'actions',
+                    width: 150,
+                    align: 'center',
+                },
+            ],
+            scoreColumns: [
+                {
+                    title: '姓名/组名',
+                    key: 'name'
+                },
+                {
+                    title: '专家评分',
+                    key: 'score'
+                },
+                {
+                    title: '建议分数',
+                    key: 'score1'
+                },
+                /* {
+                    title: '操作',
+                    slot: 'actions',
+                    width: 150,
+                    align: 'center',
+                }, */
+            ],
+            applicationList: [
+                {
+                    name: '张三',
+                    sex: '男',
+                    phone: '110',
+                    email: '111222@qq.com',
+                    school: '醍摩豆学校',
+                    zhiwu: '年级主任',
+                    period: '初中',
+                    subject: '数学',
+                    time: '2023-08-08',
+                    team: '数学组',
+                    identity: '组长',
+                    work: '',
+                },
+                {
+                    name: '李四',
+                    sex: '女',
+                    phone: '911',
+                    email: '331321@qq.com',
+                    school: '醍摩豆学校',
+                    zhiwu: '普通教师',
+                    period: '初中',
+                    subject: '数学',
+                    time: '2023-08-10',
+                    team: '数学组',
+                    identity: '组员',
+                    work: '',
+                },
+                {
+                    name: '柳五',
+                    sex: '女',
+                    phone: '110',
+                    email: '111222@qq.com',
+                    school: '醍摩豆学校',
+                    zhiwu: '语文组长',
+                    period: '初中',
+                    subject: '语文',
+                    time: '2023-08-09',
+                    team: '语文组',
+                    identity: '组长',
+                    work: '',
+                },
+                {
+                    name: '李四',
+                    sex: '女',
+                    phone: '911',
+                    email: '331321@qq.com',
+                    school: '研发学校',
+                    zhiwu: '普通教师',
+                    period: '初中',
+                    subject: '数学',
+                    time: '2023-08-10',
+                    team: '数学组',
+                    identity: '组员',
+                    work: '',
+                },
+            ],
+            processList: [
+                {
+                    name: '张专家',
+                    sex: '男',
+                    phone: '110',
+                    email: '111222@qq.com',
+                    period: '初中',
+                    subject: '英语、数学',
+                    num: 4,
+                },
+                {
+                    name: '赵专家',
+                    sex: '女',
+                    phone: '110',
+                    email: '111222@qq.com',
+                    period: '初中',
+                    subject: '语文、生物',
+                    num: 3,
+                },
+            ],
+            scoreList: [
+                {
+                    name: '数学组',
+                    score: '67',
+                    score1: '85',
+                },
+                {
+                    name: '语文组',
+                    score: '92',
+                    score1: '85',
+                },
+            ],
+            ruleDrawer: false,
+            ruleColumns: [
+                {
+                    title: '规则',
+                    key: 'name',
+                    width: 200,
+                    tree: true
+                },
+                {
+                    title: '描述',
+                    key: 'describe'
+                },
+                {
+                    title: '分值',
+                    key: 'score',
+                    width: 100,
+                },
+            ],
+            ruleTree: [
+                {
+                    title: 'parent 1',
+                    expand: true,
+                    render: (h, { root, node, data }) => {
+                        return h('span', {
+                            style: {
+                                display: 'inline-block',
+                                width: '100%'
+                            }
+                        }, [
+                            /* h('span', [
+                                h(resolveComponent('Icon'), {
+                                    type: 'ios-folder-outline',
+                                    style: {
+                                        marginRight: '8px'
+                                    }
+                                }),
+                                h('span', data.title)
+                            ]), */
+                            h('span', {
+                                style: {
+                                    display: 'inline-block',
+                                    float: 'right',
+                                    marginRight: '32px'
+                                }
+                            }, /* [
+                                h(resolveComponent('Button'), {
+                                    ...this.buttonProps,
+                                    icon: 'ios-add',
+                                    type: 'primary',
+                                    style: {
+                                        width: '64px'
+                                    },
+                                    onClick: () => { this.append(data) }
+                                })
+                            ] */)
+                        ]);
+                    },
+                    children: [
+                        {
+                            title: 'child 1-1',
+                            expand: true,
+                            children: [
+                                {
+                                    title: 'leaf 1-1-1',
+                                    expand: true
+                                },
+                                {
+                                    title: 'leaf 1-1-2',
+                                    expand: true
+                                }
+                            ]
+                        },
+                        {
+                            title: 'child 1-2',
+                            expand: true,
+                            children: [
+                                {
+                                    title: 'leaf 1-2-1',
+                                    expand: true
+                                },
+                                {
+                                    title: 'leaf 1-2-1',
+                                    expand: true
+                                }
+                            ]
+                        }
+                    ]
+                }
+            ],
+            ruleInfo: {
+                id: '100',
+                name: '创新课堂评价标准',
+                describe: '全球醍摩豆AI智慧学校联盟名师团队课堂展示创新课堂评价标准',
+                score: 100,
+                children: [
+                    {
+                        id: '101',
+                        name: '同步差异化',
+                        describe: '1.同步差异化学习活动能够更好促进以帮助不同小组、不同学生,更好的学习。 2.根据学生需求、兴趣和能力(经验),学习体现出难度不同、材料不同和份量不同等的差异化教材案例,可复制可借鉴与扩散的价值。',
+                        score: 15,
+                    },
+                    {
+                        id: '102',
+                        name: '融合创新',
+                        describe: '1.围绕教学目标,与教学理念、教学方法、策略密切关连。 2.教学模式简明、清晰、高效,有利于复制与扩散。 3.教师能透过教学模式设计,体验智能课堂“精确、精致、精进”之学习洞察力。 4.教学模式能应用于不同班级、不同单元、不同学科、体现复制与扩散的价值。',
+                        score: 15,
+                    },
+                    {
+                        id: '103',
+                        name: '合作学习',
+                        describe: '1.围绕教学目标,与教学理念、教学方法、策略密切关连。 2.教学模式简明、清晰、高效,有利于复制与扩散。 3.教师能透过教学模式设计,体验智能课堂“精确、精致、精进”之学习洞察力。 4.教学模式能应用于不同班级、不同单元、不同学科、体现复制与扩散的价值。',
+                        score: 15,
+                    },
+                    {
+                        id: '104',
+                        name: '科技互动(T)',
+                        describe: '1.掌握智能教室之软硬件设备及应用功能,正确、熟练地运用教学科技辅具。 2.科技(T)使用的时机与方式能与教学内容、教学流程(P、C)密切配合,提升教与学的质量与效能。',
+                        score: 15,
+                    },
+                    {
+                        id: '105',
+                        name: '教法应用(P)',
+                        describe: '1.教学内容能展现理论基础、创新性与特色。 2.教学仪态自然、语言流畅、精准,富有感染力。3.教师能合理运用智能教室的辅助功能,体验智能课堂“主动、生动、互动”之“教学展现力”。',
+                        score: 15,
+                    },
+                    {
+                        id: '106',
+                        name: '教材设计(C)',
+                        describe: '1.教学设计内容完整、环节流程清楚、模式结构清晰,图片、图例等辅助说明应用得当。 2.清楚说明TPC之教学设计,文字简明,并能设计有流程图结构化辅助说明。',
+                        score: 25,
+                        _showChildren: true,
+                        children: [
+                            {
+                                id: '10600',
+                                name: '教学过程',
+                                describe: '',
+                                score: 5,
+                            },
+                            {
+                                id: '10601',
+                                name: '教学设计',
+                                describe: '',
+                                score: 5,
+                            },
+                            {
+                                id: '10602',
+                                name: '融合创新',
+                                describe: '',
+                                score: 5,
+                            },
+                            {
+                                id: '10603',
+                                name: '技术应用',
+                                describe: '',
+                                score: 5,
+                            },
+                            {
+                                id: '10604',
+                                name: '教学效果',
+                                describe: '',
+                                score: 5,
+                            },
+                        ],
+                    },
+                ],
+            },
+            contextData: null,
+            buttonProps: {
+                type: 'default',
+                size: 'small',
+            }
+        }
+    },
+    mounted () {
+        this.actInfo = this.$route.params.id
+    },
+    methods: {
+        getActInfo() {
+            this.actInfo = {
+                id: 111,
+                name: "第一个活动",
+                title: '推动创新教学,全球智慧教师齐聚一堂,切磋交流、提炼优质智慧课堂',
+                address: '办公大楼',
+                host: '全球醍摩豆智慧教育研究院',
+                jianjie: "「智慧課堂創新獎」選拔活動,匯集海內外全球智慧教師課堂影像,由專家學者評選出優秀作品,在全球智慧教師之夜頒獎,並收錄其作品成為經典課例,發展「可複製、會擴散」的智慧教室創新教學模式,散播智慧教育的種子。",
+                tag: 1,
+                type: 0,
+                owner: 'area',
+                img: require("./demo.jpeg"),
+            }
+        },
+        getRuleTree() {
+            this.ruleInfo.rule.forEach(item => {
+                let firstBox = {
+                    title: item.name,
+                    // id: item.id,
+                    // pid: item.pid,
+                    expand: true,
+                    children: [],
+                    rnodes: [],
+                    render: (h, {data}) => {
+                        return h('div', [
+                            h('span',
+                                {style: {},},
+                                data.title
+                            ),
+                            h('span',
+                                {style: {},},
+                                data.title
+                            ),
+
+                        ])
+                    }
+                }
+            });
+        },
+        deleteApplica(data, index, type) {
+            console.log(data, index);
+            if(type === 'join') {
+                this.$Message.warning('团队赛不可删除报名选手!')
+                
+            } else if(type === 'process') {
+                this.$Modal.confirm({
+                    title: '删除专家',
+                    content: `确认删除${data.name}专家吗?`,
+                    onOk: () => {
+                        this.processList.splice(index, 1)
+                    }
+                })
+            } else if(type === 'score') {
+                /* this.$Modal.confirm({
+                    title: '修改成绩',
+                    content: ``,
+                    render: (h, {datas}) => {
+                        return h('div', [
+                            h('p', data.name),
+                            h('Input', {
+                                props: {
+                                    value: data.score,
+                                },
+                                style: {
+                                    width: '100px',
+                                    marginRight: '10px',
+                                },
+                                on: {
+                                    'on-change': value => {
+                                        console.log(data, value);
+                                        data.score = value.target._value
+                                    }
+                                },
+                            }),
+                        ])
+                    },
+                    onOk: () => {
+                        this.scoreList[index].splice(index, 1)
+                    }
+                }) */
+            }
+        },
+        handleContextMenu (data) {
+            this.contextData = data;
+        },
+        renderContent (h, { root, node, data }) {
+            return h('span', {
+                style: {
+                    display: 'inline-block',
+                    width: '100%'
+                }
+            }, [
+                /* h('span', [
+                    h(resolveComponent('Icon'), {
+                        type: 'ios-paper-outline',
+                        style: {
+                            marginRight: '8px'
+                        }
+                    }),
+                    h('span', data.title)
+                ]), */
+                h('span', {
+                    style: {
+                        display: 'inline-block',
+                        float: 'right',
+                        marginRight: '32px'
+                    }
+                }, [
+                    h('Button', {
+                        ...this.buttonProps,
+                        icon: 'ios-add',
+                        style: {
+                            marginRight: '8px'
+                        },
+                        onClick: () => { this.append(data) }
+                    }),
+                    h('Button', {
+                        ...this.buttonProps,
+                        icon: 'ios-remove',
+                        onClick: () => { this.remove(root, node, data) }
+                    })
+                ])
+            ]);
+        },
+        append (data) {
+            const children = data.children || [];
+            children.push({
+                title: 'appended node',
+                expand: true
+            });
+            data.children = children
+        },
+        remove (root, node, data) {
+            const parentKey = root.find(el => el === node).parent;
+            const parent = root.find(el => el.nodeKey === parentKey).node;
+            const index = parent.children.indexOf(data);
+            parent.children.splice(index, 1);
+        }
+    }
+}
+</script>
+
+<style scoped lang="less">
+.infos-box{
+    height: 100%;
+    .list-header {
+        height: 45px;
+        box-shadow: 0px 2px 5px #e9e9e9;
+        padding-left: 15px;
+        padding-right: 20px;
+        margin-bottom: 5px;
+        line-height: 45px;
+        text-align: center;
+
+        &>div:first-of-type{
+            display: inline-block;
+            float: left;
+            cursor: pointer;
+        }
+
+        .title{
+            font-size: 18px;
+            font-weight: bold;
+            margin-right: 7%;
+        }
+        .type-box{
+            background-color: #FF9900;
+            font-size: 15px;
+            font-weight: normal;
+            color: #fff;
+            padding: 3px 6px;
+            border-radius: 5px;
+            margin-right: 5px;
+        }
+    }
+
+    img{
+        width: 70%;
+        height: 400px;
+        margin-left: 15%;
+    }
+
+    // .sk-box{
+        .sk-title{
+            background-color: #eaeaea;
+            padding: 10px;
+            margin-bottom: 10px;
+            margin-top: 25px;
+        }
+        .sk-info{
+            // padding: 0 10px;
+            &>p {
+                margin: 10px;
+            }
+            .info-title {
+                background-color: #eaeaea;
+                padding: 10px;
+                margin-bottom: 10px;
+                margin-top: 25px;
+            }
+        }
+    // }
+    .tab-header{
+        margin-bottom: 10px;
+    }
+    .join-data{
+        display: flex;
+        justify-content: space-around;
+        width: 100%;
+        margin-bottom: 30px;
+        &>div {
+            text-align: center;
+            margin: 0 30px;
+            &>p:first-of-type{
+                font-size: 30px;
+                font-weight: bold;
+            }
+        }
+    }
+}
+</style>
+<style lang="less">
+.infos-box{
+    .ivu-tabs{
+        height: 100%;
+        margin: 0 20px;
+    }
+    .ivu-tabs-content{
+        height: 88%;
+    }
+}
+</style>

+ 182 - 0
TEAMModelOS/ClientApp/src/view/signupActivity/infoActivity.vue

@@ -0,0 +1,182 @@
+<template>
+    <div style="height: 100%;">
+        <div class="list-header">
+            <div @click="$router.go(-1)">
+                <Icon type="ios-arrow-back" />
+                返回审核列表
+            </div>
+            <p class="title">
+                <span class="type-box">校级</span>
+                {{ actInfo.name }}
+            </p>
+        </div>
+        <div class="info-box">
+            <vuescroll>
+                <div style="margin-bottom: 20px;" class="sk-info">
+                    <img :src="actInfo.img" alt="">
+                    <p style="text-align: center; font-size: 20px;font-weight: bold; margin: 10px 0;">
+                        <span>2023.08.01</span>-<span>2023.09.10</span>
+                    </p>
+                    <p>主题:{{ actInfo.title }}</p>
+                    <p>简介:{{ actInfo.jianjie }}</p>
+                    <p>地点:{{ actInfo.address }}</p>
+                    <p>主办:{{ actInfo.host }}</p>
+                    <p>承办:XXXX有限公司</p>
+                    <p>协办:XXXX有限公司</p>
+                    <p>支持:XXXX有限公司</p>
+                    <p>指导:XXXX有限公司</p>
+                </div>
+                <div>
+                    <Tabs value="sk" class="sk-box">
+                        <TabPane v-for="(item, index) in checkModule" :key="index" :label="item.label" :name="item.name">
+                            <div v-if="item.name === 'sk'">
+                                <div class="sk-info">
+                                    <p>报名时间:{{ '2023.08.02 - 2023.08.05' }}</p>
+                                    <p>报名人数:{{ '28' }}人</p>
+                                    <p>参赛方式:{{ '团队赛' }}</p>
+                                    <p>免责声明:{{ '这是一段免责声明,这是一段免责声明,这是一段免责声明,这是一段免责声明,这是一段免责声明,这是一段免责声明,这是一段免责声明,这是一段免责声明,' }}</p>
+                                </div>
+                                <!-- 有上传作品 -->
+                                <template v-if="true">
+                                    <p class="sk-title">上传作品</p>
+                                    <div class="sk-info">
+                                        <p>上传时间:{{ '2023.08.06 - 2023.08.20' }}</p>
+                                        <p>作品类型:{{ '文件' }}</p>
+                                        <p>上传方式:{{ '由队长统一上传' }}</p>
+                                    </div>
+                                </template>
+                                <!-- 有评审 -->
+                                <template v-if="true">
+                                    <p class="sk-title">作品评审</p>
+                                    <div class="sk-info">
+                                        <p>评审时间:{{ '2023.08.21 - 2023.08.30' }}</p>
+                                        <p>评审规则:{{ 'XX标准规则' }}</p>
+                                    </div>
+                                </template>
+                                <!-- 有公示 -->
+                                <template v-if="true">
+                                    <p class="sk-title">成绩公示</p>
+                                    <div class="sk-info">
+                                        <p>公示时间:{{ '2023.08.31 - 2023.09.10' }}</p>
+                                    </div>
+                                </template>
+                            </div>
+                        </TabPane>
+                    </Tabs>
+                </div>
+                <Button style="margin-top: 16px; margin-right: 10px;" type="success" @click="next">通过审核</Button>
+                <Button style="margin-top: 16px" type="error" @click="last">不通过</Button>
+            </vuescroll>
+        </div>
+    </div>
+</template>
+
+<script>
+export default {
+    data () {
+        return {
+            actInfo: undefined,
+            checkModule: [
+                {
+                    name: 'sk',
+                    label: "赛课模块"
+                },/* {
+                    name: 'yx',
+                    label: "线上研修"
+                },{
+                    name: 'jy',
+                    label: "教研活动"
+                } */
+            ],
+            steps: 0,
+        }
+    },
+    mounted () {
+        this.actInfo = this.$route.params.info
+    },
+    methods: {
+        getActInfo() {
+            this.actInfo = {
+                id: 111,
+                name: "第一个活动",
+                title: '推动创新教学,全球智慧教师齐聚一堂,切磋交流、提炼优质智慧课堂',
+                address: '办公大楼',
+                host: '全球醍摩豆智慧教育研究院',
+                jianjie: "「智慧課堂創新獎」選拔活動,匯集海內外全球智慧教師課堂影像,由專家學者評選出優秀作品,在全球智慧教師之夜頒獎,並收錄其作品成為經典課例,發展「可複製、會擴散」的智慧教室創新教學模式,散播智慧教育的種子。",
+                tag: 1,
+                type: 0,
+                owner: 'area',
+                img: require("./demo.jpeg"),
+            }
+        },
+        next() {
+            this.steps += 1
+        },
+        last() {
+            this.steps -= 1
+        },
+    }
+}
+</script>
+
+<style scoped lang="less">
+.list-header {
+    height: 45px;
+    box-shadow: 0px 2px 5px #e9e9e9;
+    padding-left: 15px;
+    padding-right: 20px;
+    margin-bottom: 5px;
+    line-height: 45px;
+    text-align: center;
+
+    &>div:first-of-type{
+        display: inline-block;
+        float: left;
+        cursor: pointer;
+    }
+
+    .title{
+        font-size: 18px;
+        font-weight: bold;
+        margin-right: 7%;
+    }
+    .type-box{
+        background-color: #FF9900;
+        font-size: 15px;
+        font-weight: normal;
+        color: #fff;
+        padding: 3px 6px;
+        border-radius: 5px;
+        margin-right: 5px;
+    }
+}
+
+.info-box{
+    width: 70%;
+    height: 95%;
+    margin: auto;
+    padding: 20px 0;
+    font-size: 16px;
+
+    img{
+        width: 70%;
+        height: 400px;
+        margin-left: 15%;
+    }
+
+    // .sk-box{
+        .sk-title{
+            background-color: #eaeaea;
+            padding: 10px;
+            margin-bottom: 10px;
+            margin-top: 25px;
+        }
+        .sk-info{
+            // padding: 0 10px;
+            &>p {
+                margin: 10px;
+            }
+        }
+    // }
+}
+</style>

+ 314 - 0
TEAMModelOS/ClientApp/src/view/signupActivity/manageActivity.vue

@@ -0,0 +1,314 @@
+<template>
+    <div style="height: 100%;">
+        <div class="list-header">
+            <span>审核状态:</span>
+            <Select v-model="process" style="width: 200px">
+                <Option
+                    v-for="item in listType"
+                    :value="item.value"
+                    :key="item.value"
+                >
+                    {{ item.label }}
+                </Option>
+            </Select>
+            <span class="creat-activity" @click="createActivity">
+                <Icon type="md-add" />
+                创建活动
+            </span>
+        </div>
+        <vuescroll>
+            <div class="activitya-list">
+                <div
+                    class="activity-box"
+                    v-for="(item, index) in activityList"
+                    :key="index"
+                    @click="actInfo(item)"
+                >
+                    <div :class="['train-img-box',item.owner == 'area' ? 'area-train-bg': item.owner == 'school' ? 'school-train-bg' : '']">
+                        <div class="train-img" :style="{backgroundImage: `url(${item.img})`}"></div>
+                        <span class="train-type-label">
+                            {{item.owner == 'area' ? $t('train.mgt.areaLabel') : item.owner == 'school' ? $t('train.mgt.schoolLabel') : '公开'}}
+                        </span>
+                    </div>
+                    <!-- <img :src="item.img" /> -->
+                    <div class="content-box">
+                        <p>名称:{{ item.name }}</p>
+                        <p class="synopsis">简介:{{ item.jianjie }}</p>
+                        <p>地点:{{ item.address }}</p>
+                    </div>
+                </div>
+            </div>
+        </vuescroll>
+    </div>
+</template>
+
+<script>
+export default {
+    data() {
+        return {
+            listType: [
+                {
+                    label: "全部",
+                    value: -1,
+                },
+                {
+                    label: "已审核",
+                    value: 1,
+                },
+                {
+                    label: "未审核",
+                    value: 0,
+                },
+            ],
+            process: -1,
+            activityList: [],
+        }
+    },
+    created() {
+        this.getActivityList()
+    },
+    computed: {
+        isArea() {
+            return localStorage.getItem('platform')
+        },
+    },
+    methods: {
+        getActivityList() {
+            this.activityList = [
+                {
+                    id: 111,
+                    name: "第一个活动",
+                    title: '推动创新教学,全球智慧教师齐聚一堂,切磋交流、提炼优质智慧课堂',
+                    address: '办公大楼',
+                    host: '全球醍摩豆智慧教育研究院',
+                    jianjie: "「智慧課堂創新獎」選拔活動,匯集海內外全球智慧教師課堂影像,由專家學者評選出優秀作品,在全球智慧教師之夜頒獎,並收錄其作品成為經典課例,發展「可複製、會擴散」的智慧教室創新教學模式,散播智慧教育的種子。",
+                    tag: 1,
+                    type: 0,
+                    owner: 'area',
+                    img: require("./demo.jpeg"),
+                },
+                {
+                    id: 222,
+                    name: "第一个活动",
+                    title: "第一个活动",
+                    address: '办公大楼',
+                    host: '全球醍摩豆智慧教育研究院',
+                    jianjie:
+                        "这是一段简介,这是一段简介,这是一段简介,这是一段简介,这是一段简介,这是一段简介,",
+                    tag: 1,
+                    type: 1,
+                    owner: 'school',
+                    img: require("./demo.jpeg"),
+                },
+                {
+                    id: 333,
+                    name: "第一个活动",
+                    title: "第一个活动",
+                    address: '办公大楼',
+                    host: '全球醍摩豆智慧教育研究院',
+                    jianjie:
+                        "这是一段简介,这是一段简介,这是一段简介,这是一段简介,这是一段简介,这是一段简介,这是一段简介,这是一段简介,这是一段简介,这是一段简介,这是一段简介,这是一段简介,这是一段简介,这是一段简介,这是一段简介,这是一段简介,这是一段简介,这是一段简介,",
+                    tag: 1,
+                    type: 2,
+                    owner: 'open',
+                    img: require("./demo.jpeg"),
+                },
+                {
+                    id: 444,
+                    name: "某一城市的某个大学的某一学院的某个礼堂",
+                    title: "第一个活动",
+                    address: '办公大楼',
+                    host: '全球醍摩豆智慧教育研究院',
+                    jianjie:
+                        "这是一段简介,这是一段简介,这是一段简介,这是一段简介,这是一段简介,这是一段简介,",
+                    tag: 1,
+                    type: 1,
+                    owner: 'area',
+                    img: require("./demo.jpeg"),
+                },
+                {
+                    id: 444,
+                    name: "某一城市的某个大学的某一学院的某个礼堂",
+                    title: "第一个活动",
+                    address: '办公大楼',
+                    host: '全球醍摩豆智慧教育研究院',
+                    jianjie:
+                        "这是一段简介,这是一段简介,这是一段简介,这是一段简介,这是一段简介,这是一段简介,",
+                    tag: 1,
+                    type: 1,
+                    owner: 'area',
+                    img: require("./demo.jpeg"),
+                },
+                {
+                    id: 444,
+                    name: "某一城市的某个大学的某一学院的某个礼堂",
+                    title: "第一个活动",
+                    address: '办公大楼',
+                    host: '全球醍摩豆智慧教育研究院',
+                    jianjie:
+                        "这是一段简介,这是一段简介,这是一段简介,这是一段简介,这是一段简介,这是一段简介,",
+                    tag: 1,
+                    type: 1,
+                    img: require("./demo.jpeg"),
+                },
+                {
+                    id: 444,
+                    name: "某一城市的某个大学的某一学院的某个礼堂",
+                    title: "第一个活动",
+                    address: '办公大楼',
+                    host: '全球醍摩豆智慧教育研究院',
+                    jianjie:
+                        "这是一段简介,这是一段简介,这是一段简介,这是一段简介,这是一段简介,这是一段简介,",
+                    tag: 1,
+                    type: 1,
+                    owner: 'school',
+                    img: require("./demo.jpeg"),
+                },
+                {
+                    id: 444,
+                    name: "某一城市的某个大学的某一学院的某个礼堂",
+                    title: "第一个活动",
+                    address: '办公大楼',
+                    host: '全球醍摩豆智慧教育研究院',
+                    jianjie:
+                        "这是一段简介,这是一段简介,这是一段简介,这是一段简介,这是一段简介,这是一段简介,",
+                    tag: 1,
+                    type: 1,
+                    owner: 'area',
+                    img: require("./demo.jpeg"),
+                },
+            ]
+        },
+        createActivity() {
+            this.$router.push({
+                name: this.isArea === 'area' ? 'areaCreateActivity' : "createActivity",
+            })
+        },
+        actInfo(data) {
+            this.$router.push({
+                name: this.isArea === 'area' ? 'areaInfoActivity' : 'infoActivity',
+                params: {
+                    id: data
+                }
+            })
+        },
+    },
+}
+</script>
+
+<style scoped lang="less">
+.list-header {
+    height: 45px;
+    box-shadow: 0px 2px 5px #e9e9e9;
+    padding-left: 15px;
+    padding-right: 20px;
+    margin-bottom: 5px;
+    line-height: 45px;
+
+    .creat-activity {
+        float: right;
+        user-select: none;
+        cursor: pointer;
+        color: #40a8f0;
+    }
+}
+.activitya-list {
+    display: flex;
+    flex-wrap: wrap;
+    width: 100%;
+    height: 100%;
+    padding: 10px;
+    margin-bottom: 50px;
+
+    .activity-box {
+        margin: 15px 20px;
+        border: 1px solid #e5e5e5;
+        border-radius: 5px;
+        width: 30%;
+        cursor: pointer;
+        transition: all 0.2s ease 0s;
+        display: flex;
+
+        &:hover {
+            box-shadow: 0 26px 40px -24px #aaa;
+            transform: translateY(-4px);
+        }
+        .content-box{
+            width: 50%;
+            padding: 10px;
+            position: relative;
+            &>p{
+                margin-bottom: 10px;
+            }
+            .process-type{
+                position: absolute;
+                top: 50px;
+                right: 30px;
+                transform: rotate(-25deg);
+                &>span{
+                    background-color: #fff;
+                    font-size: 1.3rem;
+                    border: 1px dashed;
+                    padding: 7px 13px;
+                    border-radius: 5px;
+                }
+            }
+            .synopsis {
+                overflow: hidden; //超出的文本隐藏
+                text-overflow: ellipsis; //溢出用省略号显示
+                display: -webkit-box;
+                -webkit-line-clamp: 2; // 超出多少行
+                -webkit-box-orient: vertical;
+            }
+        }
+        .train-img-box{
+            /* width: 310px;
+            height: 150px; */
+            overflow: hidden;   
+            position: relative;
+            width: 50%;
+            img{
+                width: 100%;
+            }
+            .train-img{
+                transition: all 0.3s;
+                width: 310px;
+                height: 150px;
+                background-size: cover;
+                background-repeat: no-repeat;
+            }
+            .train-type-label{
+                position: absolute;
+                left: -2px;
+                top: 5px;
+                color: white;
+                font-size: 12px;
+                padding: 2px 5px;
+                z-index: 99;
+                transform: rotate(-45deg);
+            }
+            &::after {
+                position: absolute;
+                content: ' ';
+                left: -30px;
+                top: -7px;
+                z-index: 0;
+                border-right: 40px solid transparent;
+                border-left: 40px solid transparent;
+                border-bottom: 40px solid #a8a8a8;
+                transform: rotate(-45deg);
+            }
+        }
+        .school-train-bg{
+            &::after{
+                border-bottom: 40px solid #19be6b !important; 
+            }
+        }
+        .area-train-bg{
+            &::after{
+                border-bottom: 40px solid #ff9900 !important;
+            }
+        }
+    }
+}
+</style>

+ 279 - 0
TEAMModelOS/ClientApp/src/view/signupActivity/processActivity.vue

@@ -0,0 +1,279 @@
+<template>
+    <div style="height: 100%;">
+        <div class="list-header">
+            <RadioGroup v-model="processType" type="button" button-style="solid">
+                <Radio :label="-1">全部</Radio>
+                <Radio :label="0">未审核</Radio>
+                <Radio :label="1">已审核</Radio>
+            </RadioGroup>
+        </div>
+        <vuescroll>
+            <div class="activitya-list">
+                <div class="activity-box" v-for="(item, index) in activityListShow" :key="index" @click="goInfo(item)">
+                    <div :class="['train-img-box',item.owner == 'area' ? 'area-train-bg': item.owner == 'school' ? 'school-train-bg' : '']">
+                        <img :src="item.img" />
+                        <span class="train-type-label">
+                            {{item.owner == 'area' ? $t('train.mgt.areaLabel') : item.owner == 'school' ? $t('train.mgt.schoolLabel') : '公开'}}
+                        </span>
+                    </div>
+                    <div class="content-box">
+                        <p>{{ item.name }}</p>
+                        <p>主题:{{ item.title }}</p>
+                        <p>地点:{{ item.address }}</p>
+                        <p>主办:{{ item.host }}</p>
+                        <!-- <p>简介:{{ item.jianjie }}</p> -->
+                        <div class="process-type">
+                            <span v-show="!item.type" style="color: #ec8130;">待审核</span>
+                            <span v-show="item.type === 1" style="color: #1e9a1e;">已通过</span>
+                            <span v-show="item.type === 2" style="color: #e03f3f;">不通过</span>
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </vuescroll>
+    </div>
+</template>
+
+<script>
+export default {
+    data () {
+        return {
+            processType: -1,
+            activityList: [],
+            activityListShow: [],
+        }
+    },
+    created() {
+        this.getActivityList()
+    },
+    computed: {
+        isArea() {
+            return localStorage.getItem('platform')
+        },
+    },
+    watch: {
+        processType: {
+            handler(n, o) {
+                if(n === -1) {
+                    this.activityListShow = this.activityList
+                } else {
+                    this.activityListShow = this.activityList.filter(item => {
+                        return n ? (item.type === 1 || item.type === 2) : item.type === 0
+                    })
+                }
+            },
+        },
+    },
+    methods: {
+        getActivityList() {
+            this.activityList = [
+                {
+                    id: 111,
+                    name: "第一个活动",
+                    title: '推动创新教学,全球智慧教师齐聚一堂,切磋交流、提炼优质智慧课堂',
+                    address: '办公大楼',
+                    host: '全球醍摩豆智慧教育研究院',
+                    jianjie: "「智慧課堂創新獎」選拔活動,匯集海內外全球智慧教師課堂影像,由專家學者評選出優秀作品,在全球智慧教師之夜頒獎,並收錄其作品成為經典課例,發展「可複製、會擴散」的智慧教室創新教學模式,散播智慧教育的種子。",
+                    tag: 1,
+                    type: 0,
+                    owner: 'area',
+                    img: require("./demo.jpeg"),
+                },
+                {
+                    id: 222,
+                    name: "第一个活动",
+                    title: "第一个活动",
+                    address: '办公大楼',
+                    host: '全球醍摩豆智慧教育研究院',
+                    jianjie:
+                        "这是一段简介,这是一段简介,这是一段简介,这是一段简介,这是一段简介,这是一段简介,",
+                    tag: 1,
+                    type: 1,
+                    owner: 'school',
+                    img: require("./demo.jpeg"),
+                },
+                {
+                    id: 333,
+                    name: "第一个活动",
+                    title: "第一个活动",
+                    address: '办公大楼',
+                    host: '全球醍摩豆智慧教育研究院',
+                    jianjie:
+                        "这是一段简介,这是一段简介,这是一段简介,这是一段简介,这是一段简介,这是一段简介,这是一段简介,这是一段简介,这是一段简介,这是一段简介,这是一段简介,这是一段简介,这是一段简介,这是一段简介,这是一段简介,这是一段简介,这是一段简介,这是一段简介,",
+                    tag: 1,
+                    type: 2,
+                    owner: 'open',
+                    img: require("./demo.jpeg"),
+                },
+                {
+                    id: 444,
+                    name: "某一城市的某个大学的某一学院的某个礼堂",
+                    title: "第一个活动",
+                    address: '办公大楼',
+                    host: '全球醍摩豆智慧教育研究院',
+                    jianjie:
+                        "这是一段简介,这是一段简介,这是一段简介,这是一段简介,这是一段简介,这是一段简介,",
+                    tag: 1,
+                    type: 1,
+                    owner: 'area',
+                    img: require("./demo.jpeg"),
+                },
+                {
+                    id: 444,
+                    name: "某一城市的某个大学的某一学院的某个礼堂",
+                    title: "第一个活动",
+                    address: '办公大楼',
+                    host: '全球醍摩豆智慧教育研究院',
+                    jianjie:
+                        "这是一段简介,这是一段简介,这是一段简介,这是一段简介,这是一段简介,这是一段简介,",
+                    tag: 1,
+                    type: 1,
+                    owner: 'area',
+                    img: require("./demo.jpeg"),
+                },
+                {
+                    id: 444,
+                    name: "某一城市的某个大学的某一学院的某个礼堂",
+                    title: "第一个活动",
+                    address: '办公大楼',
+                    host: '全球醍摩豆智慧教育研究院',
+                    jianjie:
+                        "这是一段简介,这是一段简介,这是一段简介,这是一段简介,这是一段简介,这是一段简介,",
+                    tag: 1,
+                    type: 1,
+                    img: require("./demo.jpeg"),
+                },
+                {
+                    id: 444,
+                    name: "某一城市的某个大学的某一学院的某个礼堂",
+                    title: "第一个活动",
+                    address: '办公大楼',
+                    host: '全球醍摩豆智慧教育研究院',
+                    jianjie:
+                        "这是一段简介,这是一段简介,这是一段简介,这是一段简介,这是一段简介,这是一段简介,",
+                    tag: 1,
+                    type: 1,
+                    owner: 'school',
+                    img: require("./demo.jpeg"),
+                },
+                {
+                    id: 444,
+                    name: "某一城市的某个大学的某一学院的某个礼堂",
+                    title: "第一个活动",
+                    address: '办公大楼',
+                    host: '全球醍摩豆智慧教育研究院',
+                    jianjie:
+                        "这是一段简介,这是一段简介,这是一段简介,这是一段简介,这是一段简介,这是一段简介,",
+                    tag: 1,
+                    type: 1,
+                    owner: 'area',
+                    img: require("./demo.jpeg"),
+                },
+            ]
+            this.activityListShow = this.activityList
+        },
+        goInfo(data) {
+            this.$router.push(
+                {
+                    name: this.isArea === 'area' ? 'areaInfoProcess' : 'infoProcess',
+                    params: {
+                        info: data
+                    },
+                })
+        },
+    }
+}
+</script>
+
+<style scoped lang="less">
+.list-header {
+    height: 45px;
+    box-shadow: 0px 2px 5px #e9e9e9;
+    padding-left: 15px;
+    padding-right: 20px;
+    margin-bottom: 5px;
+    line-height: 45px;
+}
+.activitya-list {
+    display: flex;
+    flex-wrap: wrap;
+    width: 100%;
+    height: 100%;
+    padding: 10px;
+    margin-bottom: 50px;
+    .activity-box {
+        margin: 15px 20px;
+        border: 1px solid #e5e5e5;
+        border-radius: 5px;
+        width: 17%;
+        cursor: pointer;
+        transition: all 0.2s ease 0s;
+
+        &:hover {
+            box-shadow: 0 26px 40px -24px #aaa;
+            transform: translateY(-4px);
+        }
+
+        img {
+            width: 100%;
+        }
+        .content-box{
+            padding: 10px;
+            position: relative;
+            &>p{
+                margin-bottom: 10px;
+            }
+            .process-type{
+                position: absolute;
+                top: 50px;
+                right: 30px;
+                transform: rotate(-25deg);
+                &>span{
+                    background-color: #fff;
+                    font-size: 1.3rem;
+                    border: 1px dashed;
+                    padding: 7px 13px;
+                    border-radius: 5px;
+                }
+            }
+        }
+        .train-img-box{
+            /* width: 310px;
+            height: 150px; */
+            overflow: hidden;   
+            position: relative;
+            .train-type-label{
+                position: absolute;
+                left: -2px;
+                top: 5px;
+                color: white;
+                font-size: 12px;
+                padding: 2px 5px;
+                z-index: 99;
+                transform: rotate(-45deg);
+            }
+            &::after {
+                position: absolute;
+                content: ' ';
+                left: -30px;
+                top: -7px;
+                z-index: 0;
+                border-right: 40px solid transparent;
+                border-left: 40px solid transparent;
+                border-bottom: 40px solid #a8a8a8;
+                transform: rotate(-45deg);
+            }
+        }
+        .school-train-bg{
+            &::after{
+                border-bottom: 40px solid #19be6b !important; 
+            }
+        }
+        .area-train-bg{
+            &::after{
+                border-bottom: 40px solid #ff9900 !important;
+            }
+        }
+    }
+}
+</style>

+ 18 - 1
TEAMModelOS/ClientApp/src/view/syllabus/Syllabus.vue

@@ -415,6 +415,7 @@ export default {
   },
   data() {
     return {
+      curSemesterTimeRange:null,
       importNodes: null,
       excelData: null,
       previewTree: null,
@@ -2841,7 +2842,23 @@ export default {
           this.currentPeriodIndex = n.periodIndex
         }
       }
-    }
+    },
+    // '$store.state.user.curSemester': {
+    //   deep: true,
+    //   immediate: true,
+    //   handler(n, old) {
+    //     if (n && this.$route.name === 'syllabus') {
+    //       this.nodeItems = []
+    //       this.tabResources = []
+    //       let curPeriodIndex = this.$store.state.user.curPeriod.periodIndex
+    //       this.curSemesterTimeRange = this.$tools.getStTimeByYearAndSemester(n.year,n.index)
+    //       console.error(n);
+    //       console.error(this.curSemesterTimeRange);
+    //       this.getSchoolInfo(curPeriodIndex)
+    //       this.currentPeriodIndex = curPeriodIndex
+    //     }
+    //   }
+    // }
   }
 }
 </script>

Plik diff jest za duży
+ 1212 - 1193
TEAMModelOS/ClientApp/src/view/vote/ManageVote.vue


+ 249 - 9
TEAMModelOS/Controllers/Both/CourseBaseController.cs

@@ -28,6 +28,8 @@ using OpenXmlPowerTools;
 using DocumentFormat.OpenXml.Drawing.Spreadsheet;
 using System.Text.RegularExpressions;
 using System.Security.Claims;
+using DocumentFormat.OpenXml.Bibliography;
+using DocumentFormat.OpenXml.Spreadsheet;
 
 namespace TEAMModelOS.Controllers.Both
 {
@@ -40,13 +42,14 @@ namespace TEAMModelOS.Controllers.Both
     {
         private AzureCosmosFactory _azureCosmos;
         private readonly DingDing _dingDing;
+        private readonly CoreAPIHttpService _coreAPIHttpService;
         private readonly Option _option;
         private readonly AzureServiceBusFactory _serviceBus;
         private readonly AzureStorageFactory _azureStorage;
         private readonly AzureRedisFactory _azureRedis;
         private static List<string> weekDays = new List<string> { "MON", "TUE", "WED", "THU", "FRI", "SAT", "SUN" };
         public IConfiguration _configuration { get; set; }
-        public CourseBaseController(AzureRedisFactory azureRedis, AzureCosmosFactory azureCosmos, DingDing dingDing, IOptionsSnapshot<Option> option, AzureServiceBusFactory serviceBus, AzureStorageFactory azureStorage, IConfiguration configuration)
+        public CourseBaseController(AzureRedisFactory azureRedis, AzureCosmosFactory azureCosmos, DingDing dingDing, IOptionsSnapshot<Option> option, CoreAPIHttpService coreAPIHttpService, AzureServiceBusFactory serviceBus, AzureStorageFactory azureStorage, IConfiguration configuration)
         {
             _azureCosmos = azureCosmos;
             _dingDing = dingDing;
@@ -54,7 +57,7 @@ namespace TEAMModelOS.Controllers.Both
             _serviceBus = serviceBus;
             _configuration = configuration;
             _azureStorage = azureStorage;
-            _azureRedis = azureRedis;
+            _azureRedis = azureRedis; _coreAPIHttpService = coreAPIHttpService;
         }
         /// <summary>
         /// 更新保存课程
@@ -104,7 +107,7 @@ namespace TEAMModelOS.Controllers.Both
                             string tbname = $"{scope}".Equals("school", StringComparison.OrdinalIgnoreCase) ? Constant.School : Constant.Teacher;
                             Azure.Response response = await client.GetContainer(Constant.TEAMModelOS, tbname).DeleteItemStreamAsync(_id.ToString(), new PartitionKey($"CourseBase-{_code}"));
                             //需要联动删除排课
-                            string taskCode = $"CourseTask-{_code}";
+                            string taskCode = $"{scope}".Equals("school", StringComparison.OrdinalIgnoreCase) ? $"CourseTask-{_code}": "CourseTask";
                             string taskSql = $"select value c from c where c.courseId='{_id}'";
                             List<CourseTask> courseTasks = new List<CourseTask>();
                             await foreach (var item in _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, tbname)
@@ -460,7 +463,7 @@ namespace TEAMModelOS.Controllers.Both
                             }
                             else
                             {
-                                string taskCode = $"CourseTask-{tmdid}";
+                                string taskCode = $"CourseTask";
                                 string taskId = $"{_courseId}";
                                 Azure.Response response = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, tbname).ReadItemStreamAsync(taskId, new PartitionKey(taskCode));
                                 CourseTask courseTask = default;
@@ -653,7 +656,7 @@ namespace TEAMModelOS.Controllers.Both
                             }
                             else
                             {
-                                string taskCode = $"CourseTask-{tmdid}";
+                                string taskCode = $"CourseTask";
                                 string taskId = $"{_courseId}";
                                 CourseTask courseTask = null;
                                 CourseBase courseBase = null;
@@ -971,7 +974,7 @@ namespace TEAMModelOS.Controllers.Both
                                     var courseTaskInsert = SchoolService.CheckCourseTaskInsertOrChanged($"{grant_type}", scope.ToString(), data, null, null, courseBases, groupListDtos, rooms, null, teachers);
                                     if (courseTaskInsert.invalidCode == 0)
                                     {
-                                        string taskCode = $"CourseTask-{tmdid}";
+                                        string taskCode = $"CourseTask";
                                         string taskId = courseTaskInsert.courseId;
                                         CourseTask courseTask = courseTasks.Where(z => z.id.Equals(taskId) && z.code.Equals(taskCode)).FirstOrDefault();
                                         if (courseTask == null)
@@ -1664,6 +1667,116 @@ namespace TEAMModelOS.Controllers.Both
             return Ok();
         }
 
+        /// <summary>
+        /// 教师任教的课程
+        /// </summary>
+        /// <param name="request"></param>
+        /// <returns></returns>
+        [ProducesDefaultResponseType]
+        [AuthToken(Roles = "student")]
+        [HttpPost("student")]
+        public async Task<IActionResult> Student(JsonElement request) {
+            try
+            {
+                (string userid, _, _, string school) = HttpContext.GetAuthTokenInfo();
+                if (!request.TryGetProperty("grant_type", out JsonElement grant_type)) return BadRequest();
+                string code = $"CourseTask-{school}";
+                var client = _azureCosmos.GetCosmosClient();
+                object scope = null;
+                HttpContext?.Items.TryGetValue("Scope", out scope);
+                int memberType = 2;
+                if ($"{scope}".Equals(Constant.ScopeStudent))
+                {
+                    memberType = 2;
+                    // Student student = await client.GetContainer(Constant.TEAMModelOS, "Student").ReadItemAsync<Student>(userid, new PartitionKey($"Base-{school}"));
+                }
+                if ($"{scope}".Equals(Constant.ScopeTmdUser))
+                {
+                    memberType = 1;
+                }
+                if ($"{scope}".Equals(Constant.ScopeTeacher))
+                {
+                    memberType = 1;
+                }
+                List<GroupListGrp> groups = await GroupListService.GetMemberInGroupList(_coreAPIHttpService, client, _dingDing, userid, memberType, school, new List<string> { "class", "teach" });
+                if (groups.IsNotEmpty()) {
+                    switch (true)
+                    {
+                        //学生应参与的课程
+                        case bool when $"{grant_type}".Equals("attend", StringComparison.OrdinalIgnoreCase):
+                            string sql = $"SELECT distinct value c FROM c  join b in c.schedules where c.pk='CourseTask' and b.groupId in() ";
+                            List<KeyValuePair<string, CourseTask>> schoolTeacherTask = new List<KeyValuePair<string, CourseTask>>();
+                            List<KeyValuePair<string, CourseTask>> schoolAssistantTask = new List<KeyValuePair<string, CourseTask>>();
+
+                            List<KeyValuePair<string, CourseTask>> privateTeacherTask = new List<KeyValuePair<string, CourseTask>>();
+                            List<KeyValuePair<string, CourseTask>> privateAssistantTask = new List<KeyValuePair<string, CourseTask>>();
+                            List<CourseDto> schoolCourses = new List<CourseDto>();
+                            List<CourseDto> teahcerCourses = new List<CourseDto>();
+                            if (!string.IsNullOrWhiteSpace(school))
+                            {
+                                School schoolBase = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.School).ReadItemAsync<School>(school, new PartitionKey("Base"));
+                                JsonElement _year = default, _semesterId = default, _periodId = default;
+                                if (!request.TryGetProperty("periodId", out _periodId)) return BadRequest();
+                                if (!request.TryGetProperty("year", out _year))
+                                {
+                                    return BadRequest();
+                                }
+                                if (!request.TryGetProperty("semesterId", out _semesterId))
+                                {
+                                    return BadRequest();
+                                }
+                                var period = schoolBase.period.Find(x => x.id.Equals($"{_periodId}"));
+                                //string date = SchoolService.GetOpensByStudyYearAndSemester(period.semesters, int.Parse($"{_year}"), $"{_semesterId}");
+                                sql = $"{sql} and  c.year={_year} and c.semesterId='{_semesterId}'";
+                                HashSet<string> courseIds = new HashSet<string>();
+                                var resultSchool = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.School).GetList<CourseTask>(sql, $"CourseTask-{school}");
+                                if (resultSchool.list.IsNotEmpty())
+                                {
+
+                                }
+                                if (courseIds.Any())
+                                {
+                                    string sqlCourse = $"select value c from c where c.id in ({string.Join(",", courseIds.Select(b => $"'{b}'"))})";
+                                    var result = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.School).GetList<CourseBase>(sqlCourse, $"CourseBase-{school}");
+                                    if (result.list.IsNotEmpty())
+                                    {
+                                        foreach (var item in result.list)
+                                        {
+                                            List<CourseTaskDto> courseTaskDtos = new List<CourseTaskDto>();
+                                            var teacher = schoolTeacherTask.Where(x => x.Key.Equals(item.id)).Select(z => new CourseTaskDto { courseTask=z.Value, type="teacher" });
+                                            if (teacher.Any())
+                                            {
+                                                courseTaskDtos.AddRange(teacher.ToList());
+                                            }
+                                            var assistant = schoolAssistantTask.Where(x => x.Key.Equals(item.id)).Select(z => new CourseTaskDto { courseTask=z.Value, type="assistant" });
+                                            if (assistant.Any())
+                                            {
+                                                courseTaskDtos.AddRange(assistant.ToList());
+                                            }
+                                            schoolCourses.Add(new CourseDto { courseBase=item, courseTasks=courseTaskDtos });
+                                        }
+                                    }
+                                }
+                            }
+                            var resultTeacher = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Teacher).GetList<CourseTask>(sql, $"CourseTask");
+                            if (resultTeacher.list.IsNotEmpty())
+                            {
+                                HashSet<string> courseIds = new HashSet<string>();
+
+                            }
+                            return Ok(new { teahcerCourses, schoolCourses });
+
+                    }
+                }
+            }
+            catch (Exception ex)
+            {
+                await _dingDing.SendBotMsg($"{_option.Location},course-base\teacher{ex.Message}\n{ex.StackTrace}", GroupNames.成都开发測試群組);
+                return BadRequest(new { ex.Message });
+            }
+            return Ok();
+        }
+
         /// <summary>
         /// 教师任教的课程
         /// </summary>
@@ -1679,24 +1792,151 @@ namespace TEAMModelOS.Controllers.Both
             {
                 (string tmdid, _, _, string school) = HttpContext.GetAuthTokenInfo();
                 if (!request.TryGetProperty("grant_type", out JsonElement grant_type)) return BadRequest();
-                if (!request.TryGetProperty("scope", out JsonElement scope)) return BadRequest();
+                string code = $"CourseTask-{school}";
                 var client = _azureCosmos.GetCosmosClient();
                 switch (true)
                 {
                     //任教学校课程和个人课程
                     case bool when $"{grant_type}".Equals("teach", StringComparison.OrdinalIgnoreCase):
+                        string sql  = $"SELECT distinct value c FROM c  join b in c.schedules where c.pk='CourseTask' and (ARRAY_CONTAINS(b.assistants,'{tmdid}')or  b.teacherId  ='{tmdid}' )";
+                        List<KeyValuePair<string,CourseTask >> schoolTeacherTask = new List<KeyValuePair<string, CourseTask>>();
+                        List<KeyValuePair<string,CourseTask >> schoolAssistantTask = new  List<KeyValuePair<string,CourseTask >>();
+
+                        List<KeyValuePair<string,CourseTask >> privateTeacherTask = new  List<KeyValuePair<string,CourseTask >>();
+                        List<KeyValuePair<string,CourseTask >> privateAssistantTask = new  List<KeyValuePair<string,CourseTask >>();
+                        List<CourseDto> schoolCourses = new List<CourseDto>();
+                        List<CourseDto> teahcerCourses = new List<CourseDto>();
+                        if (!string.IsNullOrWhiteSpace(school)) {
+                            School schoolBase = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.School).ReadItemAsync<School>(school, new PartitionKey("Base"));
+                            JsonElement _year = default, _semesterId = default, _periodId = default;
+                            if (!request.TryGetProperty("periodId", out _periodId)) return BadRequest();
+                            if (!request.TryGetProperty("year", out _year))
+                            {
+                                return BadRequest();
+                            }
+                            if (!request.TryGetProperty("semesterId", out _semesterId))
+                            {
+                                return BadRequest();
+                            }
+                            var period = schoolBase.period.Find(x => x.id.Equals($"{_periodId}"));
+                            //string date = SchoolService.GetOpensByStudyYearAndSemester(period.semesters, int.Parse($"{_year}"), $"{_semesterId}");
+                            sql = $"{sql} and  c.year={_year} and c.semesterId='{_semesterId}'";
+                            HashSet<string> courseIds = new HashSet<string>();
+                            var resultSchool= await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.School).GetList<CourseTask>(sql, $"CourseTask-{school}");
+                            if (resultSchool.list.IsNotEmpty()) {
+                                resultSchool.list.ForEach(x => {
+                                   var schedulesTeacher =   x.schedules.Where(z =>!string.IsNullOrWhiteSpace(z.teacherId)   && z.teacherId.Equals(tmdid));
+                                    if (schedulesTeacher.Any()) 
+                                    {
+                                        courseIds.Add(x.courseId);
+                                        CourseTask courseTask = x.ToJsonString().ToObject<CourseTask>();
+                                        courseTask.schedules=schedulesTeacher.ToList();
+                                        schoolTeacherTask.Add(new KeyValuePair<string,CourseTask  >(x.courseId, courseTask));
+                                    }
+                                    var schedulesAssistant = x.schedules.Where(z => z.assistants.Contains(tmdid) );
+                                    if (schedulesAssistant.Any())
+                                    {
+                                        courseIds.Add(x.courseId);
+                                        CourseTask courseTask = x.ToJsonString().ToObject<CourseTask>();
+                                        courseTask.schedules=schedulesTeacher.ToList();
+                                        schoolAssistantTask.Add(new KeyValuePair<string, CourseTask>(x.courseId, courseTask));
+                                    }
+                                });
+                            }
+                            if (courseIds.Any()) {
+                                string sqlCourse = $"select value c from c where c.id in ({string.Join(",",courseIds.Select(b=>$"'{b}'"))})";
+                                var result = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.School).GetList<CourseBase>(sqlCourse, $"CourseBase-{school}");
+                                if (result.list.IsNotEmpty()) 
+                                {
+                                    foreach (var item in result.list) 
+                                    {
+                                        List<CourseTaskDto> courseTaskDtos = new List<CourseTaskDto>();
+                                        var teacher = schoolTeacherTask.Where(x => x.Key.Equals(item.id)).Select(z => new CourseTaskDto { courseTask=z.Value, type="teacher" });
+                                        if (teacher.Any()) {
+                                            courseTaskDtos.AddRange(teacher.ToList());
+                                        }
+                                        var assistant= schoolAssistantTask.Where(x => x.Key.Equals(item.id)).Select(z => new CourseTaskDto {  courseTask=z.Value, type="assistant" });
+                                        if (assistant.Any()) {
+                                            courseTaskDtos.AddRange(assistant.ToList());
+                                        }
+                                        schoolCourses.Add(new CourseDto { courseBase=item, courseTasks=courseTaskDtos });
+                                    }
+                                }
+                            }
+                        }
+                        var resultTeacher = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Teacher).GetList<CourseTask>(sql, $"CourseTask");
+                        if (resultTeacher.list.IsNotEmpty()){
+                            HashSet<string> courseIds = new HashSet<string>();
+                            resultTeacher.list.ForEach(x => {
+                                var schedulesTeacher = x.schedules.Where(z => !string.IsNullOrWhiteSpace(z.teacherId)  &&  z.teacherId.Equals(tmdid));
+                                if (schedulesTeacher.Any())
+                                {
+                                    courseIds.Add(x.courseId);
+                                    CourseTask courseTask = x.ToJsonString().ToObject<CourseTask>();
+                                    courseTask.schedules=schedulesTeacher.ToList();
+                                    privateTeacherTask.Add(new KeyValuePair<string,CourseTask >(x.courseId, courseTask));
+                                }
+                                var schedulesAssistant = x.schedules.Where(z => z.assistants.Contains(tmdid));
+                                if (schedulesAssistant.Any())
+                                {
+                                    courseIds.Add(x.courseId);
+                                    CourseTask courseTask = x.ToJsonString().ToObject<CourseTask>();
+                                    courseTask.schedules=schedulesTeacher.ToList();
+                                    privateAssistantTask.Add(new KeyValuePair<string,CourseTask >(x.courseId, courseTask));
+                                }
+                            });
+                            if (courseIds.Any())
+                            {
+                                string sqlCourse = $"select value c from c where c.id in ({string.Join(",", courseIds.Select(b => $"'{b}'"))})";
+                                var result = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.School).GetList<CourseBase>(sqlCourse, $"CourseBase");
+                                if (result.list.IsNotEmpty())
+                                {
+                                    foreach (var item in result.list)
+
+                                    {
+                                        List<CourseTaskDto> courseTaskDtos = new List<CourseTaskDto>();
+                                        var teacher = privateTeacherTask.Where(x => x.Key.Equals(item.id)).Select(z => new CourseTaskDto { courseTask=z.Value, type="teacher" });
+                                        if (teacher.Any())
+                                        {
+                                            courseTaskDtos.AddRange(teacher.ToList());
+                                        }
+                                        var assistant = privateAssistantTask.Where(x => x.Key.Equals(item.id)).Select(z => new CourseTaskDto {   courseTask=z.Value, type="assistant" });
+                                        if (assistant.Any())
+                                        {
+                                            courseTaskDtos.AddRange(assistant.ToList());
+                                        }
+                                        teahcerCourses.Add(new CourseDto { courseBase=item, courseTasks=courseTaskDtos });
+                                    }
+                                }
+                            }
+                        }
                         //个人,
                         //学校,
                         //助教,学校。
                         //助教,个人。
-                        break;
+                        return Ok(new { teahcerCourses , schoolCourses});
+                        
                 }
             }
             catch (Exception ex)
             {
-
+               await _dingDing.SendBotMsg($"{_option.Location},course-base\teacher{ex.Message}\n{ex.StackTrace}",GroupNames.成都开发測試群組);
+                return BadRequest(new { ex.Message});
             }
             return Ok();
         }
     }
+
+    public class CourseDto { 
+        public CourseBase courseBase { get; set; }
+        public List<CourseTaskDto> courseTasks { get; set; }
+      
+    }
+    public class CourseTaskDto {
+        public CourseTask courseTask { get; set; }
+        /// <summary>
+        /// teacher主任课教师,assistant,协同教师
+        /// </summary>
+        public string type { get; set; }
+    }
 }

+ 58 - 2
TEAMModelOS/Controllers/Both/GroupListController.cs

@@ -832,7 +832,9 @@ namespace TEAMModelOS.Controllers
         [ProducesDefaultResponseType]
         [AuthToken(Roles = "teacher,admin")]
         [HttpPost("upsert-grouplist")]
-        [Authorize(Roles = "IES")]
+#if !DEBUG
+ [Authorize(Roles = "IES")]
+#endif
         public async Task<IActionResult> UpsertGroupList(JsonElement  json)
         {
 
@@ -846,7 +848,7 @@ namespace TEAMModelOS.Controllers
                 }
                 list.year = list.year > 0 ? list.year : DateTimeOffset.UtcNow.Year;
                 list.ttl = -1;
-                 
+                
                 list.creatorId = userid;
                 list.school = string.IsNullOrEmpty(list.school) ? school : list.school;
                 list.pk = "GroupList";
@@ -854,6 +856,60 @@ namespace TEAMModelOS.Controllers
                 {
                     //私人名单
                     list.code = "GroupList";
+                    //只有开启审核,且设置天数大于零,且之前没有设置过期时间的时候才会重新设置过期时间。
+                    if (list.review==1)
+                    {
+                        if (list.qrcodeDays>0)
+                        {
+                            GroupList dblist = null;
+                            if (!string.IsNullOrWhiteSpace(list.id))
+                            {
+                                var response = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Teacher).ReadItemStreamAsync(list.id, new PartitionKey(list.code));
+                                if (response.Status==200)
+                                {
+                                    dblist = JsonDocument.Parse(response.Content).RootElement.ToObject<GroupList>();
+                                  
+                                }
+                            }
+                            if (dblist!=null)
+                            {
+                                //更新过期时间
+                                if (dblist.qrcodeDays!=list.qrcodeDays)
+                                {
+                                    list.qrcodeExpire= DateTimeOffset.UtcNow.AddDays(list.qrcodeDays).ToUnixTimeMilliseconds();
+                                    string key = $"GroupList:Waitinglist:{list.scope}:{list.id}";
+                                    await _azureRedis.GetRedisClient(8).KeyExpireAsync(key, expiry: DateTimeOffset.FromUnixTimeMilliseconds(list.qrcodeExpire).UtcDateTime);
+                                }
+                                else {
+                                    //相同
+                                    if (dblist.review==0  && list.review==1)
+                                    {
+                                        list.qrcodeExpire= DateTimeOffset.UtcNow.AddDays(list.qrcodeDays).ToUnixTimeMilliseconds();
+                                        string key = $"GroupList:Waitinglist:{list.scope}:{list.id}";
+                                        await _azureRedis.GetRedisClient(8).KeyExpireAsync(key, expiry: DateTimeOffset.FromUnixTimeMilliseconds(list.qrcodeExpire).UtcDateTime);
+                                    }
+                                    else {
+                                        list.qrcodeExpire=dblist.qrcodeExpire;
+                                    }
+                                }
+                            }
+                            else {
+                                if (list.qrcodeExpire<=0)
+                                {
+                                    list.qrcodeExpire= DateTimeOffset.UtcNow.AddDays(list.qrcodeDays).ToUnixTimeMilliseconds();
+                                }
+                            }
+                        }
+                        else
+                        {
+                            return Ok(new { error = 400, msg = "必须设置二维码过期天数!" });
+                        }
+                    }
+                    else
+                    {
+                        list.qrcodeDays = 1;
+                        list.qrcodeExpire = 0;
+                    }
                     list.school = null;
                 }
                 else