Преглед изворни кода

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

zhouj1203@hotmail.com пре 1 година
родитељ
комит
7e79bbd03d
45 измењених фајлова са 4200 додато и 204 уклоњено
  1. 6 2
      TEAMModelOS/ClientApp/public/lang/en-US.js
  2. 6 2
      TEAMModelOS/ClientApp/public/lang/zh-CN.js
  3. 6 2
      TEAMModelOS/ClientApp/public/lang/zh-TW.js
  4. 4 2
      TEAMModelOS/ClientApp/src/components/questionnaire/BaseJudge.vue
  5. 3 1
      TEAMModelOS/ClientApp/src/components/questionnaire/BaseMultiple.vue
  6. 3 2
      TEAMModelOS/ClientApp/src/components/questionnaire/BaseSingle.vue
  7. 1 0
      TEAMModelOS/ClientApp/src/view/mycourse/MyCourse.less
  8. 8 2
      TEAMModelOS/ClientApp/src/view/mycourse/MyCourse.vue
  9. 5 0
      TEAMModelOS/ClientApp/src/view/mycourse/exam/Exam.less
  10. 24 2
      TEAMModelOS/ClientApp/src/view/mycourse/exam/Exam.vue
  11. 6 0
      TEAMModelOS/ClientApp/src/view/mycourse/record/Record.less
  12. 22 19
      TEAMModelOS/ClientApp/src/view/mycourse/record/Record.vue
  13. 4 4
      TEAMModelOS/ClientApp/src/view/mycourse/score/AddProject.vue
  14. 0 4
      TEAMModelOS/ClientApp/src/view/mycourse/score/DialogBox.less
  15. 7 1
      TEAMModelOS/ClientApp/src/view/mycourse/score/DialogBoxAddTable.vue
  16. 65 58
      TEAMModelOS/ClientApp/src/view/mycourse/score/DialogBoxStudentScore.vue
  17. 86 70
      TEAMModelOS/ClientApp/src/view/mycourse/score/ScoreBody.vue
  18. 80 0
      TEAMModelOS/ClientApp/src/view/mycourse/syllabus/DialogBox/AddCard.vue
  19. 102 0
      TEAMModelOS/ClientApp/src/view/mycourse/syllabus/DialogBox/AddResources.vue
  20. 70 0
      TEAMModelOS/ClientApp/src/view/mycourse/syllabus/DialogBox/DialogBox.less
  21. 50 0
      TEAMModelOS/ClientApp/src/view/mycourse/syllabus/DialogBox/DialogBoxTemp.vue
  22. 63 0
      TEAMModelOS/ClientApp/src/view/mycourse/syllabus/DialogBox/ReleaseSyllabus.vue
  23. 319 0
      TEAMModelOS/ClientApp/src/view/mycourse/syllabus/DialogBox/Resources/A_Evaluate.vue
  24. 273 0
      TEAMModelOS/ClientApp/src/view/mycourse/syllabus/DialogBox/Resources/A_Homework.vue
  25. 356 0
      TEAMModelOS/ClientApp/src/view/mycourse/syllabus/DialogBox/Resources/A_Questionnaire.vue
  26. 312 0
      TEAMModelOS/ClientApp/src/view/mycourse/syllabus/DialogBox/Resources/A_Vote.vue
  27. 60 0
      TEAMModelOS/ClientApp/src/view/mycourse/syllabus/DialogBox/Resources/C_CloudContent.vue
  28. 78 0
      TEAMModelOS/ClientApp/src/view/mycourse/syllabus/DialogBox/Resources/C_LocalFile.vue
  29. 99 0
      TEAMModelOS/ClientApp/src/view/mycourse/syllabus/DialogBox/Resources/C_URL.vue
  30. 454 0
      TEAMModelOS/ClientApp/src/view/mycourse/syllabus/DialogBox/Resources/Resources.less
  31. 192 0
      TEAMModelOS/ClientApp/src/view/mycourse/syllabus/DialogBox/Resources/part/BaseQnForm.vue
  32. 52 0
      TEAMModelOS/ClientApp/src/view/mycourse/syllabus/DialogBox/SyllabusAdd.vue
  33. 103 0
      TEAMModelOS/ClientApp/src/view/mycourse/syllabus/DialogBox/SyllabusSorting.vue
  34. 90 0
      TEAMModelOS/ClientApp/src/view/mycourse/syllabus/DialogBox/SyncClass.vue
  35. 14 0
      TEAMModelOS/ClientApp/src/view/mycourse/syllabus/SyllabusIndex.less
  36. 137 0
      TEAMModelOS/ClientApp/src/view/mycourse/syllabus/SyllabusIndex.vue
  37. 90 0
      TEAMModelOS/ClientApp/src/view/mycourse/syllabus/SyllabusMenu.less
  38. 203 0
      TEAMModelOS/ClientApp/src/view/mycourse/syllabus/SyllabusMenu.vue
  39. 146 0
      TEAMModelOS/ClientApp/src/view/mycourse/syllabus/SyllabusResource.less
  40. 258 0
      TEAMModelOS/ClientApp/src/view/mycourse/syllabus/SyllabusResource.vue
  41. 39 0
      TEAMModelOS/ClientApp/src/view/mycourse/syllabus/SyllabusSchedule.less
  42. 155 0
      TEAMModelOS/ClientApp/src/view/mycourse/syllabus/SyllabusSchedule.vue
  43. 15 0
      TEAMModelOS/ClientApp/src/view/mycourse/syllabus/addSyllabus.less
  44. 100 0
      TEAMModelOS/ClientApp/src/view/mycourse/syllabus/addSyllabus.vue
  45. 34 33
      TEAMModelOS/Controllers/Both/ScoreCalcController.cs

+ 6 - 2
TEAMModelOS/ClientApp/public/lang/en-US.js

@@ -1151,6 +1151,9 @@ const LANG_EN_US = {
         cusTab10: 'Create Announcement',
         cusTab11: 'Create Survey',
         cusTab12: 'Create Poll',
+        cusTab13: 'Syllabus',
+        cusTab14: 'Sorting Syllabus',
+        cusTab15: 'Add New Syllabus',
         fullTips1: "School space is full, can't create school course assessment",
         fullTips2: "Personal space is full, can't create personal course evaluation",
         createPrivCus: 'Create Personal Course',
@@ -7737,12 +7740,13 @@ const LANG_EN_US = {
         dataIsSave: 'Data has been saved',
         remind: 'Reminder',
         remindMessage1: 'There are unsaved settings, do you want to continue?',
-        remindMessage2: 'There are unsaved settings, do you want to continue?',
+        remindMessage2: 'There are unsaved settings, do you want to continue?',        
         deleteRemind1: 'Delete Item',
         remindMessage3: 'The data will not be retrieved after deletion. Do you want to delete it? ',
         deleteRemind2: 'Delete Activity Item',
         remindMessage4: 'The activity item has been deleted',
         remindMessage5: 'Score login completed',
+        remindMessage6: 'There are unsaved settings, do you want to continue?',
         scoreCalc: ' Score Calculation',
         modifyContent: ' can be modified',
         ask: 'Note',
@@ -7754,6 +7758,6 @@ const LANG_EN_US = {
         chooseAddWay:'Please choose how to add a score sheet',
         addNewTable:'Added "new" score sheet',
         InheritTable:'Inherit the grade list',
-        addTableNotice:'Notice: The newly added score sheet results are based on the current system scores. The score sheet results are not linked to other functions',          
+        addTableNotice:'Note: The newly added score sheet will be based on the existing scores in the current system. That the newly created score sheet will not be affected or linked to other functions',          
     }
 }

+ 6 - 2
TEAMModelOS/ClientApp/public/lang/zh-CN.js

@@ -1151,6 +1151,9 @@ const LANG_ZH_CN = {
         cusTab10: '新建公告',
         cusTab11: '新建问卷',
         cusTab12: '新建投票',
+        cusTab13: '课堂课纲',
+        cusTab14: '排序课纲',
+        cusTab15: '新增课纲',
         fullTips1: '学校空间已满,无法创建学校课程评测',
         fullTips2: '个人空间已满,无法创建个人课程评测',
         createPrivCus: '新建个人课程',
@@ -7739,12 +7742,13 @@ const LANG_ZH_CN = {
         dataIsSave:'资料已储存',
         remind:'提醒',
         remindMessage1:'有未储存的设定,要继续切换活动项目吗? ',
-        remindMessage2:'有未储存的设定,要继续新增活动项目吗? ',
+        remindMessage2:'有未储存的设定,要继续新增活动项目吗? ',        
         deleteRemind1:'删除活动子项目',
         remindMessage3:'活动子项目数据删除后将无法找回,确认删除当前活动子项目吗?',
         deleteRemind2:'删除活动项目',
         remindMessage4:'活动项目已删除',
         remindMessage5:'分数登录完成',
+        remindMessage6:'有未储存的设定,确定要继续吗? ',
         scoreCalc:'成绩计算',        
         modifyContent:'可修改内容       ',
         ask:'询问',
@@ -7756,6 +7760,6 @@ const LANG_ZH_CN = {
         chooseAddWay:'请选择新增成绩表的方式',
         addNewTable:'新增"新"成绩表',
         InheritTable:'沿用成绩表',
-        addTableNotice:'注意:新增的成绩表成绩以当下系统有的成绩为准,成绩表成绩没有跟其他功能连动',          
+        addTableNotice:'注意:新增的成绩表将以当下系统中的现有成绩作为基础,新建的成绩表不会受到其他功能的影响或连动',          
     }
 }

+ 6 - 2
TEAMModelOS/ClientApp/public/lang/zh-TW.js

@@ -1151,6 +1151,9 @@ const LANG_ZH_TW = {
         cusTab10: '新建公告',
         cusTab11: '新建問卷',
         cusTab12: '新建投票',
+        cusTab13: '課堂課綱',
+        cusTab14: '排序課綱',
+        cusTab15: '新增課綱',
         fullTips1: '學校空間已滿,無法創建學校課程評量',
         fullTips2: '個人空間已滿,無法創建個人課程評量',
         createPrivCus: '新建個人課程',
@@ -7738,12 +7741,13 @@ const LANG_ZH_TW = {
         dataIsSave:'資料已儲存',
         remind:'提醒',
         remindMessage1:'有未儲存的設定,要繼續切換活動項目嗎?',
-        remindMessage2:'有未儲存的設定,要繼續新增活動項目嗎?',
+        remindMessage2:'有未儲存的設定,要繼續新增活動項目嗎?',        
         deleteRemind1:'刪除活動子項目',
         remindMessage3:'活動子項目數據刪除後將無法找回,確認刪除當前活動子項目嗎?',
         deleteRemind2:'刪除活動項目',
         remindMessage4:'活動項目已刪除',
         remindMessage5:'分數登錄完成',
+        remindMessage6:'有未儲存的設定,確定要繼續嗎?',
         scoreCalc:'成績計算',        
         modifyContent:'可修改內容       ',
         ask:'詢問',
@@ -7755,7 +7759,7 @@ const LANG_ZH_TW = {
         chooseAddWay:'請選擇新增成績表的方式',
         addNewTable:'新增"新"成績表',
         InheritTable:'沿用成績表',
-        addTableNotice:'注意:新增的成績表成績以當下系統有的成績為準,成績表成績沒有跟其他功能連動',
+        addTableNotice:'注意:新增的成績表將以當下系統中的現有成績作為基礎,新建的成績表不會受到其他功能的影響或連動',
     }
 
 

+ 4 - 2
TEAMModelOS/ClientApp/src/components/questionnaire/BaseJudge.vue

@@ -132,7 +132,7 @@
 					})
 					this.$nextTick(() => {
 						let editor = new E(that.$refs['singleOption' + newIndex][0])
-						this.$editorTools.initSimpleEditor(editor)
+						this.$editorTools.initSimpleEditor(editor)						
 						editor.config.onchange = (html) => {
 							let key = String.fromCharCode(64 + parseInt(newIndex + 1))
 							let codeArr = this.optionsContent.map(item => item.code)
@@ -241,7 +241,9 @@
 			this.$editorTools.initSimpleEditor(stemEditor)
 					stemEditor.create()
 			this.stemEditor = stemEditor
-			this.initEditors()
+			if(!this.editInfo){
+				this.initEditors()
+			}			
 
 			if (this.editInfo && this.editInfo.question) {
 				console.log('进入判断题Mounted编辑')

+ 3 - 1
TEAMModelOS/ClientApp/src/components/questionnaire/BaseMultiple.vue

@@ -273,7 +273,9 @@
 			this.$editorTools.initSimpleEditor(stemEditor)
 					stemEditor.create()
 			this.stemEditor = stemEditor
-			this.initEditors()
+			if(!this.editInfo){
+				this.initEditors()
+			}
 
 			if (this.editInfo && this.editInfo.question) {
 				console.log('进入多选题Mounted编辑')

+ 3 - 2
TEAMModelOS/ClientApp/src/components/questionnaire/BaseSingle.vue

@@ -239,8 +239,9 @@
 			this.$editorTools.initSimpleEditor(stemEditor)
 			stemEditor.create()
 			this.stemEditor = stemEditor
-			this.initEditors()
-
+			if(!this.editInfo){
+				this.initEditors()
+			}
 			if (this.editInfo && this.editInfo.question) {
 				console.log('进入单选题Mounted编辑')
 				this.editSingleInfo = JSON.parse(JSON.stringify(this.editInfo)) 

+ 1 - 0
TEAMModelOS/ClientApp/src/view/mycourse/MyCourse.less

@@ -150,6 +150,7 @@
     width: 100%;
     height: 45px;
     padding-left: 15px;
+    padding-right: 150px;
     // background: #f9f9f9;
     border-bottom: 1px solid #e2e2e2;
 }

+ 8 - 2
TEAMModelOS/ClientApp/src/view/mycourse/MyCourse.vue

@@ -113,6 +113,9 @@
 						<span @click="selectTab('score')" :class="tabName == 'score' ? 'course-classroom-label pane active':'course-classroom-label pane'">
 							{{$t('cusMgt.cusTab7')}}
 						</span>
+						<span @click="selectTab('classSyllabus')" :class="tabName == 'classSyllabus' ? 'course-classroom-label pane active':'course-classroom-label pane'">
+							{{$t('cusMgt.cusTab13')}}
+						</span>
 					</div>
 					<!-- 评测活动 -->
 					<Exam v-if="tabName == 'exam'" v-model="fIds" :gradeParams="gradeParams" :students="students" :classInfo="teaClassList[curClassIndex]" :courseInfo="courseInfo" :teaClassList="teaClassList"> </Exam>
@@ -124,6 +127,7 @@
 					<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>
+					<SyllabusIndex v-else-if="tabName == 'classSyllabus'" :gradeParams="gradeParams" :grouplistId="teaClassList[curClassIndex]" :listType="listType"></SyllabusIndex>
 				</div>
 			</Split>
 		</div>
@@ -343,7 +347,8 @@
 	import Student from "./student/Student.vue";
 	import Notice from "./notice/Notice.vue";
 	import Discussion from "./discussion/Discussion.vue";
-	import Score from "./score/Score.vue"
+	import Score from "./score/Score.vue";
+	import SyllabusIndex from "./syllabus/SyllabusIndex.vue";
 	export default {
 		components: {
 			Exam,
@@ -354,7 +359,8 @@
 			Student,
 			Notice,
 			Discussion,
-			Score
+			Score,
+			SyllabusIndex
 		},
 		data() {
 			// 验证只能是字母和数字

+ 5 - 0
TEAMModelOS/ClientApp/src/view/mycourse/exam/Exam.less

@@ -7,6 +7,11 @@
 @borderColor: var(--border-color);
 @second-fontSize: 16px;
 
+.stu-action-wrap{
+    position: absolute;
+    right: 20px;
+    top: 7px;
+}
 .course-exam-container{
     width: 100%;
     height: ~"calc(100% - 46px)";

+ 24 - 2
TEAMModelOS/ClientApp/src/view/mycourse/exam/Exam.vue

@@ -1,6 +1,28 @@
 <template>
     <div class="course-exam-container" id="course-exam-container">
-        <div class="exam-action-wrap">
+        <div class="stu-action-wrap common-save-btn">
+            <Dropdown style="margin-top:5px;margin-right:8px">
+                <a href="javascript:void(0)">
+                    {{$t('cusMgt.moreFn')}}
+                    <Icon type="ios-arrow-down"></Icon>
+                </a>
+                <DropdownMenu slot="list">
+                    <DropdownItem @click.native="isShowGrade = !isShowGrade">
+                        <span class="action-item" >
+                            <Icon :type="isShowGrade ? 'md-list' : 'md-grid'" />
+                            {{isShowGrade ? $t('cusMgt.cusTab6') : $t('cusMgt.cusTab7')}}
+                        </span>
+                    </DropdownItem>
+                    <DropdownItem @click.native="toCreate">
+                    <span class="action-item" >
+                        <Icon type="md-add" />
+                        {{$t('cusMgt.cusTab8')}}
+                    </span>
+                    </DropdownItem>
+                </DropdownMenu>
+            </Dropdown>
+        </div>
+        <!-- <div class="exam-action-wrap">
             <span class="action-item" @click="isShowGrade = !isShowGrade">
                 <Icon :type="isShowGrade ? 'md-list' : 'md-grid'" />
                 {{isShowGrade ? $t('cusMgt.cusTab6') : $t('cusMgt.cusTab7')}}
@@ -9,7 +31,7 @@
                 <Icon type="md-add" />
                 {{$t('cusMgt.cusTab8')}}
             </span>
-        </div>
+        </div> -->
         <Loading v-show="isLoading"></Loading>
         <Grade v-show="isShowGrade" :paramsInfo="gradeParams" :student="students" class="exam-grade-view"></Grade>
         <vuescroll>

+ 6 - 0
TEAMModelOS/ClientApp/src/view/mycourse/record/Record.less

@@ -1,3 +1,9 @@
+
+.stu-action-wrap{
+    position: absolute;
+    right: 20px;
+    top: 7px;
+}
 .record-container{
     width: 100%;
     height: ~"calc(100% - 50px)";

+ 22 - 19
TEAMModelOS/ClientApp/src/view/mycourse/record/Record.vue

@@ -14,7 +14,7 @@
         {{$t('cusMgt.autoShare')}}
       </span>
       <i-switch :loading="sLoading" v-model="isAuto" size="small" @on-change="setAutoPublish" />
-    </div>
+    </div> -->
     <vuescroll>
       <Alert v-show="rcdParams.scope == 'private'" show-icon type="warning" closable>
         {{$t('cusMgt.recordTips')}}
@@ -25,12 +25,21 @@
           <p class="record-name" style="padding-left:10px">
             {{item.name}}
             <span class="item-icon-wrap">
-              <Icon type="md-create" class="common-item-icon ed-name" @click.stop="editRecordName(index)" :title="$t('cusMgt.edRdName')" />
-              <Icon type="md-trash" class="common-item-icon delete-item" @click.stop="delRecord(item)" :title="$t('cusMgt.delRcd')" />
-              <Icon v-show="item.isShare" type="ios-share-alt" :size="20" :class="['common-item-icon', 'share-student', 'share-active']" @click.stop="toggleShare(item)" :title="$t('cusMgt.unShare')" />
-              <Icon v-show="!item.isShare" type="ios-share-alt-outline" :size="20" :class="['common-item-icon','share-student',]" @click.stop="toggleShare(item)" :title="$t('cusMgt.shareToStu')" />
-              <Icon v-show="isFavorite(item.id)" type="md-heart" style="right:2px" :class="['common-item-icon', 'heart-active']" @click.stop="toggleFavorite(item)" :title="$t('cusMgt.unfvt')" />
-              <Icon v-show="!isFavorite(item.id)" type="md-heart-outline" style="right:2px" :class="['common-item-icon',]" @click.stop="toggleFavorite(item)" :title="$t('cusMgt.fvt')" />
+              <Icon type="md-create" class="common-item-icon ed-name" @click.stop="editRecordName(index)"
+                :title="$t('cusMgt.edRdName')" />
+              <Icon type="md-trash" class="common-item-icon delete-item" @click.stop="delRecord(item)"
+                :title="$t('cusMgt.delRcd')" />
+              <Icon v-show="item.isShare" type="ios-share-alt" :size="20"
+                :class="['common-item-icon', 'share-student', 'share-active']" @click.stop="toggleShare(item)"
+                :title="$t('cusMgt.unShare')" />
+              <Icon v-show="!item.isShare" type="ios-share-alt-outline" :size="20"
+                :class="['common-item-icon','share-student',]" @click.stop="toggleShare(item)"
+                :title="$t('cusMgt.shareToStu')" />
+              <Icon v-show="isFavorite(item.id)" type="md-heart" style="right:2px"
+                :class="['common-item-icon', 'heart-active']" @click.stop="toggleFavorite(item)"
+                :title="$t('cusMgt.unfvt')" />
+              <Icon v-show="!isFavorite(item.id)" type="md-heart-outline" style="right:2px"
+                :class="['common-item-icon',]" @click.stop="toggleFavorite(item)" :title="$t('cusMgt.fvt')" />
             </span>
           </p>
           <div style="padding-left:10px;margin-top:10px">
@@ -130,11 +139,14 @@
           </div>
           <p style="margin:10px 0;color:red">* {{ $t('cusMgt.rcd.cover.tip') }}</p>
           <div v-show="previewCover">
-            <p style="margin-bottom:10px">{{ $t('cusMgt.rcd.cover.preview') }} <span style="text-decoration:underline;color:gray;cursor: pointer;" @click="previewCover = ''">{{ $t('cusMgt.rcd.cover.undo') }}</span></p>
+            <p style="margin-bottom:10px">{{ $t('cusMgt.rcd.cover.preview') }} <span
+                style="text-decoration:underline;color:gray;cursor: pointer;"
+                @click="previewCover = ''">{{ $t('cusMgt.rcd.cover.undo') }}</span></p>
             <img :src="previewCover" alt="" style="width: 430px;height:242px">
           </div>
         </div>
-        <Button :loading="btnLoading" @click="confirmEditRd" long type="primary" class="confirm-btn">{{ $t('syllabus.confirm') }}</Button>
+        <Button :loading="btnLoading" @click="confirmEditRd" long type="primary"
+          class="confirm-btn">{{ $t('syllabus.confirm') }}</Button>
       </div>
     </Modal>
   </div>
@@ -367,7 +379,7 @@ export default {
       }
       this.$api.courseMgmt.FavoriteUpsert(params).then(
         res => {
-          if(res.status) {
+          if (res.status) {
             this.$Message.warning(this.$t('cusMgt.spaceFull'))
           } else {
             this.$Message.success(this.$t('cusMgt.fvtOk'))
@@ -540,19 +552,10 @@ export default {
                 promiseArr.push(new Promise(async (r, j) => {
                     let imageCover = ''
                     try {
-                      // 存在其他老师发布的课堂记录
-                      if(item.tmdid != this.$store.state.userInfo.TEAMModelId && this.rcdParams.scope != 'school') {
-                        let blobInfos = await this.$evTools.getBlobPrivateSasObj(item.tmdid)
-                        let url = `${blobInfos.url}/${blobInfos.name}/records/${item.id}/IES/TimeLine.json${blobInfos.sas}`
-                        let res = JSON.parse(await this.$tools.getFile(url))
-                        let pgids = res.PgIdList || []
-                        imageCover = `${blobInfos.url}/${blobInfos.name}/records/${item.id}/Memo/${pgids[0]}.jpg${blobInfos.sas}`
-                      } else {
                         let url = `${sasInfo.url}/${sasInfo.name}/records/${item.id}/IES/TimeLine.json${sasInfo.sas}`
                         let res = JSON.parse(await this.$tools.getFile(url))
                         let pgids = res.PgIdList || []
                         imageCover = `${sasInfo.url}/${sasInfo.name}/records/${item.id}/Memo/${pgids[0]}.jpg${sasInfo.sas}`
-                      }
                     } catch (e) {
                     }
                     item.poster = imageCover

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

@@ -247,9 +247,9 @@ export default {
     },
     ProjectGoback(vule) {
       if (vule == "save") {
-        if (this.totalNum > 100) {
-          this.$Message.warning(this.$t("scoreCalc.warringTotalRate"));
-        } else {
+        // if (this.totalNum > 100) {
+        //   this.$Message.warning(this.$t("scoreCalc.warringTotalRate"));
+        // } else {
           //#region 更新項目及子項目資料
           let items = [];
           let sort = 1;
@@ -289,7 +289,7 @@ export default {
           );
           this.isSave = true;
           //#endregion
-        }
+        // }
       } else if (vule == "back") {
         //直接返回
         this.$emit("tableInfo", this.tableInfo);

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

@@ -37,10 +37,6 @@
       margin: 1px 5px;
       border: 1px solid #dcdee2;
     }
-    .txt-box {
-      text-align: left;
-      width: 400px;
-    }
     .txt-box2 {
       text-align: left;
       width: 350px;

+ 7 - 1
TEAMModelOS/ClientApp/src/view/mycourse/score/DialogBoxAddTable.vue

@@ -15,7 +15,13 @@
           </RadioGroup>
           <div class="flex-item">
             <Select v-model="chooseID" style="width:200px">
-              <Option v-for="item in lists" :value="item.id" :key="item.id">{{ item.name+"_"+$jsFn.dateFormat(item.createTime) }}</Option>
+              <Option v-for="item in lists" :value="item.id" :key="item.id">
+                {{ item.name}} 
+                <span style="color:#808695;">
+                  <Icon type="md-time" style="margin:0px;" v-if="$jsFn.dateFormat(item.createTime) !== ''"/>
+                  {{$jsFn.dateFormat(item.createTime) }}
+                </span>                
+              </Option>
             </Select>
           </div>          
         </div>        

+ 65 - 58
TEAMModelOS/ClientApp/src/view/mycourse/score/DialogBoxStudentScore.vue

@@ -12,8 +12,8 @@
           <div>
             <DataTable :value="classData" showGridlines v-if="class_type == 'lessonrecord'" :scrollable="true"
               scrollHeight="400px" editMode="cell" class="editable-cells-table" responsiveLayout="scroll"
-              style="min-height: 400px;" :sortField="sortField" :sortOrder="sortOrder" removableSort ref="myLessonTable">
-              <Column field="irs" :header="$t('scoreCalc.IRSnumber')" 
+              style="min-height: 400px;" :sortField="sortField" :sortOrder="sortOrder" removableSort @sort="userSort($event)">
+              <Column field="irs" :header="$t('scoreCalc.IRSnumber')" sortable
                 :styles="{ 'min-width': '80px', 'text-align': 'center', 'display': 'block' }"><!--sortable -->
                 <template #body="rowData">
                   <div>
@@ -21,7 +21,7 @@
                   </div>
                 </template>
               </Column>
-              <Column field="name" :header="$t('scoreCalc.name')"
+              <Column field="name" :header="$t('scoreCalc.name')" sortable
                 :styles="{ 'min-width': '120px', 'text-align': 'center', 'display': 'block' }">
                 <template #body="rowData">
                   <div>
@@ -29,7 +29,7 @@
                   </div>
                 </template>
               </Column>
-              <Column field="id" :header="$t('scoreCalc.id')"
+              <Column field="id" :header="$t('scoreCalc.id')" sortable
                 :styles="{ 'min-width': '120px', 'text-align': 'center', 'display': 'block' }">
                 <template #body="rowData">
                   <div>
@@ -44,7 +44,7 @@
                   <div>
                     <!-- <input type="text" :value="attendShow(rowData.data[rowData.column.field],rowData)" @input="falseIsSave($event, rowData.index,'attend')"
                       maxlength="1" class="column-input" style=" width:70px"/> -->
-                    <Select v-model="rowData.data.attendance" style="width:100px; color: #2d8cf0;"  @on-change="attSelectFun">
+                    <Select v-model="rowData.data.attendance" style="width:100px; color: #2d8cf0;"  @on-change="attSelectFun"  tabindex="-1">
                         <Option v-for="item in cityList" :value="item.value" :key="item.value" >{{ item.label }}</Option>
                     </Select>
                   </div>
@@ -56,7 +56,7 @@
                 <template #body="rowData">
                   <div>
                     <input type="text" v-model.number="rowData.data[rowData.column.field]" @input="falseIsSave($event, rowData.data.id,'score')"
-                      maxlength="3" class="column-input" />
+                      maxlength="3" class="column-input" @keydown.tab.stop/>
                   </div>
                 </template>
               </Column>
@@ -66,15 +66,15 @@
                 <template #body="rowData">
                   <div>
                     <input type="text" v-model.number="rowData.data[rowData.column.field]" @input="falseIsSave($event, rowData.data.id,'interact')"
-                      maxlength="3" class="column-input" />
+                      maxlength="3" class="column-input"  @keydown.tab.stop/>
                   </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"
-              :sortField="sortField" :sortOrder="sortOrder" removableSort ref="myActivityTable" @sort="userSort($event)">
-              <Column field="irs" :header="$t('scoreCalc.IRSnumber')"  
+              :sortField="sortField" :sortOrder="sortOrder" removableSort @sort="userSort($event)">
+              <Column field="irs" :header="$t('scoreCalc.IRSnumber')"  sortable
                 :styles="{ 'min-width': '90px', 'text-align': 'center', 'display': 'block' }"><!--sortable-->
                 <template #body="rowData">
                   <div>
@@ -82,7 +82,7 @@
                   </div>
                 </template>
               </Column>
-              <Column field="name" :header="$t('scoreCalc.name')" 
+              <Column field="name" :header="$t('scoreCalc.name')" sortable
                 :styles="{ 'min-width': '120px', 'text-align': 'center', 'display': 'block' }">
                 <template #body="rowData">
                   <div style="text-align: center;">
@@ -90,7 +90,7 @@
                   </div>
                 </template>
               </Column>
-              <Column field="id" :header="$t('scoreCalc.id')" 
+              <Column field="id" :header="$t('scoreCalc.id')" sortable
                 :styles="{ 'min-width': '120px', 'text-align': 'center', 'display': 'block' }">
                 <template #body="rowData">
                   <div>
@@ -102,14 +102,14 @@
                 :styles="{ 'min-width': '120px', 'color': 'rgb(45, 140, 240)', 'text-align': 'center', 'display': 'block' }"
                 :headerStyle="{ 'color': '#515a6e' }">
                 <template #body="rowData">
-                  <div style="width: 100%">
+                  <div style="width: 100%" >
                     <!-- <input type="text" v-model.number="rowData.data[rowData.column.field]" @input="falseIsSave()"
                       maxlength="3" class="column-input" v-show="isNotrated(rowData.data[rowData.column.field])" />
                       <input type="text" style="width: 50px;" value="未評分" @input="falseIsSave()"
                       maxlength="6" class="column-input" v-show="!isNotrated(rowData.data[rowData.column.field])"/> -->
 
                     <input type="text" style="width: 50px;" :value="checkNotrated(rowData.data[rowData.column.field])" @input="falseIsSave($event, rowData.data.id,'score')"
-                      maxlength="3" class="column-input" />
+                      maxlength="3" class="column-input" @keydown.tab.stop/>
                   </div>
                 </template>
               </Column>
@@ -124,7 +124,7 @@
           <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>
+              @input="isTextareaInput = true, TextareaInput_system" @keydown.tab.prevent="insertTab"></textarea>
 
             <textarea v-if="class_type == 'activity'" v-model="textareaValue_system" type="text" rows="4" cols="30"
               datatype="*" class="enter-box txt-box" :placeholder="checkNotratedTextarea(custom_score_data2)" required
@@ -204,11 +204,15 @@ export default {
     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 data = this.textareaValue_system.split('\n').map((line) => line.split('\t'));
+        const data = this.textareaValue_system.split('\n').map((line) => {
+          const values = line.split('\t');
+          // 使用 map 方法遍历每个值,如果为 undefined 或 null,则替换为 0
+          return values.map((value) => (value === undefined || value === null || value.trim() === '' ? '0' : value));
+        });
         const lastLine = data[data.length - 1];
         const isLastLineEmpty = lastLine.length === 1 && lastLine[0].trim() === '';
-
-        let classDataTemp =  this.sortTable();//回傳根據使用者排列而排列的array資料        
+        let classDataTemp =  this.sortTable();//回傳根據使用者排列而排列的array資料   
         for (let i = 0; i < data.length - 1; i++) { // 迭代到倒數第二行
           if (classDataTemp[i]) {
             const [attendance, score, interactiveScore] = data[i];
@@ -228,14 +232,14 @@ export default {
           let irsToMatch = this.classData[k].irs;
           const matchingItem = classDataTemp.find(item => item.irs === irsToMatch);
           if (matchingItem) {// 如果找到匹配,將 score 值給 classData 中對應屬性
-          this.classData[k].attendance = matchingItem.attendance;
-              this.classData[k].score = matchingItem.score;
-              this.classData[k].interactiveScore = matchingItem.interactiveScore;
+            this.classData[k].attendance = matchingItem.attendance;
+            this.classData[k].score = matchingItem.score;
+            this.classData[k].interactiveScore = matchingItem.interactiveScore;
           }
         }
         this.isTextareaInput = false;
       }
-    },    
+    }, 
     TextareaInput_custom() {
       this.isSave = false;
       if (this.isTextareaInput && this.textareaValue_system.trim() !== '') {
@@ -277,52 +281,55 @@ export default {
   },
   mounted() {    
   },
-  methods: {          
+  methods: {    
+    insertTab(event) {// 捕獲 Tab 鍵事件並插入 \t 字符
+      if (event.key === "Tab") {
+        event.preventDefault(); // 阻止默認的 Tab 鍵行為
+        const textarea = event.target;
+        const start = textarea.selectionStart;
+        const end = textarea.selectionEnd;
+
+        // 在字串位置插入 \t
+        this.textareaValue_system =
+          this.textareaValue_system.substring(0, start) +
+          "\t" +
+          this.textareaValue_system.substring(end);
+
+        // 調整鼠標位置
+        textarea.selectionStart = textarea.selectionEnd = start + 1;
+      }
+    }, 
     userSort(event) {      
-      //嘗試去抓排序,但還不知道event哪個抓得到升冪降冪,以及sortField的值
       this.sortField = event.sortField;
       this.sortOrder = event.sortOrder;
-      console.log("this.sortField="+this.sortField);
-      console.log("this.sortOrder="+this.sortOrder);
-      console.log(`用户选择了按照列 ${event.sortField} 进行排序,方向是 ${event.sortOrder === 1 ? '升序' : '降序'}`);
+      // console.log("this.sortField="+this.sortField);
+      // console.log("this.sortOrder="+this.sortOrder);
+      // console.log(`用戶選擇了按照列 ${event.sortField} 進行排序,方向是 ${event.sortOrder === 1 ? '升序' : '降序'}`);      
       this.sortTable();//進行畫面渲染與排序。
     },
-    sortTable(){//回傳根據使用者排列而排列的array資料
-      let classDataTemp =  [...this.classData];
-      //以下僅是根據sortField的值進行升冪排序,目前寫死sortField=irs
-      //因為尚找不到使用者根據什麼排序。
-      classDataTemp.sort((a, b) => {
-       return this.sortOrder === 1 ? a[this.sortField] - b[this.sortField] : b[this.sortField] - a[this.sortField];
+    sortTable(){//回傳根據使用者排列而排列的array資料      
+      this.classData.forEach(element => {//判別classData的attendance是否是undifind如果是就轉成0。
+        let value =  element.attendance;
+        if(value === undefined || value === null ) element.attendance = 0;
       });
-      /*
-      if(this.sortOrder===1){//判斷升序
-        classDataTemp.sort((a, b) => {
-          if (typeof a[this.sortField] === 'number') {
-            // 如果字段是数字类型,按数字大小排序
-            console.log("typeof = number");
-            console.log("a[this.sortField] - b[this.sortField] ="+a[this.sortField] - b[this.sortField]);
-            return a[this.sortField] - b[this.sortField];
-          } else {
-            // 否则,按照字符串排序
-            console.log("typeof = else");
-            console.log("a[this.sortField].localeCompare(b[this.sortField]) ="+ a[this.sortField].localeCompare(b[this.sortField]));
-            return a[this.sortField].localeCompare(b[this.sortField]);
-          }
-        });
-      }else if(this.sortOrder===-1){//判斷降序
-        classDataTemp.sort((a, b) => {
-          if (typeof a[this.sortField] === 'string') {
-            return b[this.sortField].localeCompare(a[this.sortField]);
-          } else {
-            return b[this.sortField] - a[this.sortField];
-          }
-        });
-      }*/
-      classDataTemp.forEach(element => {
-        //console.log("id="+element.id+"/score="+element.score);
+      let classDataTemp =  [...this.classData];
+      classDataTemp.sort((a, b) => {                
+        if(this.sortOrder===null) return;//如果不排序則不繼續往下執行。        
+        // 检查是否是纯数字        
+        const isNumeric = /^\d+$/.test(a[this.sortField]) && /^\d+$/.test(b[this.sortField]);
+
+        if (isNumeric) {// 如果两个都是数字,按数字大小排序
+          return this.sortOrder === 1 ? a[this.sortField] - b[this.sortField] : b[this.sortField] - a[this.sortField];
+        } else {
+          return this.sortOrder === 1 ? a[this.sortField].localeCompare(b[this.sortField]) : b[this.sortField].localeCompare(a[this.sortField]);
+        }
       });
+      
       this.custom_score_data = classDataTemp.map(item => `${item.attendance}\t${item.score}\t${item.interactiveScore}`).join('\n');
       this.custom_score_data2 = classDataTemp.map(item => `${item.score}`).join('\n');
+      //不採用下面的,因為在使用者輸入雙位數時,因為及時連動,反而會出錯
+      // if(this.class_type==='lessonrecord') this.textareaValue_system = this.custom_score_data;
+      // else if(this.class_type==='activity') this.textareaValue_system = this.custom_score_data2;
       return  classDataTemp;
     }, 
     attSelectFun(){

+ 86 - 70
TEAMModelOS/ClientApp/src/view/mycourse/score/ScoreBody.vue

@@ -4,7 +4,8 @@
     <div class="body-content">      
       <div class="foot">
         <Button @click="delNewScoreFile">{{ $t('scoreCalc.delete') }}</Button>
-        <div v-if="lists.length !== 0" class="creat-txt">{{ $t('scoreCalc.creatDate') }}:{{$jsFn.secondTimeFormat(this.tableInfo.creatTime)}}</div>
+        <div v-if="lists.length !== 0 && this.tableInfo.creatTime===0" class="creat-txt">{{ $t('scoreCalc.creatDate') }}:--</div>
+        <div v-if="lists.length !== 0 && this.tableInfo.creatTime!==0" class="creat-txt">{{ $t('scoreCalc.creatDate') }}:{{$jsFn.secondTimeFormat(this.tableInfo.creatTime)}}</div>
         <div class="button-group">
           <div v-if="isSave && lists.length !== 0" class="save-info-ok">
             <div>
@@ -34,7 +35,10 @@
           v-bind:key="index">
           <Card :class="['ivu-card', { active: list.click }]">
             <p><b>{{ list.name }}</b></p>
-            <p>{{$jsFn.dateFormat(list.createTime)}}</p>
+            <p style="color:#515a6e;">
+              <Icon type="md-time" style="margin:0px;" v-if="$jsFn.dateFormat(list.createTime) !== ''"/>
+              {{$jsFn.dateFormat(list.createTime)}}
+            </p>
           </Card>
         </div>
       </div>      
@@ -99,19 +103,18 @@
                       </div>
                     </template>
                   </Column>
-                  <Column field="homework" header="" :styles="{ ...commonStyle, width: '90px' }">
+                  <Column field="evaluate" header="" :styles="{ ...commonStyle, width: '90px' }">
                     <template #header>
                       <div class="div-center">
                         <div>
-                          <div @click="changePageTo(Proportion.HomeWorkActRate.id)"
+                          <div @click="changePageTo(Proportion.ExamRate.id)"
                             style=" align-items: center; textDecoration: underline ; cursor: pointer;">
-                            {{ $t('scoreCalc.th_homework') }}
+                            {{ $t('scoreCalc.th_exam') }}
                           </div>
                           <div style="display: flex; align-items: center;">
-                            ( <input type="text" v-model="Proportion.HomeWorkActRate.score"
-                              @input="onRateChange('homework')"
+                            ( <input type="text" v-model="Proportion.ExamRate.score" @input="onRateChange('evaluate')"
                               style="width: 20px; height: 20px; box-sizing: border-box;"
-                              :style="{ color: Proportion.HomeWorkActRate.color, margin: '0px 5px', border: 'none', background: 'none' }"
+                              :style="{ color: Proportion.ExamRate.color, margin: '0px 5px', border: 'none', background: 'none' }"
                               maxlength="2">
                             %)
                           </div>
@@ -122,27 +125,28 @@
                       <div class="div-center" style="  flex-direction: column;">
                         <div class="div-centerY">
                           <div class="text-size20">
-                            {{ rowData.data.homework.score+rowData.data.homework.add }}
+                            {{ rowData.data.evaluate.score+rowData.data.evaluate.add }}
                           </div>
                         </div>
                         <div>
-                          ({{ calculateClassPercent(rowData.data, 'homework') }})
+                          ({{ calculateClassPercent(rowData.data, 'evaluate') }})
                         </div>
                       </div>
                     </template>
                   </Column>
-                  <Column field="evaluate" header="" :styles="{ ...commonStyle, width: '90px' }">
+                  <Column field="homework" header="" :styles="{ ...commonStyle, width: '90px' }">
                     <template #header>
                       <div class="div-center">
                         <div>
-                          <div @click="changePageTo(Proportion.ExamRate.id)"
+                          <div @click="changePageTo(Proportion.HomeWorkActRate.id)"
                             style=" align-items: center; textDecoration: underline ; cursor: pointer;">
-                            {{ $t('scoreCalc.th_exam') }}
+                            {{ $t('scoreCalc.th_homework') }}
                           </div>
                           <div style="display: flex; align-items: center;">
-                            ( <input type="text" v-model="Proportion.ExamRate.score" @input="onRateChange('evaluate')"
+                            ( <input type="text" v-model="Proportion.HomeWorkActRate.score"
+                              @input="onRateChange('homework')"
                               style="width: 20px; height: 20px; box-sizing: border-box;"
-                              :style="{ color: Proportion.ExamRate.color, margin: '0px 5px', border: 'none', background: 'none' }"
+                              :style="{ color: Proportion.HomeWorkActRate.color, margin: '0px 5px', border: 'none', background: 'none' }"
                               maxlength="2">
                             %)
                           </div>
@@ -153,15 +157,15 @@
                       <div class="div-center" style="  flex-direction: column;">
                         <div class="div-centerY">
                           <div class="text-size20">
-                            {{ rowData.data.evaluate.score+rowData.data.evaluate.add }}
+                            {{ rowData.data.homework.score+rowData.data.homework.add }}
                           </div>
                         </div>
                         <div>
-                          ({{ calculateClassPercent(rowData.data, 'evaluate') }})
+                          ({{ calculateClassPercent(rowData.data, 'homework') }})
                         </div>
                       </div>
                     </template>
-                  </Column>
+                  </Column>                  
                   <Column v-for="count in table_count" :key="count" field="custom" header=""
                     :styles="{ ...commonStyle, width: '100px' }">
                     <template #header>
@@ -277,7 +281,7 @@
                                 style=" align-items: center; textDecoration: underline ; cursor: pointer;">
                                 {{ $t('scoreCalc.class') }}
                               </div>
-                              <div style="display: flex; align-items: center;">
+                              <div>
                                 ( <input type="text" v-model="Proportion.ScoreCalcActRate.score"
                                   @input="onRateChange('class')"
                                   style="width: 20px; height: 20px; box-sizing: border-box;"
@@ -289,19 +293,19 @@
                           </div>
                         </template>
                       </Column>
-                      <Column header="" :rowspan="2" :styles="{ ...sticky , ...tableBorder, top:'0px',}"><!--作業-->
+                      <Column header="" :rowspan="2" :styles="{ ...sticky , ...tableBorder, top:'0px',}"><!--評量-->
                         <template #header>
                           <div class="div-center" style="width: 80px;">
                             <div>
-                              <div @click="changePageTo(Proportion.HomeWorkActRate.id)"
+                              <div @click="changePageTo(Proportion.ExamRate.id)"
                                 style=" align-items: center; textDecoration: underline ; cursor: pointer;">
-                                {{ $t('scoreCalc.th_homework') }}
+                                {{ $t('scoreCalc.th_exam') }}
                               </div>
-                              <div style="display: flex; align-items: center;">
-                                ( <input type="text" v-model="Proportion.HomeWorkActRate.score"
-                                  @input="onRateChange('homework')"
+                              <div>
+                                ( <input type="text" v-model="Proportion.ExamRate.score"
+                                  @input="onRateChange('evaluate')"
                                   style="width: 20px; height: 20px; box-sizing: border-box;"
-                                  :style="{ color: Proportion.HomeWorkActRate.color, margin: '0px 5px', border: 'none', background: 'none' }"
+                                  :style="{ color: Proportion.ExamRate.color, margin: '0px 5px', border: 'none', background: 'none' }"
                                   maxlength="2">
                                 %)
                               </div>
@@ -309,19 +313,19 @@
                           </div>
                         </template>
                       </Column>
-                      <Column header="" :rowspan="2" :styles="{ ...sticky , ...tableBorder, top:'0px',}"><!--評量-->
+                      <Column header="" :rowspan="2" :styles="{ ...sticky , ...tableBorder, top:'0px',}"><!--作業-->
                         <template #header>
                           <div class="div-center" style="width: 80px;">
                             <div>
-                              <div @click="changePageTo(Proportion.ExamRate.id)"
+                              <div @click="changePageTo(Proportion.HomeWorkActRate.id)"
                                 style=" align-items: center; textDecoration: underline ; cursor: pointer;">
-                                {{ $t('scoreCalc.th_exam') }}
+                                {{ $t('scoreCalc.th_homework') }}
                               </div>
-                              <div style="display: flex; align-items: center;">
-                                ( <input type="text" v-model="Proportion.ExamRate.score"
-                                  @input="onRateChange('evaluate')"
+                              <div>
+                                ( <input type="text" v-model="Proportion.HomeWorkActRate.score"
+                                  @input="onRateChange('homework')"
                                   style="width: 20px; height: 20px; box-sizing: border-box;"
-                                  :style="{ color: Proportion.ExamRate.color, margin: '0px 5px', border: 'none', background: 'none' }"
+                                  :style="{ color: Proportion.HomeWorkActRate.color, margin: '0px 5px', border: 'none', background: 'none' }"
                                   maxlength="2">
                                 %)
                               </div>
@@ -338,7 +342,7 @@
                                 style=" align-items: center; textDecoration: underline ; cursor: pointer;">
                                 {{ Proportion['custom' + (count)].name }}
                               </div>
-                              <div style="display: flex; align-items: center;">
+                              <div>
                                 ( <input type="text" v-model="Proportion['custom' + (count)].score"
                                   @input="onRateChange('custom' + (count))"
                                   style="width: 20px; height: 20px; box-sizing: border-box;"
@@ -360,8 +364,7 @@
                       </Column>
                       <Column header="" :rowspan="2" :styles="tableNoneBackground" />
                       <Column header="" :rowspan="2" field="total" sortable 
-                      :styles="{ ...sticky , top:'0px',}"              
-                      ><!--總評分-->
+                        :styles="{ ...sticky , top:'0px',}"><!--總評分-->
                         <template #header>
                           <div class="div-center" style="width: 80px;">
                             {{ $t('scoreCalc.totalScore') }}
@@ -386,7 +389,7 @@
                                 style=" align-items: center; textDecoration: underline ; cursor: pointer;">
                                 {{ $t('scoreCalc.attend') }}
                               </div>
-                              <div style="display: flex; align-items: center;">
+                              <div>
                                 ( <input type="text" v-model="Proportion.ScoreCalcAct.attendRate.score"
                                   @input="onRateChange('attend')"
                                   style="width: 20px; height: 20px; box-sizing: border-box;"
@@ -406,7 +409,7 @@
                                 style=" align-items: center; textDecoration: underline ; cursor: pointer;">
                                 {{ $t('scoreCalc.point') }}
                               </div>
-                              <div style="display: flex; align-items: center;">
+                              <div>
                                 ( <input type="text" v-model="Proportion.ScoreCalcAct.pointRate.score"
                                   @input="onRateChange('point')"
                                   style="width: 20px; height: 20px; box-sizing: border-box;"
@@ -427,7 +430,7 @@
                                 style=" align-items: center; textDecoration: underline ; cursor: pointer;">
                                 {{ $t('scoreCalc.interactiveScore') }}
                               </div>
-                              <div style="display: flex; align-items: center;">
+                              <div>
                                 ( <input type="text" v-model="Proportion.ScoreCalcAct.interactRate.score"
                                   @input="onRateChange('interact')"
                                   style="width: 20px; height: 20px; box-sizing: border-box;"
@@ -535,36 +538,36 @@
                       </div>
                     </template>
                   </Column>
-                  <Column field="homework" header="作業" :styles="{ ...tableBorder }">
+                  <Column field="evaluate" header="評量" :styles="{ ...tableBorder }">
                     <template #body="rowData">
                       <div class="div-center" style="  flex-direction: column;">
-                        <div class="div-centerY" >
-                          <div  class="text-size20">
-                            {{ rowData.data.homework.score }}
+                        <div class="div-centerY">
+                          <div class="text-size20">
+                            {{ rowData.data.evaluate.score }}
                           </div>
-                          <input type="text" :value="inputNumShow(rowData.data.homework.add)"
-                            :style="inputColorShow(rowData.data.homework.add)" maxlength="2"
-                            @change="addOnChange($event, rowData.data.id, 'homework')" />                          
+                          <input type="text" :value="inputNumShow(rowData.data.evaluate.add)"
+                            :style="inputColorShow(rowData.data.evaluate.add)" maxlength="2"
+                            @change="addOnChange($event, rowData.data.id, 'evaluate')" />
                         </div>
                         <div>
-                          ({{ calculateClassPercent(rowData.data, 'homework') }})
+                          ({{ calculateClassPercent(rowData.data, 'evaluate') }})
                         </div>
                       </div>
                     </template>
                   </Column>
-                  <Column field="evaluate" header="評量" :styles="{ ...tableBorder }">
+                  <Column field="homework" header="作業" :styles="{ ...tableBorder }">
                     <template #body="rowData">
                       <div class="div-center" style="  flex-direction: column;">
-                        <div class="div-centerY">
-                          <div class="text-size20">
-                            {{ rowData.data.evaluate.score }}
+                        <div class="div-centerY" >
+                          <div  class="text-size20">
+                            {{ rowData.data.homework.score }}
                           </div>
-                          <input type="text" :value="inputNumShow(rowData.data.evaluate.add)"
-                            :style="inputColorShow(rowData.data.evaluate.add)" maxlength="2"
-                            @change="addOnChange($event, rowData.data.id, 'evaluate')" />
+                          <input type="text" :value="inputNumShow(rowData.data.homework.add)"
+                            :style="inputColorShow(rowData.data.homework.add)" maxlength="2"
+                            @change="addOnChange($event, rowData.data.id, 'homework')" />                          
                         </div>
                         <div>
-                          ({{ calculateClassPercent(rowData.data, 'evaluate') }})
+                          ({{ calculateClassPercent(rowData.data, 'homework') }})
                         </div>
                       </div>
                     </template>
@@ -612,7 +615,8 @@
                       <div class="div-center" style="  flex-direction: column;">
                         <div class="div-centerY">
                           <div class="text-size20">
-                            {{ calculatePR(rowData.index,rowData.data) }}
+                            {{ calculatePR(rowData.data) }}
+                            <!-- {{ Number(rowData.data.PR) }} -->
                           </div>
                         </div>
                       </div>
@@ -809,12 +813,12 @@ export default {
         //缺席absent 病假SL 事假PL 公假OL
         { title: this.$t('scoreCalc.absence2'), key: 'absent' }, { title: this.$t('scoreCalc.sickLeave2'), key: 'SL' }, { title: this.$t('scoreCalc.personalLeave2'), key: 'PL' }, { title: this.$t('scoreCalc.publicLeave2'), key: 'OL' },
         { title: this.$t('scoreCalc.attend'), key: 'attend' }, { title: this.$t('scoreCalc.point'), key: 'point' }, { title: this.$t('scoreCalc.interactiveScore'), key: 'interact' },
-        { title: this.$t('scoreCalc.class'), key: 'class' }, { title: this.$t('scoreCalc.th_homework'), key: 'homework' }, { title: this.$t('scoreCalc.th_exam'), key: 'evaluate' },
+        { title: this.$t('scoreCalc.class'), key: 'class' },  { title: this.$t('scoreCalc.th_exam'), key: 'evaluate' }, { title: this.$t('scoreCalc.th_homework'), key: 'homework' },
         { title: this.$t('scoreCalc.totalScore'), key: 'total' }, { title: this.$t('scoreCalc.PRScore'), key: 'PR' }
       ],
       table_columns_Simple_print: [//打算輸出的表單格式(簡單)
         { title: this.$t('scoreCalc.IRSnumber'), key: 'irs' },{ title: this.$t('scoreCalc.name'), key: 'name' }, { title: this.$t('scoreCalc.id'), key: 'id' },
-        { title: this.$t('scoreCalc.class'), key: 'class' }, { title: this.$t('scoreCalc.th_homework'), key: 'homework' }, { title: this.$t('scoreCalc.th_exam'), key: 'evaluate' },
+        { title: this.$t('scoreCalc.class'), key: 'class' },  { title: this.$t('scoreCalc.th_exam'), key: 'evaluate' }, { title: this.$t('scoreCalc.th_homework'), key: 'homework' },
         { title: this.$t('scoreCalc.totalScore'), key: 'total' }
       ],
     };
@@ -890,9 +894,10 @@ export default {
         lazyLoading.value = false;
       }, Math.random() * 1000 + 250);
     },
-    calculatePR(index,rowData) {//PR值計算
+    calculatePR(rowData) {//PR值計算
+      //console.log("calculatePR in/rowData.total="+rowData.total);
       const N = this.table_class_Data_print.length; // 團體總人數
-      const studentScore = rowData.total; // 學生的總分      
+      const studentScore = this.table_class_Data_print.find(item => item.id === rowData.id).total; // 學生的總分      
       const sortedScores = this.table_class_Data_print.map(item => item.total).sort((a, b) => b - a);
       const studentRank = sortedScores.indexOf(studentScore) + 1;// 學生的排名
       // 使用公式計算 PR 值,PR=[(N-R)/N]×100,其中N代表團體總人數,R是某個人的分數在團體中的排名。      
@@ -1011,7 +1016,7 @@ export default {
       }
       //PR動一個會所有人成績都有變動,因此要全部變更。 
       for(let j = 0;j< this.table_class_Data.length ; j++){        
-        this.table_class_Data[j].PR = this.calculatePR(j,this.table_class_Data[j]);
+        this.table_class_Data[j].PR = this.calculatePR(this.table_class_Data[j]);
         // console.log("this.table_class_Data[j].total="+this.table_class_Data[j].total);
         // console.log("calculatePR()="+this.calculatePR(j,this.table_class_Data[j]));
         // console.log("this.table_class_Data[j].PR="+this.table_class_Data[j].PR);
@@ -1234,13 +1239,7 @@ export default {
     getAPI_ScoreCalc_all(score_id) {//讀取成績API表單all成績資料
       this.isLoading = true;
       if (score_id) this.scoreCalcId = score_id;
-      console.log("score_id=" + score_id);//重要的console,成績表的ID
-      console.log({ 
-        "id": score_id,
-        "grouplistId": this.classInfo.grouplistId,
-        "classId": this.classInfo.classId,
-        "scope": this.listType,
-        });
+      console.log("score_id=" + score_id);//重要的console,成績表的ID      
       console.log("-------------------");
       this.$api.learnActivity.getScoreCalcAll({ 
         "id": score_id,
@@ -1430,7 +1429,7 @@ export default {
             }
             //需要等total算完才能繼續算pr,否則會有錯誤
             for(let i =0 ; i<this.table_class_Data.length ; i++){
-              this.table_class_Data[i].PR = this.calculatePR(i,this.table_class_Data[i]);
+              this.table_class_Data[i].PR = this.calculatePR(this.table_class_Data[i]);
               // console.log("i="+i);
               // console.log("this.table_class_Data[i].total="+this.table_class_Data[i].total);
               // console.log("this.table_class_Data[i].PR="+this.table_class_Data[i].PR);
@@ -2091,6 +2090,22 @@ export default {
       }, 1000);
     },
     menuClcik(index) {
+      if(!this.isSave){
+        this.$Modal.confirm({
+          title: this.$t("scoreCalc.remind"),
+          content: '<p>' + this.$t("scoreCalc.remindMessage6") + '</p>',
+          onOk: async () => {
+            this.menuChangeTable(index);
+          },
+            onCancel: () => {
+              //this.$Message.info('Clicked cancel');
+            }
+          });
+      }else{
+        this.menuChangeTable(index);
+      }
+    },
+    menuChangeTable(index){
       if(index===-1){
         this.tableClear();//清空表單資料
         this.tableReLoad_fun();//重新加載表單
@@ -2105,7 +2120,8 @@ export default {
             this.tableReLoad_fun();//重新加載表單
           }
         }
-      }      
+      } 
+      this.isSave = true;
     },
     //拖曳相關--str---↓↓--
     allowDrop(e) {//取消默認行為

+ 80 - 0
TEAMModelOS/ClientApp/src/view/mycourse/syllabus/DialogBox/AddCard.vue

@@ -0,0 +1,80 @@
+<template>
+  <div class="AddCard-container">
+    <Modal v-model="DialogBoxBtn" title="新增節點" @on-ok="ok" @on-cancel="cancel">
+      <div>
+        名稱:
+        <Input v-model="nameValue" placeholder="Enter something..." style="width: 400px" />
+      </div>
+      <div class="AddCard-choose">
+        <div class="div-centerY">
+          <RadioButton v-model="RadioChoose" inputId="ingredient1" value="Chapter" />
+          <span class="AddSpen">章節</span>
+        </div>
+        <div class="div-centerY">
+          <RadioButton v-model="RadioChoose" inputId="ingredient2" value="Section" />
+          <span class="AddSpen">項目
+            <Select v-model="selectOne" style="width:200px" placeholder="課綱">
+              <Option v-for="item in syllabusList" :value="item.id" :key="item.id">{{ item.name }}</Option>
+            </Select>
+          </span>
+        </div>
+      </div>
+    </Modal>
+  </div>
+</template>
+<script>
+import RadioButton from 'primevue/radiobutton';
+import 'primevue/resources/themes/saga-blue/theme.css'; // 根據你喜好的主題選擇
+import 'primevue/resources/primevue.min.css';
+//import AddSyllabus from "./addSyllabus.vue"
+
+export default {
+  components: {
+    RadioButton,
+  },
+  props: {
+    DialogBoxCtl: Boolean,
+    chooseOne:Object,
+    cardData:Array,
+  },
+  data() {
+    return {
+      DialogBoxBtn: false,
+      RadioChoose: '',//Chapter章節 / Section資源卡片
+      selectOne:'',//多選,選擇的內容
+      nameValue:'',//輸入的內容
+      syllabusList: [],//放入下拉選單的選項
+    }
+  },
+  computed: {
+  },
+  created() {
+    this.syllabusList.push({id:this.chooseOne.id,name:this.chooseOne.name});
+    if(this.cardData){
+      for(let i = 0;i<this.cardData.length;i++){
+        this.syllabusList.push({id:this.cardData[i].id,name:this.cardData[i].name});
+      }
+    }
+  },
+  methods: {
+    ok() {
+      // this.$Message.info('Clicked ok');
+      this.$emit('DialogBoxClose', false);
+    },
+    cancel() {
+      this.$emit('DialogBoxClose', false);
+    },
+  },
+  watch: {
+    DialogBoxCtl(newVal) {
+      this.DialogBoxBtn = newVal;
+    },    
+  }
+}
+</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>

+ 102 - 0
TEAMModelOS/ClientApp/src/view/mycourse/syllabus/DialogBox/AddResources.vue

@@ -0,0 +1,102 @@
+<template>
+  <div class="AddResources-container">
+    <Modal v-model="DialogBoxBtn" title="新增資源" @on-ok="ok" @on-cancel="cancel" :width="TabsClick.width" class="AddResources-Modal">
+      <Tabs type="card" :animated="false" @on-click="TabsClickFun">
+        <TabPane label="內容資源" name="content">
+          <Button class="attend-button" :type="C_buttonType[0].btn ? 'primary':'default'" @click="changePage('content',0)">雲端內容</Button>
+          <Button class="attend-button" :type="C_buttonType[1].btn ? 'primary':'default'" @click="changePage('content',1)">本機檔案</Button>          
+          <Button class="attend-button" :type="C_buttonType[2].btn ? 'primary':'default'" @click="changePage('content',2)">超連結</Button>  
+          <div v-if="TabsClick.tab==='content'" class="content-div">
+            <CloudContent v-if="C_buttonType[0].btn"></CloudContent>
+            <LocalFile v-else-if="C_buttonType[1].btn"></LocalFile>            
+            <URL v-else-if="C_buttonType[2].btn"></URL>
+          </div>          
+        </TabPane>
+        <TabPane label="活動資源" name="activity">
+          <Button class="attend-button" :type="A_buttonType[0].btn ? 'primary':'default'" @click="changePage('activity',0)">個人評量</Button>
+          <Button class="attend-button" :type="A_buttonType[1].btn ? 'primary':'default'" @click="changePage('activity',1)">作業活動</Button>
+          <Button class="attend-button" :type="A_buttonType[2].btn ? 'primary':'default'" @click="changePage('activity',2)">個人投票</Button>
+          <Button class="attend-button" :type="A_buttonType[3].btn ? 'primary':'default'" @click="changePage('activity',3)">個人問卷</Button>
+          <div v-if="TabsClick.tab==='activity'" class="content-div">
+            <Evaluate v-if="A_buttonType[0].btn"></Evaluate>
+            <Homework v-else-if="A_buttonType[1].btn"></Homework>
+            <Vote v-else-if="A_buttonType[2].btn"></Vote>
+            <Questionnaire v-else-if="A_buttonType[3].btn"></Questionnaire>
+          </div>          
+        </TabPane>
+      </Tabs>
+    </Modal>
+  </div>
+</template>
+<script>
+import CloudContent from "./Resources/C_CloudContent.vue"//雲端內容
+import LocalFile from "./Resources/C_LocalFile.vue"//本機檔案
+import URL from "./Resources/C_URL.vue"//超連結
+import Evaluate from "./Resources/A_Evaluate.vue"//個人評量
+import Homework from "./Resources/A_Homework.vue"//作業活動
+import Vote from "./Resources/A_Vote.vue"//個人投票
+import Questionnaire from "./Resources/A_Questionnaire.vue"//個人問卷
+
+export default {
+  components: {
+    CloudContent,LocalFile,URL,Evaluate,Homework,Vote,Questionnaire
+  },
+  props: {
+    DialogBoxCtl: Boolean,    
+  },
+  data() {
+    return {
+      DialogBoxBtn:false,
+      TabsClick:{tab:'',width:800},//{<Tab>類型/按鈕類型/寬度}
+      //雲端內容/本機檔案/超連結↓
+      C_buttonType: [{btn:true,width:800},{btn:false,width:800},{btn:false,width:800}],
+      //個人評量/作業活動/個人投票/個人問卷↓
+      A_buttonType: [{btn:true,width:1000},{btn:false,width:800},{btn:false,width:800},{btn:false,width:800}],
+    }
+  },
+  computed: {
+  },
+  created() {
+    
+  },
+  mounted(){
+    this.TabsClick.tab = 'content';//必須在產生後才給值,否則tabs會互相干擾
+  },
+  methods: {  
+    TabsClickFun(name){//確認目前點到哪個標籤。此判斷是為了防止Tabs內外互相干擾
+      this.TabsClick.tab = name;
+      if(name==='content') this.TabsClick.width = this.C_buttonType.find(item => item.btn === true).width;
+      else if(name==='activity') this.TabsClick.width = this.A_buttonType.find(item => item.btn === true).width;      
+    },  
+    changePage(type,index){
+      let widthNum = type === 'content' ? this.C_buttonType[index].width : this.A_buttonType[index].width;
+      this.TabsClick = { tab: type, button: index, width: widthNum };
+      if(type==='content'){
+        for(let i =0;i<this.C_buttonType.length;i++)this.$set(this.C_buttonType[i], 'btn' , false);
+        this.$set(this.C_buttonType[index], 'btn' , true);
+      }else if(type==='activity'){
+        for(let i =0;i<this.A_buttonType.length;i++)this.$set(this.A_buttonType[i], 'btn', false);
+        this.$set(this.A_buttonType[index], 'btn', true);
+      }
+    },
+    ok() {
+      // this.$Message.info('Clicked ok');
+      this.$emit('DialogBoxClose',false);
+    },
+    cancel() {
+      this.$emit('DialogBoxClose',false);
+    },
+  },
+  watch: {
+    DialogBoxCtl(newVal) {
+      this.DialogBoxBtn= newVal;
+    }
+  }
+}
+</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>

+ 70 - 0
TEAMModelOS/ClientApp/src/view/mycourse/syllabus/DialogBox/DialogBox.less

@@ -0,0 +1,70 @@
+/deep/ .ivu-modal-header-inner{
+    font-weight: bold !important;
+}
+.div-center{//水平置中
+    display: flex; 
+    justify-content: center; 
+    text-align:center;
+    width:100%;
+}
+.div-centerY{//垂直置中
+    display: flex; 
+    align-items: center;  
+}
+.between{//分兩邊
+    display: flex;
+    justify-content: space-between;
+}
+.gray-font{
+    font-size: 10px;
+    color: #808695;
+}
+.showClass-div
+    {
+        margin-bottom: 10px;
+
+        .showClass
+        {
+            color: #2d8cf0;
+            padding: 5px;
+            border: 1px solid #2d8cf0;
+            border-radius: 5px;
+            margin: 5px;
+        }
+    }
+.SortingDiv{
+    border: 1px solid #c5c8ce;
+    overflow: auto;
+    max-height: 300px;
+    .itemCard{
+        margin: 10px 5px;
+    }
+}
+.mleft20{
+    margin-left: 20px;
+}
+
+.AddCard-choose{
+    display: flex;
+    margin: 5px;
+    margin-top: 10px;
+    .AddSpen{
+        margin-left: 5px;
+        margin-right: 30px;
+    }
+}
+
+
+
+.AddResources-Modal{
+    .attend-button{
+        white-space:normal ;
+        height: auto;
+        margin-right:15px;
+        padding: 2px 15px;
+    }
+    .content-div{
+        margin-top: 20px;
+    }
+    
+}

+ 50 - 0
TEAMModelOS/ClientApp/src/view/mycourse/syllabus/DialogBox/DialogBoxTemp.vue

@@ -0,0 +1,50 @@
+<template>
+  <div class="AddCard-container">
+    <Modal v-model="DialogBoxBtn" title="新增節點" @on-ok="ok" @on-cancel="cancel">
+      123
+    </Modal>
+  </div>
+</template>
+<script>
+//import AddSyllabus from "./addSyllabus.vue"
+
+export default {
+  components: {
+
+  },
+  props: {
+    DialogBoxCtl: Boolean,    
+  },
+  data() {
+    return {
+      DialogBoxBtn:false,
+      
+    }
+  },
+  computed: {
+  },
+  created() {
+    
+  },
+  methods: {
+    ok() {
+      // this.$Message.info('Clicked ok');
+      this.$emit('DialogBoxClose',false);
+    },
+    cancel() {
+      this.$emit('DialogBoxClose',false);
+    },
+  },
+  watch: {
+    DialogBoxCtl(newVal) {
+      this.DialogBoxBtn= newVal;
+    }
+  }
+}
+</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>

+ 63 - 0
TEAMModelOS/ClientApp/src/view/mycourse/syllabus/DialogBox/ReleaseSyllabus.vue

@@ -0,0 +1,63 @@
+<template>
+  <div class="ReleaseSyllabus-container">
+    <Modal v-model="ReleaseBtn" title="上架設定" @on-ok="ok" @on-cancel="cancel">
+      <div class="mleft20">
+        <RadioGroup v-model="addData.chooseWay" vertical>
+          <Radio label="Empty">
+            <span>立即上架</span>
+          </Radio>        
+          <Radio label="Choose" :tabindex="0">
+            <span>預約上架:</span>          
+          </Radio>
+          <DatePicker type="daterange" placement="bottom-end" placeholder="Select date" style="width: 230px; margin-left:20px" />             
+        </RadioGroup>
+      </div>      
+    </Modal>
+  </div>
+</template>
+<script>
+//import AddSyllabus from "./addSyllabus.vue"
+
+export default {
+  components: {
+
+  },
+  props: {
+    ReleaseCtl: Boolean,    
+  },
+  data() {
+    return {
+      ReleaseBtn:false,
+      addData: {//新增課綱的相關內容
+        chooseWay: 'Empty',
+        codeData: '',
+      },
+    }
+  },
+  computed: {
+  },
+  created() {
+    
+  },
+  methods: {
+    ok() {
+      // this.$Message.info('Clicked ok');
+      this.$emit('ReleaseOpenFun',true);
+    },
+    cancel() {
+      this.$emit('ReleaseFun',false);
+    },
+  },
+  watch: {
+    ReleaseCtl(newVal) {
+      this.ReleaseBtn= newVal;
+    }
+  }
+}
+</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>

+ 319 - 0
TEAMModelOS/ClientApp/src/view/mycourse/syllabus/DialogBox/Resources/A_Evaluate.vue

@@ -0,0 +1,319 @@
+<template>
+  <div class="Evaluate-container">
+    <div class="between">
+      <div class="evaluation-attr-wrap">
+      <p class="wrap-label">{{$t('learnActivity.createEv.baseInfo')}}</p>
+      <div style=" padding-top:15px;"
+        class="ivu-select-nochoose light-el-input">
+          <Form ref="evaForm" :model="evaluationInfo" label-position="top" class="evaluation-attr-form " label-colon
+            :rules="ruleValidate">
+            <FormItem :label="$t('learnActivity.createEv.evName')" prop="name">
+              <Input v-special-char v-model="evaluationInfo.name"
+                :placeholder="$t('learnActivity.createEv.evName')"></Input>
+            </FormItem>
+            <FormItem prop="source">
+              <label slot="label" style="width:200px">
+                <span>{{$t('learnActivity.createEv.evMode')}}</span>
+                <Tooltip :content="$t('tip.examMode')" transfer theme="light" max-width="300">
+                  <Icon type="ios-information-circle-outline" color="#1cc0f3" style="margin-left:5px" />
+                </Tooltip>
+              </label>
+              <Select v-model="evaluationInfo.source" @on-change="checkItemSort">
+                <!-- 暂时取消创建课中评量 --> <!-- 20230105課中評測放回 -->
+                <Option v-for="(item,index) in $GLOBAL.EV_MODE()" v-show="true" :label="item.label" :value="item.value"
+                  :key="index"
+                  :disabled="index > 1 && (!$store.state.userInfo.hasSchool || !$store.state.user.schoolProfile.svcStatus.VABAJ6NV)">
+                  <div>
+                    <span>
+                      {{ item.label }}
+                    </span>
+                    <span @click.stop="toProduct" v-show="index > 1 && scaleStatusText()" class="no-auth-tag">
+                      {{ scaleStatusText() }}
+                    </span>
+                  </div>
+                </Option>
+              </Select>
+            </FormItem>
+            <FormItem>
+              <Checkbox v-model="evaluationInfo.isCompletion">{{ $t('learnActivity.createEv.completeScore') }}
+              </Checkbox>
+            </FormItem>
+          </Form>
+      </div>
+      </div>
+      <div class="evaluation-question-wrap">
+        <div class="evaluation-question-main">
+          <Tabs v-model="activeTab" type="card" class="question-main-tabs" name="createTest">
+            <TabPane :label="$t('learnActivity.createEv.papersLabel')" name="manualPaper"
+              v-if="createType == 'manualPaper'" :index="1" tab="createTest">
+              <ManualPaper @selectPaper="selectPaper"
+                :selectedId="evaluationInfo.paperInfo[0] ? evaluationInfo.paperInfo[0].id : ''"></ManualPaper>
+            </TabPane>
+            <TabPane :label="$t('learnActivity.createEv.perviewLabel')" name="preview" :index="2" tab="createTest">
+              <div class="teacher-preview-container">
+                <!--返回顶部-->
+                <!-- <div class="back-to-top" :title="$t('learnActivity.mgtScEv.returnTop')" v-show="showBack" @click="handleBackToTop">
+                                      <Icon type="ios-arrow-up" />
+                                  </div> -->
+                <BackToTop @on-to-top="handleBackToTop"></BackToTop>
+                <vuescroll ref="paper-preview" @handle-scroll="checkBackTop">
+                  <TestPaper v-if="activeTab == 'preview'"
+                    :paper="evaluationInfo.paperInfo.length ? evaluationInfo.paperInfo[0] : {item:[]}" isExamPaper
+                    hideSheet></TestPaper>
+                </vuescroll>
+              </div>
+            </TabPane>
+          </Tabs>
+        </div>
+      </div>
+    </div>    
+  </div>
+</template>
+<script>
+//參考於CreatePrivEva.vue
+import BlobTool from '@/utils/blobTool.js'
+import TestPaper from '@/view/evaluation/index/TestPaper.vue'
+import ManualPaper from '../../../../learnactivity/ManualPaper.vue'
+export default {
+  components: {
+    TestPaper,
+    ManualPaper,
+  },
+  props: {
+  },
+  data() {
+    return {
+      activeTab: 'manualPaper',
+      createType: 'manualPaper',
+      evaluationInfo: {
+        name: '',
+        targets: [],
+        classes: [],
+        scope: '',
+        type: '',  //测试类别
+        source: '',
+        publish: '0',
+        examType: null, //测试类型
+        paperInfo: [],
+        papers: [],
+        isCompletion: 0
+      },
+      ruleValidate: {
+        name: [
+          { required: true, message: this.$t('learnActivity.createEv.errTips1'), trigger: 'change' }
+        ],
+        'type.id': [
+          { required: true, message: this.$t('learnActivity.createEv.errTips2'), trigger: 'change' }
+        ],
+        source: [
+          { required: true, message: this.$t('learnActivity.createEv.errTips3'), trigger: 'change' }
+        ],
+        'type': [
+          { required: true, message: this.$t('learnActivity.createEv.errTips4'), trigger: 'change' }
+        ],
+        'examType.id': [
+          { required: true, message: this.$t('learnActivity.createEv.errTips5'), trigger: 'change' }
+        ],
+        targets: [
+          { required: true, message: this.$t('learnActivity.createEv.errTips6'), type: 'array', trigger: 'change' }
+        ],
+        classes: [
+          { required: true, message: this.$t('learnActivity.createEv.errTips6'), type: 'array', trigger: 'change' }
+        ],
+        publish: [
+          { required: true, message: this.$t('learnActivity.createEv.errTips7'), trigger: 'change' }
+        ],
+      },
+    }
+  },
+  computed: {
+  },
+  created() {
+    //编辑评测逻辑
+    let routerData = this.$route.params.evaluationInfo
+    if (routerData) {
+      // console.log(JSON.stringify(routerData))
+      console.log(routerData.subjects)
+      this.initData(routerData)
+    }
+  },
+  methods: {
+    scaleStatusText() {
+      if (!this.$store.state.userInfo.hasSchool) {
+        return this.$t('learnActivity.createEv.noSchool')
+      }
+      if (this.$store.state.user.schoolProfile.svcStatus.VABAJ6NV) {
+        return ''
+      } else {
+        return this.$t('learnActivity.createEv.noAuth')
+      }
+    },
+    toProduct() {
+      if (this.$store.state.userInfo.hasSchool) {
+        this.$router.push({
+          name: 'product'
+        })
+      }
+    },
+    //设置评测模式后检查试卷itemSort
+    checkItemSort() {
+      if (this.evaluationInfo.source == '2') {
+        let p = this.evaluationInfo.paperInfo.find(paper => paper.itemSort == 1)
+        if (p) {
+          this.$Modal.confirm({
+            title: this.$t('learnActivity.createEv.sort1'),
+            content: this.$t('learnActivity.createEv.sort2'),
+            onOk: () => {
+              for (let i = 0; i < this.evaluationInfo.paperInfo.length; i++) {
+                if (this.evaluationInfo.paperInfo[i].itemSort == 1) {
+                  let defaultPaper = {
+                    periodName: this.evaluationInfo.name,
+                    periodId: this.evaluationInfo.id,
+                    subjectName: this.evaluationInfo.paperInfo[i].subjectName,
+                    subjectId: this.evaluationInfo.paperInfo[i].subjectId,
+                    createType: 'manualPaper',
+                    score: 100,
+                    item: [],
+                    filter: {},
+                    name: this.$t('learnActivity.createEv.defaultPaper'),
+                    time: 0 //作答时间
+                  }
+                  this.evaluationInfo.paperInfo.splice(i, 1, this._.cloneDeep(defaultPaper))
+                }
+              }
+            },
+            onCancel: () => {
+              this.evaluationInfo.source = '0'
+            }
+          })
+        }
+      }
+    },
+    //复制或编辑评测初始化数据
+    async initData(data) {
+      let subjects = this._.cloneDeep(data.subjects)
+      data.id = ''
+      data.name = `${data.name}(${this.$t('learnActivity.mgtScEv.copy')})`
+      // this.endTime = new Date(data.endTime)
+      let pp = {
+        "@DESC": "createTime",
+        code: data.papers[0].code?.replace('Paper-', ''),
+        scope: data.papers[0].scope,
+        id: data.papers.map(item => item.id)
+      }
+      data.paperInfo = []
+      data.papers = []
+      data.publish = '0'
+      this.evaluationInfo = data
+      console.log(JSON.stringify(this.evaluationInfo))
+      try {
+        let paperInfo = await this.$api.learnActivity.FindExamPaper(pp)
+        if (!paperInfo.length) {
+          // pp.sc
+          paperInfo = await this.$api.learnActivity.FindExamPaper(pp)
+        }
+        for (let i = 0; i < paperInfo.papers.length; i++) {
+          let fullPaper = await this.$evTools.getFullPaper(paperInfo.papers[i])
+          this.comfirmSelectPaper(paperInfo.papers[i], fullPaper)
+        }
+        this.evaluationInfo.subjects = subjects
+      } catch (e) {
+
+      }
+      this.activeTab = 'preview'
+      // this.endTime = new Date(new Date(new Date().toLocaleDateString()).getTime() + 2 * 24 * 60 * 60 * 1000 - 1)
+      // this.evaluationInfo.endTime = new Date(new Date().toLocaleDateString()).getTime() + 2 * 24 * 60 * 60 * 1000 - 1
+      // data.id = ''
+      // data.name = `${data.name}(${this.$t('learnActivity.mgtScEv.copy')})`
+      // this.startTime = new Date(data.startTime)
+      // this.endTime = new Date(data.endTime)
+      // data.paperInfo = []
+      // this.defTargets = data.targets
+      // for (let i = 0; i < data.papers.length; i++) {
+      //     let paper = this._.cloneDeep(data.papers[i])
+      //     data.paperInfo.push(paper)
+      //     data.paperInfo[i].subjectId = data.subjects[i].id
+      //     data.paperInfo[i].subjectName = data.subjects[i].name
+      // }
+      // data.publish = '0'
+      // this.evaluationInfo = data
+      // this.activeTab = 'preview'
+    },
+
+    comfirmSelectPaper(simplePaper, fullPaper) {
+      if (!this.evaluationInfo.name) {
+        this.evaluationInfo.name = simplePaper.name
+      }
+      fullPaper.examId = ''
+      fullPaper.examScope = this.mode
+      fullPaper.examCode = this.$store.state.userInfo.schoolCode
+      this.evaluationInfo.paperInfo[0] = fullPaper
+      this.evaluationInfo.papers[0] = simplePaper
+      this.goToPreview()
+    },
+    goToPreview() {
+      this.activeTab = 'preview'
+    },
+    /**
+     * 处理挑选试卷事件
+     * @param data
+     */
+    selectPaper(data) {
+      if (this.evaluationInfo.source == '2' && data.itemSort == 1) {
+        return this.$Message.warning(this.$t('learnActivity.createEv.sort3'))
+      }
+      let simplePaper = data
+      this.$Modal.confirm({
+        title: this.$t('learnActivity.createEv.stPaperTitle'),
+        content: `${this.$t('learnActivity.createEv.stPaperContent')}${data.name}?`,
+        onOk: async () => {
+          let fullPaper = await this.$evTools.getFullPaper(simplePaper)
+          this.comfirmSelectPaper(simplePaper, fullPaper)
+        }
+      })
+    },
+    /**vuescroll回到顶部 */
+    handleBackToTop() {
+      this.$refs['paper-preview'].scrollTo(
+        {
+          y: '0'
+        },
+        300
+      )
+    },
+    /**
+     * 判断是否显示回到顶部按钮
+     * @param vertical
+     * @param horizontal
+     * @param nativeEvent
+     */
+    checkBackTop(vertical, horizontal, nativeEvent) {
+      if (vertical.scrollTop > 100) {
+        this.showBack = true
+      } else {
+        this.showBack = false
+      }
+    },
+  },
+  watch: {
+  }
+}
+</script>
+<style lang="less" scoped>
+@import "./Resources.less";
+</style>
+<style lang="less">
+//.ivu-table.ivu-table-stripe .ivu-table-cell.demo-table-info-column
+
+.evaluation-question-main .ivu-tabs-tab-active {
+  font-weight: 600;
+}
+
+.evaluation-question-main .ivu-tabs-bar {
+  border-color: #404040;
+  margin-bottom: 0px;
+  border-bottom: none;
+}
+
+
+</style>

+ 273 - 0
TEAMModelOS/ClientApp/src/view/mycourse/syllabus/DialogBox/Resources/A_Homework.vue

@@ -0,0 +1,273 @@
+<template>
+  <div class="Homework-container">
+    <Form ref="voteForm" :model="voteForm" label-position="top" :rules="ruleValidate" :disabled="!voteFormEdit" hide-required-mark>
+      <div style="display: flex;">
+        <div style="width: 48%;">
+          <FormItem :label="$t('homework.form.name')" prop="name">
+            <Input :class="!voteFormEdit ? 'vote-form-disabled':''" v-model="voteForm.name" 
+              :placeholder="$t('homework.form.namePlace')"></Input>
+          </FormItem>
+          <!-- 作业描述 -->
+          <FormItem :label="$t('homework.form.description')" prop="description">
+            <div ref="descriptionEditor" style="text-align:left" v-show="voteFormEdit"></div>
+            <div v-html="voteForm.description" v-show="!voteFormEdit" style="margin:10px;font-size:16px;font-weight:bold;word-break: break-all;"></div>
+          </FormItem>      
+        </div>
+        <div style="width: 48%;padding-left: 2%;margin-left:2%;border-left: 1px dashed #D2D2D2;">
+          <!-- 作业附件 -->
+          <FormItem :label="$t('homework.form.attachments')">            
+            <p style="text-align: right;font-size: 12px;color:#1ba6dd;text-decoration: underline;cursor: pointer;" @click="openChooseContent">{{ $t('homework.chooseContent') }}</p>
+            <BaseUpload simpleUpload v-show="voteFormEdit" @uploadFinish="uploadFinish"></BaseUpload>
+            <div v-if="attachmentsArr.length" class="attachments-list">
+              <div class="attachments-item" v-for="(item,index) in attachmentsArr" :key="index">
+                <span class="name">
+                  <span style="margin-right: 10px;">{{ getSizeByBytes(item.size) }}</span>
+                  <Icon type="md-close" color="#adadad" @click="removeAttachment(index)" v-if="voteFormEdit" />                  
+                </span>
+              </div>
+            </div>
+          </FormItem>
+          <!-- 作业其他选项 -->
+          <FormItem :label="$t('homework.form.other')" style="margin-bottom: 0px;">
+            <CheckboxGroup v-model="voteForm.allowSupply">
+              <Checkbox label="allow">{{ $t('homework.form.allowFix') }}</Checkbox>
+            </CheckboxGroup>
+            <CheckboxGroup v-model="voteForm.allowComment">
+              <Checkbox label="allow">{{ $t('homework.form.allowComment') }}</Checkbox>
+            </CheckboxGroup>
+            <CheckboxGroup v-model="voteForm.mustSubmit">
+              <Checkbox label="mustSubmit">{{ $t('homework.form.mustSubmit') }}</Checkbox>
+            </CheckboxGroup>
+            <CheckboxGroup v-model="voteForm.extLimit" v-if="voteForm.mustSubmit.length">
+              <Checkbox label="extLimit">{{ $t('homework.form.allowFileType') }}</Checkbox>
+            </CheckboxGroup>
+            <Select v-model="chooseExtends" filterable multiple allow-create @on-create="handleCreate" v-if="voteForm.extLimit.length && voteForm.mustSubmit.length && voteFormEdit" :placeholder="$t('homework.extLimitPlace')">
+              <Option v-for="item in extendArr" :value="item" :key="item">.{{ item  }}</Option>
+            </Select>
+            <div v-if="chooseExtends.length && !voteFormEdit">
+              <span>{{ $t('homework.limitType') }}:</span>
+              <span v-for="(item,index) in chooseExtends" :key="index"
+                style="background-color: #5da9ff;margin-right: 10px;color: #fff;padding: 2px 10px;border-radius: 4px;">.{{ item }}</span>
+            </div>
+          </FormItem>
+        </div>
+      </div>
+    </Form>
+
+    <!-- 关联内容弹窗 -->
+    <Modal v-model="contentModal" width="880" footer-hide class="related-modal base-repair-modal">
+      <div class="modal-header" slot="header">{{$t('evaluation.newExercise.contentRelate')}}</div>
+      <NewChooseContent :showSyllabus="false" :showOther="false" :showQuestion="false" :showPaper="false" ref="chooseContentRef" @on-file-change="onSelectFile" v-if="contentModal"></NewChooseContent>
+
+      <Button class="modal-btn" @click="onConfirmRelate">{{$t('evaluation.confirm')}}</Button>
+    </Modal>
+
+  </div>
+</template>
+<script>
+//程式碼參考BaseHwForm.vue
+import E from 'wangeditor'
+
+export default {
+  components: {
+
+  },
+  props: { 
+    editItem: {
+      type: Object,
+      default: null
+    },
+  },
+  data(vm) {
+    return {
+      maxAttachmentCount: 5,
+      maxFileSize: 1024 * 1024 * 50,
+      contentModal: false,
+      extendArr: ['doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'zip', 'rar', 'mp3', 'mp4', 'txt', 'jpg', 'jpeg',
+        'png'
+      ],
+      chooseExtends: [],
+      attachmentsArr: [],
+      classNameArr: [],
+      isEdit: false,
+      editInfo: null,
+      voteFormEdit: true,
+      descriptionEditor: null,
+      voteForm: {
+        name: '',
+        classes: [],
+        startTime: '',
+        endTime: '',
+        description: '',
+        allowSupply: [],
+        allowComment: [],
+        extLimit: [],
+        mustSubmit: []
+      },
+      ruleValidate: {
+        name: [{
+          required: true,
+          message: vm.$t('homework.form.ruleName'),
+          trigger: 'blur'
+        }],
+        // description: [{
+        // 	// required: true,
+        // 	message: vm.$t('homework.form.ruleDescription'),
+        // 	trigger: 'blur'
+        // }],
+        // startTime: [{
+        // 	type: 'date',
+        // 	message: this.$t('learnActivity.createEv.errTips8'),
+        // 	trigger: 'change'
+        // }],
+        endTime: [{
+          required: true,
+          type: 'date',
+          message: this.$t('learnActivity.createEv.errTips9'),
+          trigger: 'change'
+        }]
+      },
+      selectFiles: [],
+    }
+  },
+  
+  created() {
+    
+  },
+  methods: {
+    openChooseContent() {
+      this.contentModal = true
+      this.$nextTick(() => {
+        if (this.$refs.chooseContentRef) {
+          this.$refs.chooseContentRef.clickTab('content')
+        }
+      })
+    },
+    /* 内容挑选变化 */
+    onSelectFile(val) {
+      this.selectFiles = val.files
+    },
+    /* 确认添加站内资源 */
+    onConfirmRelate() {
+      for (let i = 0; i < this.selectFiles.length; i++) {
+        let file = this.selectFiles[i]
+        if (this.attachmentsArr.length >= this.maxAttachmentCount) {
+          this.$Message.warning(this.$t('homework.countTip'))
+          return
+        } else {
+          this.attachmentsArr.push(file)
+        }
+      }
+      this.contentModal = false
+    },
+    /* 自定义添加文件后缀 */
+    handleCreate(val) {
+      this.extendArr.push(val);
+    },
+    /* 添加附件 */
+    uploadFinish(file) {
+      if (this.attachmentsArr.length >= this.maxAttachmentCount) {
+        this.$Message.warning(this.$t('homework.countTip'))
+      } else if (file.size > this.maxFileSize) {
+        this.$Message.warning(this.$t('homework.sizeTip'))
+      } else {
+        this.attachmentsArr.push(file)
+      }
+    },
+     /* 移除附件 */
+    removeAttachment(index) {
+      this.attachmentsArr.splice(index, 1)
+    },
+    /* 根据字节数转换为大小 */
+    getSizeByBytes(bytes) {
+      return bytes / 1024 < 1024 ? (bytes / 1024).toFixed(1) + 'KB' : bytes / 1024 / 1024 < 1024 ? (bytes /
+        1024 / 1024).toFixed(1) + 'M' : (bytes / 1024 / 1024 / 1024).toFixed(1) + 'G'
+    },
+    /* 渲染作业活动 */
+    async doRender(item) {
+      console.log('需要渲染的投票对象', item)
+      if (item.targetType === 'research') {
+        console.log(this.groupList);
+        let groupNameArr = []
+        item.tchLists.forEach(i => {
+          let findObj = this.groupList.find(j => j.id === i)
+          if (findObj) {
+            groupNameArr.push(findObj)
+          }
+        })
+        this.classNameArr = groupNameArr
+      } else {
+        this.classNameArr = item.classes.length ? await this.getClassNameByIds(item.classes) : item
+          .stuLists.length ? await this.getClassNameByIds(item.stuLists) : []
+      }
+      this.voteForm = null
+      this.chooseExtends = item.extLimit
+      this.attachmentsArr = item.attachments
+      this.voteForm = {
+        name: item.name,
+        code: item.code,
+        targets: item.targets,
+        classes: item.classes.length ? item.classes : item.stuLists,
+        startTime: item.startTime ? new Date(item.startTime) : '',
+        endTime: item.endTime ? new Date(item.endTime) : '',
+        description: item.description,
+        allowSupply: item.allowSupply ? ['allow'] : [],
+        allowComment: item.allowComment ? ['allow'] : [],
+        mustSubmit: item.mustSubmit ? ['mustSubmit'] : [],
+        extLimit: item.extLimit.length ? ['extLimit'] : [],
+      }
+      this.descriptionEditor.txt.html(item.description)
+
+      this.$nextTick(() => {
+        this.$refs.voteForm.validateField('name')
+        this.$refs.voteForm.validateField('classes')
+        this.$refs.voteForm.validateField('description')
+      })
+    },
+  },
+  watch: {
+    editItem: {
+      handler(newValue) {
+        /** 编辑回显 */
+        if (newValue) {
+          console.log(newValue)
+          this.doRender(JSON.parse(JSON.stringify(newValue)))
+          this.isEdit = Boolean(newValue.id)
+          this.editInfo = JSON.parse(JSON.stringify(newValue))
+        }
+      },
+    }
+  },
+  mounted() {
+    let descriptionEditor = new E(this.$refs.descriptionEditor)
+    descriptionEditor.config.onchange = (html) => {
+      this.voteForm.description = html
+    }
+    descriptionEditor.config.height = 200
+    this.$editorTools.initSimpleEditor(descriptionEditor, this)
+    descriptionEditor.create()
+    this.descriptionEditor = descriptionEditor
+
+    if (this.editItem && this.editItem.name) {
+      console.log(this.editItem)
+      this.editInfo = JSON.parse(JSON.stringify(this.editItem))
+      this.doRender(this.editInfo)
+    }
+  },
+  computed: {
+    isDraft() {
+      return this.editInfo.progress === 'draft'
+    }
+  },
+}
+</script>
+<style lang="less" scoped>
+@import "./Resources.less";
+
+</style>
+<style lang="less">
+//.ivu-table.ivu-table-stripe .ivu-table-cell.demo-table-info-column
+
+
+
+</style>

+ 356 - 0
TEAMModelOS/ClientApp/src/view/mycourse/syllabus/DialogBox/Resources/A_Questionnaire.vue

@@ -0,0 +1,356 @@
+<template>
+  <div class="Questionnaire-container">
+    <Loading :top="200" bgColor="rgba(103, 103, 103, 0.27)" type="1" v-show="isLoading"></Loading>
+    <div class="body">
+      <div class="left">
+        <BaseQnForm :editItem="currentQn" @onAddSuccess="onAddSuccess" @onAddFail="isBtnLoading = false" ref="qnForm">
+        </BaseQnForm>
+      </div>
+      <div class="right">
+        <!-- 问卷提交数据 -->
+        <div class="qn-col qn-data-box">
+          <div class="qn-box-header">
+            <div class="qn-box-header-tools">
+              <div class="qn-box-header-tools-tool" style="margin-right: 10px">
+                <Dropdown @on-click="onAddItem">
+                  <Button icon="md-list-box">
+                    {{ $t('survey.addItem') }}
+                    <Icon type="ios-arrow-down"></Icon>
+                  </Button>
+                  <DropdownMenu slot="list">
+                    <DropdownItem name="single">{{ $t('survey.single') }}</DropdownItem>
+                    <DropdownItem name="multiple">{{ $t('survey.multiple') }}
+                    </DropdownItem>
+                    <DropdownItem name="judge">{{ $t('survey.judge') }}</DropdownItem>
+                    <DropdownItem name="subjective">{{ $t('survey.subjective') }}
+                    </DropdownItem>
+                  </DropdownMenu>
+                </Dropdown>
+              </div>
+              <div class="qn-box-header-tools-tool" style="margin-right: 10px">
+                <Button class="btn-save" icon="md-folder" :loading="isBtnLoading"
+                  @click="onSaveQn">{{ $t('survey.save') }}</Button>
+              </div>
+            </div>            
+          </div>
+          <div class="contect">
+            <BaseQuestionnaire :currentQn="currentQn" :students="allSsList" :isEdit="editable" ref="qnPaper" v-if="isEmptyData">
+            </BaseQuestionnaire>
+          </div>          
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+<script>
+import BaseQnForm from './part/BaseQnForm.vue';
+import BaseQuestionnaire from "@/components/questionnaire/BaseQuestionnaire.vue";
+export default {
+  components: {
+    BaseQnForm,BaseQuestionnaire,
+  },
+  props: {
+  },
+  data() {
+    return {
+      isEmptyData: true,
+      isLoading: false,
+      isBtnLoading: false,
+      editable: true,
+      qnList: [],
+      currentQn: {
+        progress: "pending",
+      },
+      noFinishStudents: [],
+      allSsList: [],
+      areaRecord: [],
+    }
+  },
+  mounted() {
+
+  },
+  computed: {
+    isAreaSurvey() {
+      return this.$route.name === 'manageAreaQuestionnaire'
+    },
+    getCurScope() {
+      return this.$route.name === "personalSurvey" ? "private" : "school";
+    },
+    isPrivate() {
+      return this.$route.name === 'personalSurvey'
+    },
+  },
+  created() {
+  },
+  methods: {
+    onAddItem(name) {
+      this.$refs.qnPaper.onMenuClick(name);
+    },
+    
+
+    /* 获取区级活动的作答数据 */
+    async getAreaRecord(schoolIndex) {
+      let curSchoolInfo = this.areaRecord[schoolIndex]
+      let schoolSas = await this.$evTools.getBlobSchoolSas(curSchoolInfo.code)
+      let fullPath = this.$evTools.getBlobHost() + '/' + curSchoolInfo.code + curSchoolInfo.url + schoolSas
+      let recordJson = JSON.parse(await this.$tools.getFile(fullPath))
+      console.log(curSchoolInfo)
+      console.log(recordJson)
+      this.allSsList = new Array(curSchoolInfo.count).fill('0')
+      this.noFinishStudents = new Array(curSchoolInfo.count - recordJson.userids.length).fill('0')
+    },
+
+    /* 保存问卷操作,由外層的確定觸發 */
+    async onSaveQn() {
+      try {
+        let qnBaseInfo = this.handleSubmit("qnForm");
+        let qnItems = this.$refs.qnPaper.items || [];
+
+        if (!qnItems.length) {
+          this.$Message.warning(this.$t('survey.noItemTip'))
+          return
+        }
+        this.isBtnLoading = true;
+        if (this.isAreaSurvey) {
+          qnBaseInfo.survey.scope = 'school'
+          qnBaseInfo.survey.id = this.$tools.guid()
+          // 将问卷试题内容保存到blob 用blobUrl来替换quesitons字段
+          qnBaseInfo.survey.answers = this.getSurveyAns(qnItems)
+          qnBaseInfo.survey.recordUrl = ""
+          qnBaseInfo.survey.blob = await this.doUploadAreaBlob(qnBaseInfo.survey, qnItems);
+          qnBaseInfo.survey.status = 1
+          console.log(qnBaseInfo)
+        } else {
+          // 将问卷试题内容保存到blob 用blobUrl来替换quesitons字段
+          qnBaseInfo.answers = this.getSurveyAns(qnItems)
+          qnBaseInfo.blob = await this.doUploadBlob(qnBaseInfo, qnItems);
+        }
+
+        // 开始保存问卷
+        this.saveorUpdataQn(qnBaseInfo)
+          .then((res) => {
+            this.$Message.success(this.$t('survey.doSuc'));
+            this.hasModify = true
+            this.onAddSuccess();
+          })
+          .catch((error) => {
+            this.$Message.error(`${error}`);
+          }).finally(data => {
+            this.isLoading = false;
+            this.isBtnLoading = false;
+          });
+      } catch (e) {
+        console.log(e)
+        this.isLoading = false;
+        this.isBtnLoading = false;
+      }
+
+    },
+    /* 保存问卷题目到Blob */
+    doUploadAreaBlob(qnBaseInfo, items) {
+      return new Promise(async (resolve, reject) => {
+        try {
+          let s = this.$store.state.user.userProfile.osblob_uri
+          // 获取初始化Blob需要的数据
+          let sasData = {
+            sas: '?' + this.$store.state.user.userProfile.osblob_sas,
+            name: 'teammodelos',
+            // url: s.split(s.substring(s.lastIndexOf('/')))[0]
+            url: this.$evTools.getBlobHost(s)
+          }
+          //初始化Blob
+          let containerClient = new blobTool(
+            sasData.url,
+            sasData.name,
+            sasData.sas,
+            qnBaseInfo.scope
+          );
+          let areaId = sessionStorage.getItem('areaId')
+          let itemUrls = await this.doUploadAreaItems(qnBaseInfo, items, containerClient)
+          qnBaseInfo.slides = itemUrls
+          let file = new File([JSON.stringify(qnBaseInfo)], "index.json");
+          // 等待上传blob的返回结果
+          let blobFile = await containerClient.upload(file, {
+            path: `${areaId}/survey/${qnBaseInfo.id}`,
+            checkSize: false,
+            root: `${areaId}/`
+          })
+
+          if (blobFile.blob) {
+            delete qnBaseInfo.slides
+            resolve(blobFile.blob)
+          } else {
+            this.$Message.error(this.$t('evaluation.newExercise.uploadErrorTip'));
+          }
+        } catch (e) {
+          this.isLoading = false;
+          this.isBtnLoading = false;
+          reject(e);
+        }
+      })
+    },
+    async doUploadAreaItems(qnBaseInfo, items, containerClient) {
+      return new Promise(async (resolve, reject) => {
+        let promiseArr = []
+        let areaId = sessionStorage.getItem('areaId')
+        for (let item of items) {
+          let curId = item.id || this.$tools.guid()
+          promiseArr.push(new Promise(async (r, j) => {
+            // let itemJsonFile = await this.$evTools.createBlobItem(exerciseItem);
+            let file = new File([JSON.stringify(item)], curId + ".json");
+            try {
+              // 等待上传blob的返回结果
+              // let blobFile = await containerClient.upload(file, `${areaId}/survey/${qnBaseInfo.id}`,{} , false,`${areaId}/`);
+              let blobFile = await containerClient.upload(file, {
+                path: `${areaId}/survey/${qnBaseInfo.id}`,
+                checkSize: false,
+                root: `${areaId}/`
+              })
+              if (blobFile.blob) {
+                console.log('上传Blob成功', blobFile)
+                r(blobFile.blob)
+              } else {
+                this.$Message.error(this.$t('evaluation.newExercise.uploadErrorTip'));
+              }
+            } catch (e) {
+              j(e)
+              this.isLoading = false;
+              this.isBtnLoading = false;
+              this.$Message.error(e.spaceError);
+            }
+          }))
+        }
+        Promise.all(promiseArr).then(result => {
+          resolve(result)
+        })
+      })
+    },
+
+    /* 上传index.json */
+    async doUploadItems(qnBaseInfo, items) {
+      return new Promise(async (resolve, reject) => {
+        let promiseArr = []
+        // 获取初始化Blob需要的数据
+        let sasData = qnBaseInfo.scope === 'private' ? await this.$tools.getPrivateSas() :
+          await this.$tools.getSchoolSas();
+        //初始化Blob
+        let containerClient = new blobTool(
+          sasData.url,
+          sasData.name,
+          sasData.sas,
+          qnBaseInfo.scope
+        );
+
+        for (let item of items) {
+          let curId = item.id || this.$tools.guid()
+          promiseArr.push(new Promise(async (r, j) => {
+            // let itemJsonFile = await this.$evTools.createBlobItem(exerciseItem);
+            let file = new File([JSON.stringify(item)], curId + ".json");
+            try {
+              // 等待上传blob的返回结果
+              let blobFile = await containerClient.upload(file, { path: 'survey/' + qnBaseInfo.id });
+              if (blobFile.blob) {
+                console.log('上传Blob成功', blobFile)
+                r(blobFile.blob)
+              } else {
+                this.$Message.error(this.$t(
+                  'evaluation.newExercise.uploadErrorTip'));
+              }
+            } catch (e) {
+              j(e)
+              this.isLoading = false;
+              this.isBtnLoading = false;
+              this.$Message.error(e.spaceError);
+            }
+          }))
+        }
+        Promise.all(promiseArr).then(result => {
+          resolve(result)
+        })
+      })
+    },
+    /* 保存问卷题目到Blob */
+    doUploadBlob(qnBaseInfo, items) {
+      return new Promise(async (resolve, reject) => {
+        try {
+          let itemUrls = await this.doUploadItems(qnBaseInfo, items)
+          qnBaseInfo.slides = itemUrls
+          // 获取初始化Blob需要的数据
+          let sasData = qnBaseInfo.scope === 'private' ? await this.$tools.getPrivateSas() :
+            await this.$tools.getSchoolSas();
+          //初始化Blob
+          let containerClient = new blobTool(
+            sasData.url,
+            sasData.name,
+            sasData.sas,
+            qnBaseInfo.scope
+          );
+          let file = new File([JSON.stringify(qnBaseInfo)], "index.json");
+          // 等待上传blob的返回结果
+          let blobFile = await containerClient.upload(file, { path: 'survey/' + qnBaseInfo.id });
+          if (blobFile.blob) {
+            delete qnBaseInfo.slides
+            resolve(blobFile.blob)
+          } else {
+            this.$Message.error(this.$t('evaluation.newExercise.uploadErrorTip'));
+          }
+        } catch (e) {
+          this.isLoading = false;
+          this.isBtnLoading = false;
+          reject(e);
+        }
+      })
+    },
+    /**
+     * 新增问卷或者编辑问卷
+     * @param data
+     */
+    saveorUpdataQn(data) {
+      return new Promise((r, j) => {
+        if (this.isAreaSurvey) {
+          this.$api.ability.saveAreaSurveys(data).then((res) => {
+            if (!res.error) {
+              r(res);
+            } else {
+              j(res.error);
+            }
+          });
+        } else {
+          this.$api.questionnaire.UpsertSurvey(data).then((res) => {
+            if (!res.error) {
+              r(res);
+            } else {
+              j(res.error);
+            }
+          });
+        }
+
+      });
+    },
+
+    /** 新增问卷成功回调 */
+    onAddSuccess() {
+      this.continueToken = null
+      this.handleTabClick(this.$route.name === "manageQuestionnaire" ? 0 : 1);
+    },
+
+    /* 获取问卷所有试题的选项内容 */
+    getSurveyAns(items) {
+      let result = []
+      items.forEach(item => {
+        result.push(item.option.map(i => i.code))
+      })
+      return result
+    },
+
+  },
+  watch: {
+  }
+}
+</script>
+<style lang="less" scoped>
+@import "./Resources.less";
+</style>
+<style lang="less">
+//.ivu-table.ivu-table-stripe .ivu-table-cell.demo-table-info-column
+</style>

+ 312 - 0
TEAMModelOS/ClientApp/src/view/mycourse/syllabus/DialogBox/Resources/A_Vote.vue

@@ -0,0 +1,312 @@
+<template>
+  <div class="Vote-container">
+    <Form ref="voteForm" :model="voteForm" label-position="top" :rules="ruleValidate" hide-required-mark>
+      <div style="display: flex;">
+        <div style="width: 48%;">
+          <FormItem :label="$t('vote.form.name')" prop="name">
+            <Input v-special-char  v-model="voteForm.name" :placeholder="$t('vote.form.namePlace')"></Input>
+          </FormItem>          
+          <FormItem :label="$t('vote.form.description')" prop="description">
+            <div ref="descriptionEditor" style="text-align:left;"></div>
+            <!-- <div v-html="voteForm.description" style="margin:10px;font-size:16px;font-weight:bold;word-break: break-all;"></div> -->
+          </FormItem>
+        </div>
+        <div style="width: 48%;padding-left: 2%;margin-left:2%;border-left: 1px dashed #D2D2D2;">
+          <FormItem :label="$t('vote.form.optionSetting')" prop="attachment" ref="optionsBox">
+            <div v-if="voteOptions.length">
+              <div v-for="(item,index) in voteOptions" :key="index" class="option-editor-wrap">
+                <div style="display: flex;">
+                  <span class="option-order">{{ index + 1 }}
+                    <Icon type="md-trash" @click="deleteOption(index)" />
+                  </span>
+                  <div :ref="'voteOption'+index" style="text-align:left" class="option-editor" @click="optionClick(index)"></div>
+                </div>                
+              </div>
+            </div>
+            <p style="float:right;color:#757575;cursor:pointer" @click="onAddOption" >
+              <Icon type="md-add" />{{ $t('vote.form.addOption') }}
+            </p>
+          </FormItem>
+          <FormItem :label="$t('vote.form.times')">
+            <Select v-model="voteForm.times" >
+              <Option value="once">{{ $t('vote.form.once') }}</Option>
+              <Option value="day">{{ $t('vote.form.day') }}</Option>
+              <Option value="week">{{ $t('vote.form.week') }}</Option>
+              <Option value="month">{{ $t('vote.form.month') }}</Option>
+              <Option value="year">{{ $t('vote.form.year') }}</Option>
+            </Select>
+          </FormItem>
+          <FormItem :label="$t('vote.form.selectNum')" prop="selectMax">
+            <!--<Input :class="!voteFormEdit ? 'vote-form-disabled':''" v-model="voteForm.count" placeholder="请输入投票名称"></Input>-->
+            <InputNumber :min="1" v-model="voteForm.selectMax"></InputNumber>
+          </FormItem>
+          <FormItem :label="$t('vote.form.other')" prop="secret">
+            <CheckboxGroup v-model="voteForm.secret">
+              <Checkbox label="secret">{{ $t('vote.form.openSecret') }}</Checkbox>
+            </CheckboxGroup>
+            <CheckboxGroup v-model="voteForm.repeat" v-if="voteForm.selectMax > 1">
+              <Checkbox label="repeat">{{ $t('vote.form.openRepeat') }}</Checkbox>
+            </CheckboxGroup>
+          </FormItem>
+        </div>
+      </div>
+    </Form>
+  </div>
+</template>
+<script>
+//import AddSyllabus from "./addSyllabus.vue"
+import E from 'wangeditor'
+export default {
+  components: {
+
+  },
+  props: { 
+  },
+  data(vm) {
+    return {
+      classNameArr: [],
+      descriptionEditor: null,
+      voteOptions: [...new Array(2).keys()], // 默认四个选项,
+      voteOptionsContent: [],
+      voteForm: {
+        name: '',
+        classes: [],
+        endTime: '',
+        publishModel: '0',
+        startTime: '',
+        rangeTime: null,
+        description: '',
+        isReset: [],
+        selectMax: 1,
+        secret: [],
+        repeat: [],
+        times: 'once',
+        voteNum: '1'
+      },
+      ruleValidate: {
+        name: [{
+          required: true,
+          message: vm.$t('vote.form.ruleName'),
+          trigger: 'blur'
+        }],
+        endTime: [{
+          required: true,
+          type: 'date',
+          message: this.$t('learnActivity.createEv.errTips9'),
+          trigger: 'change'
+        }]
+      },
+    }
+  },
+  created() {
+    
+  },
+  methods: {   
+    /* 渲染投票对象表单 */
+    async doRender(item) {
+      console.log('需要渲染的投票对象', item)
+      // await this.findResearchList()
+      // 如果是区级投票
+      if (this.isAreaVote) {
+        this.classNameArr = item.targets.map(i => {
+          return {
+            name: i
+          }
+        })
+      } else {
+        if (item.targetType === 'research') {
+          if (item.tchLists.includes('TeacherAll')) {
+            this.classNameArr = [{
+              name: this.$t('vote.allTeacher')
+            }]
+          } else {
+            let groupNameArr = []
+            item.tchLists.forEach(i => {
+              let findObj = this.groupList.find(j => j.id === i)
+              if (findObj) {
+                groupNameArr.push(findObj)
+              }
+            })
+            this.classNameArr = groupNameArr
+          }
+        } else {
+          this.classNameArr = item.classes.length ? await this.getClassNameByIds(item.classes) : item
+            .stuLists.length ? await this.getClassNameByIds(item.stuLists) : []
+        }
+      }
+
+      console.log(this.classNameArr);
+      this.voteForm = null
+      this.voteOptionsContent = []
+      this.voteOptions = []
+      this.initEditors()
+      this.voteForm = {
+        name: item.name,
+        code: item.code,
+        targets: item.targets,
+        classes: item.targetType === 'research' ? item.tchLists : item.classes.length ? item.classes : item.stuLists,
+        startTime: item.startTime ? new Date(item.startTime) : '',
+        endTime: item.endTime ? new Date(item.endTime) : '',
+        description: item.description,
+        secret: item.secret ? ['secret'] : [],
+        repeat: item.repeat ? ['repeat'] : [],
+        selectMax: item.voteNum || 1,
+        times: item.times || 'once',
+        voteNum: item.voteNum || 1,
+        isReset: []
+      }
+      console.error('render', this.voteForm)
+      this.isImmediate = item.isImmediate || false
+      this.descriptionEditor.txt.html(item.description)
+      this.voteOptionsContent = item.options
+      this.voteOptions = item.options.map((item, index) => index)
+
+      this.$nextTick(() => {
+        this.initEditors()
+        this.$refs.voteForm.validateField('name')
+        this.$refs.voteForm.validateField('classes')
+        this.$refs.voteForm.validateField('description')
+      })
+    },
+    /* 初始化编辑器 */
+    initEditors() {
+      console.log('进入编辑起初始化')
+      console.log(this.voteOptions)
+      console.log(this.voteOptionsContent)
+      this.$nextTick(() => {
+        // Editor默认配置
+        if (this.voteOptions.length > 0) {
+          this.voteOptions.forEach((item, i) => {
+            let that = this
+            let editor = new E(that.$refs['voteOption' + i][0])
+            this.$editorTools.initSimpleEditor(editor)
+            // 选项编辑器失焦隐藏工具栏
+            editor.config.onblur = function () {
+              let allToolbars = document.getElementsByClassName('option-editor')
+              for (let i = 0; i < allToolbars.length; i++) {
+                if (allToolbars[i].children.length) {
+                  allToolbars[i].children[0].style.visibility = 'hidden'
+                }
+              }
+            }
+
+            // 选项编辑器内容发生变化时
+            editor.config.onchange = (html) => {
+              let key = String.fromCharCode(64 + parseInt(i + 1))
+              let codeArr = this.voteOptionsContent.map(item => item.code)
+              // 如果已经编辑过则 修改选项内容
+              if (codeArr.indexOf(key) !== -1) {
+                this.voteOptionsContent[codeArr.indexOf(key)].value = html
+              } else { // 否则创建新选项
+                let option = {
+                  code: key,
+                  value: html
+                }
+                this.voteOptionsContent.push(option)
+              }
+            }
+            editor.create()
+
+            // 如果是编辑状态 则将选项内容回显
+            if (this.editInfo) {
+              editor.txt.html(this.voteOptionsContent[i].value)
+            }
+          })
+        }
+      })
+    },
+    /* 模拟选项聚焦事件 */
+    optionClick(index) {
+      console.log(index)
+      let allToolbars = document.getElementsByClassName('option-editor')
+      console.log(allToolbars)
+      let that = this
+      for (let i = 0; i < allToolbars.length; i++) {
+        allToolbars[i].children[0].style.visibility = 'hidden'
+      }
+      setTimeout(function () {
+        let currentToolBar = that.$refs['voteOption' + index][0].children[0]
+        if (currentToolBar.clientHeight > 50) {
+          currentToolBar.style.top = '-90px'
+        }
+        currentToolBar.style.visibility = 'visible'
+      }, 100)
+    },
+    /* 删除选项 */
+    deleteOption(index) {
+      this.voteOptions.splice(index, 1)
+      this.voteOptionsContent.splice(index, 1)
+      console.log(this.voteOptions)
+      console.log(this.voteOptionsContent)
+      this.$nextTick(() => {
+        this.initEditors()
+      })
+    },
+    /* 添加选项 */
+    onAddOption() {
+      let that = this
+      let newIndex = this.voteOptions.length
+      let optionsLength = this.voteOptions.length
+      console.log(this.voteOptionsContent)
+      if (optionsLength < 10) {
+        this.voteOptions.push(newIndex)
+        this.$nextTick(() => {
+          let editor = new E(that.$refs['voteOption' + newIndex][0])
+          this.$editorTools.initSimpleEditor(editor)
+          editor.config.onchange = (html) => {
+            let key = String.fromCharCode(64 + parseInt(newIndex + 1))
+            let codeArr = this.voteOptionsContent.map(item => item.code)
+            // 如果已经编辑过则 修改选项内容
+            if (codeArr.indexOf(key) !== -1) {
+              this.voteOptionsContent[codeArr.indexOf(key)].value = html
+            } else { // 否则创建新选项
+              let option = {
+                code: key,
+                value: html
+              }
+              this.voteOptionsContent.push(option)
+            }
+          }
+          editor.config.onblur = function () {
+            let allToolbars = document.getElementsByClassName('option-editor')
+            for (let i = 0; i < allToolbars.length; i++) {
+              if (allToolbars[i].children.length) {
+                allToolbars[i].children[0].style.visibility = 'hidden'
+              }
+            }
+          }
+          editor.create()
+        })
+      } else {
+        this.$Message.warning(this.$t('vote.form.optionNumsTip'))
+      }
+    },
+  },
+  watch: {
+  },
+  mounted() {
+    let descriptionEditor = new E(this.$refs.descriptionEditor)
+    descriptionEditor.config.onchange = (html) => {
+      this.voteForm.description = html
+    }
+    descriptionEditor.config.height = 200//重要
+    this.$editorTools.initSimpleEditor(descriptionEditor, this)
+    descriptionEditor.create()
+    this.descriptionEditor = descriptionEditor
+    
+    this.voteOptions = [...new Array(2).keys()]
+    this.initEditors()
+  },
+  computed: {
+    isAreaVote() {
+      return this.$route.name === 'manageAreaVote'
+    },
+  },
+}
+</script>
+<style lang="less" scoped>
+@import "./Resources.less";
+</style>
+<style lang="less">
+//.ivu-table.ivu-table-stripe .ivu-table-cell.demo-table-info-column
+</style>

+ 60 - 0
TEAMModelOS/ClientApp/src/view/mycourse/syllabus/DialogBox/Resources/C_CloudContent.vue

@@ -0,0 +1,60 @@
+<template>
+  <div class="CloudContent-container">
+    <div style="overflow:hidden;">
+      <NewChooseContent :showSyllabus="false" ref="chooseContentRef"
+        :showContent="true" :showQuestion="false" 
+        :showPaper="false" :showOther="false"
+        :singleChoose="false" style="position: relative; top:-40px;"
+        @on-select-question="getSelectedQuestion" @on-cancel-question="cancelQuestion" 
+        @on-file-change="getSelectFile"></NewChooseContent>      
+    </div>    
+  </div>
+</template>
+<script>
+//import AddSyllabus from "./addSyllabus.vue"
+import NewChooseContent from '@/components/selflearn/NewChooseContent.vue'
+export default {
+  components: {
+    NewChooseContent,
+  },
+  props: { 
+  },
+  data() {
+    return {      
+      learnUnit: {//放資源
+        name: '',
+        resource: [],
+        item: []
+      },
+    }
+  },
+  computed: {
+  },
+  created() {    
+  },
+  methods: {
+    cancelQuestion(data) {
+      console.log("cancelQuestion");
+      this.learnUnit.item = data.questions 
+    },
+    getSelectedQuestion(data) {
+      console.log("getSelectedQuestion");
+      this.learnUnit.item = data
+    },
+    getSelectFile(data) {
+      console.log("getSelectFile");
+      console.log("data.files="+data.files);
+      this.learnUnit.resource = data.files
+    },
+  },
+  watch: {
+    
+  }
+}
+</script>
+<style lang="less" scoped>
+@import "./Resources.less";
+</style>
+<style lang="less">
+//.ivu-table.ivu-table-stripe .ivu-table-cell.demo-table-info-column
+</style>

+ 78 - 0
TEAMModelOS/ClientApp/src/view/mycourse/syllabus/DialogBox/Resources/C_LocalFile.vue

@@ -0,0 +1,78 @@
+<template>
+  <div class="LocalFile-container">
+    <BaseUpload simpleUpload @uploadFinish="uploadFinish"></BaseUpload>
+    <div v-if="!attachmentsArr.length "
+      style="margin:-20px 10px 10px 10px;font-size:14px;font-weight:bold;">{{ $t('homework.noAttachments') }}</div>
+    <div v-if="attachmentsArr.length" class="attachments-list" style="margin:-20px 10px 10px 10px;">
+      <div class="attachments-item" v-for="(item,index) in attachmentsArr" :key="index">
+        <span class="name" >{{ item.name }}</span>
+        <span class="name">
+          <span style="margin-right: 10px;">{{ getSizeByBytes(item.size) }}</span>
+          <Icon type="md-close" color="#adadad" @click="removeAttachment(index)" />
+        </span>
+      </div>
+    </div>
+  </div>
+</template>
+<script>
+//程式碼參考:BaseHwForm.vue 如何存檔以及存到資料庫還尚未研究
+//import AddSyllabus from "./addSyllabus.vue"
+
+export default {
+  components: {
+
+  },
+  props: {
+  },
+  data() {
+    return {
+      attachmentsArr: [],
+      maxFileSize: 1024 * 1024 * 50,
+      previewFile: null,
+      previewStatus: false,
+    }
+  },
+  computed: {
+
+  },
+  created() {
+    
+  },
+  methods: {    
+    /* 上传本地文件成功回调 */
+    uploadFinish(file) {
+      console.log('file.name='+file.name);
+      console.log('file.type='+file.type);
+      console.log('file.size ='+file.size);
+      console.log('file.hash='+file.hash);
+      /*
+      if (this.attachmentsArr.length >= this.maxAttachmentCount) {
+        this.$Message.warning(this.$t('homework.countTip'))
+      } else*/
+      if (file.size > this.maxFileSize) {
+        this.$Message.warning(this.$t('homework.sizeTip'))
+      } else {
+        //file.name,file.type,file.hash
+        this.attachmentsArr.push(file)
+      }
+    },
+    /* 移除附件 */
+    removeAttachment(index) {
+      this.attachmentsArr.splice(index, 1)
+    },  
+    /* 根据字节数转换为大小 */
+    getSizeByBytes(bytes) {
+      return bytes / 1024 < 1024 ? (bytes / 1024).toFixed(1) + 'KB' : bytes / 1024 / 1024 < 1024 ? (bytes /
+        1024 / 1024).toFixed(1) + 'M' : (bytes / 1024 / 1024 / 1024).toFixed(1) + 'G'
+    },
+  },
+  watch: {
+  }
+}
+</script>
+<style lang="less" scoped>
+@import "./Resources.less";
+</style>
+<style lang="less">
+//.ivu-table.ivu-table-stripe .ivu-table-cell.demo-table-info-column
+</style>

+ 99 - 0
TEAMModelOS/ClientApp/src/view/mycourse/syllabus/DialogBox/Resources/C_URL.vue

@@ -0,0 +1,99 @@
+<template>
+  <div class="URL-container">
+    <div style="margin:20px 10px 10px 10px;">{{ $t('syllabus.linkName') }}</div>
+    <Input v-special-char v-model="curLink.name" style="width:calc(100% - 20px); margin:0px 20px 0px 10px;"/>
+    <div style="margin:10px;">{{ $t('syllabus.linkUrl') }}</div>
+    <Input v-model="curLink.url" placeholder="" style="width:calc(100% - 20px); margin:0px 20px 10px 10px;"/>
+  </div>
+</template>
+<script>
+//參考Syllabus.vue 新增超链接资源弹窗
+//import AddSyllabus from "./addSyllabus.vue"
+
+export default {
+  components: {
+
+  },
+  props: {
+  },
+  data() {
+    return {
+      curLink: {
+        name: '',
+        link: ''
+      },
+    }
+  },
+  computed: {
+  },
+  created() {
+
+  },
+  methods: {
+    /* 添加超链接 */
+    onAddLink() {//按下確定按鈕後執行的程式 
+      console.log(this.$refs.treeRef.curData)
+      let curLink = this.curLink.url
+      if (!/^(http:|https:)/i.test(curLink)) {
+        curLink = "http://" + curLink;
+      }
+      if (!this.isURL(curLink)) {
+        this.$Message.warning(this.$t('evaluation.repairResourse.formatErrorTip'))
+        return
+      }
+      this.$refs.treeRef.curData.rnodes.push({
+        code: this.curCode,
+        scope: this.curScope,
+        id: this.$tools.guid(),
+        link: curLink,
+        title: this.curLink.name,
+        type: 'link',
+        ctnr: '',
+        hash: this.$tools.getStringMd5(curLink)
+      })
+      this.$refs.treeRef.curData.creatorId = this.curTeammodelId
+      this.$refs.treeRef.curData.creatorName = this.curName
+      this.modifyIdArr.push(this.curChapter.data.id)
+      //   this.hasModify = true
+      this.isShowLinkModal = false
+    },
+    /* 判断是否为正确的链接地址 */
+    isURL(url) {
+      const strRegex = '^((https|http|ftp)://)?' //(https或http或ftp):// 可有可无
+        +
+        '(([\\w_!~*\'()\\.&=+$%-]+: )?[\\w_!~*\'()\\.&=+$%-]+@)?' //ftp的user@ 可有可无
+        +
+        '(([0-9]{1,3}\\.){3}[0-9]{1,3}' // IP形式的URL- 3位数字.3位数字.3位数字.3位数字
+        +
+        '|' // 允许IP和DOMAIN(域名)
+        +
+        '(localhost)|' //匹配localhost
+        +
+        '([\\w_!~*\'()-]+\\.)*' // 域名- 至少一个[英文或数字_!~*\'()-]加上.
+        +
+        '\\w+\\.' // 一级域名 -英文或数字 加上.
+        +
+        '[a-zA-Z]{1,6})' // 顶级域名- 1-6位英文
+        +
+        '(:[0-9]{1,5})?' // 端口- :80 ,1-5位数字
+        +
+        '((/?)|' // url无参数结尾 - 斜杆或这没有
+        +
+        '(/[\\w_!~*\'()\\.;?:@&=+$,%#-]+)+/?)$'; //请求参数结尾- 英文或数字和[]内的各种字符
+      const re = new RegExp(strRegex, 'i'); // 大小写不敏感
+      if (re.test(encodeURI(url))) {
+        return true;
+      }
+      return false;
+    },
+  },
+  watch: {
+  }
+}
+</script>
+<style lang="less" scoped>
+@import "./Resources.less";
+</style>
+<style lang="less">
+//.ivu-table.ivu-table-stripe .ivu-table-cell.demo-table-info-column
+</style>

+ 454 - 0
TEAMModelOS/ClientApp/src/view/mycourse/syllabus/DialogBox/Resources/Resources.less

@@ -0,0 +1,454 @@
+.div-center{//水平置中
+    display: flex; 
+    justify-content: center; 
+    text-align:center;
+    width:100%;
+}
+.div-centerY{//垂直置中
+    display: flex; 
+    align-items: center;  
+}
+.between{//分兩邊
+    display: flex;
+    justify-content: space-between;
+}
+.textBox{//對話框
+    width: 100%;
+    // color: #2d8cf0;
+    padding-left: 5px;
+    border: 1px solid #e8eaec;
+    border-radius: 5px;
+}
+.Evaluate-container{    
+    .evaluation-attr-wrap{
+        width: 40%;
+        margin-right: 10px;
+    }
+    .evaluation-question-wrap{
+        width: 60%;
+    }
+}
+.Homework-container{
+    .base-upload-container {
+        padding-bottom: 0px;
+    }
+}
+
+
+
+
+@main-bgColor: rgb(40, 40, 40);
+@borderColor: #424242;
+@primary-color: #70B1E7;
+@primary-textColor: #393939; //�ı�����ɫ
+@second-textColor: #636363; //�ı�������ɫ
+@primary-fontSize: 14px;
+@second-fontSize: 16px;
+
+/deep/ .ivu-tag {
+    border: none;
+    background: @primary-color;
+    height: 28px;
+    line-height: 28px;
+
+    i {
+        top: 8px;
+        color: #fff;
+    }
+
+    .ivu-tag-text {
+        color: #fff;
+    }
+}
+
+.Vote-container {
+    // padding: 0px 20px 0px 15px;
+    background-color: #fff;
+    
+    .image-viewer {
+        background-color: rgba(0, 0, 0, 0.8);
+        z-index: 9999;
+        width: 100%;
+        height: 100%;
+        position: fixed;
+        top: 0;
+        left: 0;
+        overflow-y: scroll;
+        overflow-x: hidden;
+        text-align: center;
+        /*display: flex;
+	    justify-content: center;
+	    align-items: center;*/
+        padding: 50px;
+        padding-top: 8%;
+
+        .close-icon {
+            position: absolute;
+            right: -16px;
+            top: -16px;
+            font-size: 24px;
+            color: black;
+            cursor: pointer;
+            border-radius: 50px;
+            background: white;
+            padding: 2px;
+        }
+    }
+    .ivu-form-item:not(:first-child) {
+        margin-top: 30px;
+    }
+
+    .ivu-form .btn-save {
+        width: 100%;
+        background: @primary-color;
+        border: none;
+        height: 38px;
+    }
+
+    .ivu-form .btn-reset {
+        width: 48%;
+        border: none;
+        height: 38px;
+        background-color: rgb(236, 236, 236);
+    }
+
+    .ivu-input::-webkit-input-placeholder {
+        color: #939393;
+    }
+
+    .ivu-input,
+    .ivu-select-single .ivu-select-selection,
+    .ivu-select-multiple .ivu-select-selection {
+        // border: 1px solid #c1c1c1;
+        margin-top: 10px;
+        height: 40px;
+        line-height: 40px;
+        padding-left: 8px;
+        /* color: @primary-textColor;
+		background-color: transparent; */
+    }
+
+    .ivu-input-number-input,
+    .ivu-input-number {
+        width: 100%;
+        padding-left: 10px;
+        height: 40px;
+        line-height: 40px;
+        color: @primary-textColor;
+
+        .ivu-input-number-handler-wrap {
+            background: none;
+            border: none;
+
+            .ivu-input-number-handler-down {
+                border-top: 1px solid #6f6f6f;
+            }
+
+            .ivu-input-number-handler {
+                height: 20px;
+
+                span {
+                    height: 20px;
+                    line-height: 20px;
+                }
+            }
+        }
+    }
+
+    .ivu-select-multiple .ivu-select-selection {
+        height: auto;
+    }
+
+    .ivu-tag {
+        border: none;
+        background: @primary-color;
+        height: 28px;
+        line-height: 28px;
+
+        i {
+            top: 8px;
+            color: #fff;
+        }
+
+        .ivu-tag-text {
+            color: #fff;
+        }
+    }
+
+    .ivu-select {
+        color: @second-textColor;
+    }
+
+    .ivu-input-prefix i,
+    .ivu-input-suffix i {
+        line-height: 60px;
+    }
+
+    .ivu-select-single .ivu-select-selection .ivu-select-placeholder,
+    .ivu-select-multiple .ivu-select-selection .ivu-select-placeholder,
+    .ivu-select-single .ivu-select-selection .ivu-select-selected-value {
+        height: 40px;
+        line-height: 40px;
+    }
+
+    .ivu-date-picker {
+        width: 100%;
+        margin-top: -20px;
+    }
+
+    .ivu-radio-wrapper,
+    .ivu-checkbox-wrapper {
+        color: @second-textColor;
+    }
+
+    .ivu-radio-wrapper:not(:first-child),
+    .ivu-checkbox-wrapper:not(:first-child) {
+        margin-left: 15px;
+    }
+
+    .ivu-radio-inner:after {
+        background: @primary-color;
+    }
+
+    .ivu-upload-drag {
+        margin-top: 10px;
+        border: 1px dashed #6c6e71;
+        color: @second-textColor;
+
+        .ivu-icon {
+            color: @primary-color;
+            font-weight: bold;
+        }
+    }
+
+    .ivu-checkbox-checked .ivu-checkbox-inner {
+        border-color: @primary-color;
+        background: @primary-color;
+    }
+
+    .ivu-upload-list-file,
+    .ivu-upload-list-file>span i {
+        color: @second-textColor;
+        margin-right: 5px;
+    }
+
+    .ivu-upload-list-file:hover {
+        span {
+            color: @second-textColor;
+        }
+    }
+
+    .ivu-upload-list-remove {
+        margin-top: 5px;
+        font-size: 22px;
+    }
+
+    .ivu-select-single .ivu-select-selection .ivu-select-placeholder {
+        color: #939393;
+    }
+
+    .w-e-toolbar {
+        margin-top: 10px;
+        z-index: 1 !important;
+    }
+
+    .w-e-text-container {
+        z-index: 0 !important;
+    }
+
+    .option-editor-wrap {
+        position: relative;
+        margin-top: 10px;
+
+        .option-order {
+            position: relative;
+            display: inline-flex;
+            justify-content: center;
+            align-items: center;
+            min-height: 42px;
+            width: 10%;
+            float: left;
+            // color: #a0a0a0;
+            background: var(--input-bg-color);
+
+            &:hover {
+                .ivu-icon {
+                    display: unset;
+                }
+            }
+
+            .ivu-icon {
+                position: absolute;
+                top: 50%;
+                left: 50%;
+                transform: translate(-50%, -50%);
+                font-size: 20px;
+                display: none;
+                cursor: pointer;
+            }
+        }
+
+        .option-editor {
+            position: relative;
+            display: inline-block;
+            // height: 42px;
+            // border: none;
+            width: 90%;
+            border:1px solid rgba(204, 204, 204, 0.49);
+        }
+
+        .w-e-text-container {
+            height: auto !important;
+            z-index: 0 !important;
+        }
+
+        .w-e-toolbar {
+            position: absolute;
+            top: -50px;
+            left: 0px;
+            visibility: hidden;
+            font-size: 12px;
+            width: 100%;
+        }
+    }
+
+    .vote-form-disabled {
+
+        .ivu-input,
+        .ivu-select-single .ivu-select-selection,
+        .ivu-select-selection,
+        .ivu-date-picker-focused input {
+            background: transparent;
+            border: none;
+            font-size: @second-fontSize;
+            font-weight: bold;
+            box-shadow: none;
+        }
+
+        .ivu-icon {
+            display: none !important;
+        }
+
+        .ivu-select-multiple .ivu-tag span:not(.ivu-select-max-tag) {
+            margin-right: 10px;
+        }
+
+
+    }
+
+    .ivu-input-number-disabled {
+        background-color: transparent !important;
+        border: none !important;
+
+        .ivu-input-number-input {
+            background-color: transparent !important;
+        }
+    }
+}
+
+.Questionnaire-container{
+    .body{
+        width:100%;
+        .left{
+            float:left;
+            width: 50%;
+            padding: 10px;
+        }
+        .right{
+            float:right;
+            width: 50%;
+            overflow-y: auto;
+            
+            .contect{
+                float: unset;
+                height: 470px;
+                padding-top: 10px;
+                overflow-y: auto;
+                .component-questionnaire{
+                    min-height: 30vh;
+                    padding-bottom:0px;
+                    height:unset;
+                }
+            }
+            
+        }
+    }
+    .qn-col {
+		height: 100%;
+		overflow: hidden;
+
+		.qn-box-header {
+			height: 45px;
+			line-height: 45px;
+			padding: 0 15px;
+			// font-weight: bold;
+			// border-bottom: 1px solid @borderColor;
+			display: flex;
+			justify-content: space-between;
+
+			&-tab {
+				margin-right: 25px;
+				cursor: pointer;
+				color: #909090;
+			}
+
+			&-tab-active {
+				border-bottom: 2px solid var(--tabs-bottom-color);
+				padding-bottom: 5px;
+				color: var(--tabs-text-color);
+				font-weight: bold;
+			}
+
+			.qn-box-header-tools {
+				display: flex;
+
+				&-tool {
+					cursor: pointer;
+					font-weight: 500;
+					color: #40A8F0;
+
+					.ivu-btn:before {
+						background-color: none;
+					}
+				}
+
+				.ivu-icon {
+					font-size: 16px;
+					margin-right: 5px;
+					vertical-align: sub;
+				}
+
+				.ivu-dropdown-rel {
+					display: flex;
+					align-items: center;
+
+					.ivu-btn {
+						padding: 0;
+					}
+				}
+
+				.ivu-btn {
+					background: transparent;
+					border: none;
+					padding: 0 10px;
+					color: #40A8F0;
+
+					&::before {
+						background-color: none;
+					}
+
+					&:focus {
+						border: none;
+					}
+
+					.ivu-icon {
+						font-size: 16px;
+					}
+				}
+
+			}
+		}
+	}
+    
+
+}

+ 192 - 0
TEAMModelOS/ClientApp/src/view/mycourse/syllabus/DialogBox/Resources/part/BaseQnForm.vue

@@ -0,0 +1,192 @@
+<template>
+  <div class="Questionnaire-container">
+    <Loading :top="200" bgColor="rgba(103, 103, 103, 0.27)" type="1" v-show="isLoading"></Loading>
+    <div class="">
+      <Form ref="qnForm" :model="qnForm" label-position="top" :rules="ruleValidate">
+        <FormItem :label="$t('survey.form.name')" prop="name">
+          <Input v-special-char class="qn-form-disabled" v-model="qnForm.name"
+            :placeholder="$t('survey.form.namePlace')"></Input>
+        </FormItem>
+        <FormItem :label="$t('vote.form.other')" prop="publishModel">
+          <Checkbox v-model="allowModify" style="margin:0 5px"> {{ $t('survey.questionaire.allowModify') }}</Checkbox>
+        </FormItem>
+        <FormItem :label="$t('survey.form.description')" prop="description">
+          <div ref="descriptionEditor" style="text-align:left"></div>
+        </FormItem>
+      </Form>
+    </div>
+  </div>
+</template>
+<script>
+import E from 'wangeditor'
+export default {
+  components: {
+  },
+  props: {
+  },
+  data(vm) {
+    return {
+      allowModify: false,
+      descriptionEditor: null,
+      qnForm: {
+        name: '',
+        classes: [],
+        publishModel: 0,
+        rangeTime: [],
+        description: '',
+        isReset: [],
+        other: []
+      },
+      ruleValidate: {
+        name: [{
+          required: true,
+          message: vm.$t('survey.form.ruleName'),
+          trigger: 'blur'
+        }],
+      },
+      isLoading: false,
+      isBtnLoading: false,
+    }
+  },
+  mounted() {
+    /** 初始化问卷详情的富文本编辑器 */
+    let descriptionEditor = new E(this.$refs.descriptionEditor)
+    descriptionEditor.config.onchange = (html) => {
+      this.qnForm.description = html
+    }
+    descriptionEditor.config.height = 200//重要
+    this.$editorTools.initSimpleEditor(descriptionEditor, this)
+    descriptionEditor.create()
+    this.descriptionEditor = descriptionEditor
+  },
+  computed: {
+    isAreaSurvey() {
+      return this.$route.name === 'manageAreaQuestionnaire'
+    },
+  },
+  created() {
+
+  },
+  methods: {
+    
+    /**
+     * 提交新增问卷表单
+     * @param name FormName
+     */
+    handleSubmit(name) {
+      return new Promise((resolve, reject) => {
+        this.$refs[name].validate((valid) => {
+          // let hasTargets = (!this.isAreaSurvey && this.qnForm.classes.length) || (this.isAreaSurvey && this.schoolTarget.length)
+          let hasTargets = this.$refs.classSelectRef.targetType === 'research' ? this.$refs.classSelectRef.groupArr.length : this.$refs.classSelectRef.defaultArr.length
+          if (valid && this.getSimpleText(this.qnForm.description) && hasTargets) {
+            let params = JSON.parse(JSON.stringify(this.defaultParams))
+            let areaParams = null
+            let target = []
+            let classSelectScope = this.isAreaSurvey ? 'school' : this.$refs.classSelectRef.evaluationInfo.scope
+            let isSchoolClass = this.isAreaSurvey ? false : this.$refs.classSelectRef.evaluationInfo.scope ===
+              'school' && this.$refs.classSelectRef.privateClassType === 'school'
+            let isPersonal = this.$route.name === 'personalSurvey' && classSelectScope ===
+              'private'
+            let isSchool = this.$route.name === 'manageQuestionnaire'
+            params.code = this.getCurCode
+            // 如果个人问卷的班级是校本班级 那么也要把scope置为school
+            params.scope = isPersonal ? 'private' : 'school'
+            params.name = this.qnForm.name
+            params.description = this.qnForm.description
+            params.targets = this.qnForm.targets
+            params.isSub = this.allowModify ? 1 : 0
+            // 新增参数
+            params.owner = this.$route.name === 'personalSurvey' ? 'teacher' : 'school'
+            params.creatorId = this.$store.state.userInfo.TEAMModelId
+            params.school = params.scope === 'school' ? this.$store.state.userInfo
+              .schoolCode : null
+
+            params.tchLists = []
+            params.classes = []
+            params.stuLists = []
+            // 如果是编辑状态 则直接复制ID 如果是新增 则直接赋值新ID
+            if (this.isEdit && this.editInfo.id && this.editInfo.code) {
+              params.id = this.editInfo.id
+              params.createTime = this.editInfo.createTime
+            } else {
+              params.id = this.$tools.guid()
+            }
+
+            // 如果是发布的区级投票 则需要修改保存参数
+            if (this.isAreaSurvey) {
+              let schArr = [...new Set(this.schoolTarget.map(i => i[0]))]
+              let para = []
+              schArr.forEach((i, index) => {
+                para.push({
+                  sId: i,
+                  sName: this.schList.find(j => j.id === i).sname,
+                  gName: [],
+                  gId: []
+                })
+                // 如果是区级活动 需要拼接学校加教研组信息
+                this.schoolTarget.forEach(j => {
+                  if (j[0] === i) {
+                    let groupName = j[1].split('-')[1]
+                    if (groupName === '所有老师(未分组)') {
+                      para[index].gName = []
+                      para[index].gId = ['default']
+                    } else {
+                      let curSch = this.schList.find(sch => sch.id === i)
+                      let groupId = curSch.gId[curSch.name.indexOf(groupName)]
+                      para[index].gName.push(groupName)
+                      para[index].gId.push(groupId)
+                    }
+                  }
+                })
+              })
+              delete this.qnForm.classes
+              delete this.qnForm.targets
+              delete this.qnForm.teachers
+              delete this.qnForm.stuLists
+              areaParams = {
+                "id": this.areaId,
+                "para": para,
+                "survey": params
+              }
+            } else {
+              // 如果是校本投票 则需要进一步确定发布对象是教研组还是学生名单
+              if (isSchool && this.$refs.classSelectRef.targetType === 'research') {
+                params.tchLists = this.qnForm.classes
+                params.targetType = 'research'
+              } else if (isSchoolClass) {
+                params.classes = this.qnForm.classes
+                params.targetType = 'student'
+              } else {
+                params.stuLists = this.qnForm.classes
+                params.targetType = 'student'
+              }
+            }
+            let finalParams = this.isAreaSurvey ? areaParams : params
+            console.log(finalParams)
+            resolve(finalParams)
+          } else {
+            this.$emit('onAddFail')
+            if (!hasTargets) {
+              this.$Message.error(this.$t('survey.form.ruleClasses'))
+            } else if (!this.getSimpleText(this.qnForm.description)) {
+              this.$Message.error(this.$t('survey.form.ruleDescription'))
+            } else {
+              this.$Message.error(this.$t('survey.form.noCompleteTip'))
+            }
+            reject(500)
+          }
+        })
+      })
+    },
+    
+  },
+  watch: {
+  }
+}
+</script>
+<style lang="less" scoped>
+@import "../Resources.less";
+</style>
+<style lang="less">
+//.ivu-table.ivu-table-stripe .ivu-table-cell.demo-table-info-column
+</style>

+ 52 - 0
TEAMModelOS/ClientApp/src/view/mycourse/syllabus/DialogBox/SyllabusAdd.vue

@@ -0,0 +1,52 @@
+<template>
+  <div class="SyllabusAdd-container">
+    <Modal v-model="DialogBoxAddBtn" title="新增課綱" @on-ok="ok" @on-cancel="cancel"
+      :width="750">
+      <AddSyllabus ref="addSyllabus" class="RadioGroup" :addBtnCtl="false"></AddSyllabus>
+      <span class="gray-font mleft20">※貼代碼與選擇課綱所新增的課綱『不同步』於來源課綱,若要同步,請在『來源課綱』設定同步對象!</span>
+    </Modal>    
+  </div>
+</template>
+<script>
+//import AddSyllabus from "./addSyllabus.vue"
+import AddSyllabus from "../addSyllabus.vue"
+export default {
+  components: {
+    AddSyllabus,
+  },
+  props: {
+    AddCtl: Boolean,    
+  },
+  data() {
+    return {
+      DialogBoxAddBtn:false,
+    }
+  },
+  computed: {
+  },
+  created() {
+    
+  },
+  methods: {
+    ok() {
+      // this.$Message.info('Clicked ok');
+      this.$refs.addSyllabus.addSyllabusFun();
+      this.$emit('AddClose',false);
+    },
+    cancel() {
+      this.$emit('AddClose',false);
+    },
+  },
+  watch: {
+    AddCtl(newVal) {
+      this.DialogBoxAddBtn= newVal;
+    }
+  }
+}
+</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>

+ 103 - 0
TEAMModelOS/ClientApp/src/view/mycourse/syllabus/DialogBox/SyllabusSorting.vue

@@ -0,0 +1,103 @@
+<template>
+  <div class="SyllabusSorting-container">
+    <Modal v-model="SortingBtn" title="課綱排序" @on-ok="ok" @on-cancel="cancel"
+      class="SyllabusSorting-div">
+      ※拖曳可排序
+      <div class="SortingDiv">
+        <div v-for="(item,index) in syllabusList" :key="item.id" class="itemCard"
+          draggable="true"
+          @dragstart="dragStart($event, index)" @dragover="allowDrop" 
+          @drop="drop($event, index)" @dragend="dragEnd">
+          <Icon v-if="item.type ==='share'" type="md-people" size="25" />
+          <Icon v-if="item.type ==='link'" type="ios-link" size="25" />
+          {{ item.name }}
+        </div>
+      </div>      
+    </Modal>
+  </div>
+</template>
+<script>
+//import AddSyllabus from "./addSyllabus.vue"
+
+export default {
+  components: {
+
+  },
+  props: {
+    SortingCtl: Boolean,    
+  },
+  data() {
+    return {
+      SortingBtn:false,
+      syllabusList: [//share同步他人,link被同步
+        {id: 'a1',sort:1,name: '數學一年級上學期課綱(1)',type:'share'},
+        {id: 'b2',sort:2,name: '數學二年級上學期課綱(2)',type:'link'},
+        {id: 'c3',sort:3,name: '數學三年級上學期課綱(3)',type:''},
+        {id: 'a4',sort:4,name: '數學一年級上學期課綱(4)',type:'share'},
+        {id: 'b5',sort:5,name: '數學二年級上學期課綱(5)',type:'link'},
+        {id: 'c6',sort:6,name: '數學三年級上學期課綱(6)',type:''},
+        {id: 'a7',sort:7,name: '數學一年級上學期課綱(7)',type:'share'},
+        {id: 'b8',sort:8,name: '數學二年級上學期課綱(8)',type:'link'},
+        {id: 'c9',sort:9,name: '數學三年級上學期課綱(9)',type:''},
+        {id: 'a10',sort:10,name: '數學一年級上學期課綱(10)',type:'share'},
+        {id: 'b20',sort:11,name: '數學二年級上學期課綱(11)',type:'link'},
+        {id: 'c30',sort:12,name: '數學三年級上學期課綱(12)',type:''},
+      ],
+    }
+  },
+  computed: {
+  },
+  created() {
+    
+  },
+  methods: {
+    ok() {
+      // this.$Message.info('Clicked ok');
+      this.$emit('SortingClose',false);
+    },
+    cancel() {
+      this.$emit('SortingClose',false);
+    },
+    //拖曳相關--str---↓↓--
+    allowDrop(e) {//取消默認行為
+      e.preventDefault();
+    },
+    dragStart(e, index) {//拖曳開始
+      let tar = e.target;
+      e.dataTransfer.setData('Text', index);
+      if (tar.tagName.toLowerCase() == 'li') {
+        // console.log('drag start')
+        // console.log('drag Index: ' + index)
+      }
+    },
+    drop(e, index) {//放置
+      this.allowDrop(e);
+      let arr = this.syllabusList.concat([]),
+      dragIndex = e.dataTransfer.getData('Text');
+      let temp = arr.splice(dragIndex, 1);  
+      arr.splice(index, 0, temp[0]);
+      this.syllabusList = arr;
+
+      //↓改正syllabusList資料的sort
+      for (let i = 0; i < this.syllabusList.length; i++) {
+        this.syllabusList[i].sort = i+1;
+      }   
+    },
+    dragEnd() {//放置結束
+      console.log("dragEnd");
+    },
+    //拖曳相關--end---↑↑--
+  },
+  watch: {
+    SortingCtl(newVal) {
+      this.SortingBtn= newVal;
+    }
+  }
+}
+</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>

+ 90 - 0
TEAMModelOS/ClientApp/src/view/mycourse/syllabus/DialogBox/SyncClass.vue

@@ -0,0 +1,90 @@
+<template>
+  <div class="SyncClass-container">
+    <Modal v-model="SyncClassBtn" title="同步對象" @on-ok="ok" @on-cancel="cancel">
+      <div class="showClass-div">
+        本班:
+        <span  class="showClass">
+          七年四班
+        </span>
+      </div>
+      <Transfer :data="data" :target-keys="targetKeys" 
+        @on-change="handleChange"></Transfer>
+    </Modal>
+  </div>
+</template>
+<script>
+//import AddSyllabus from "./addSyllabus.vue"
+
+export default {
+  components: {
+
+  },
+  props: {
+    SyncClassCtl: Boolean,    
+  },
+  data() {
+    return {
+      SyncClassBtn:false,
+      data: [//本班不在選項內
+        { key: 1, label: "一年二班"},
+        { key: 2, label: "9A"},
+        { key: 3, label: "2021級2班"},
+        { key: 4, label: "七年四班"},
+        { key: 5, label: "七年一班"},
+        { key: 6, label: "七年七班"},
+      ],
+      targetKeys: [1, 3,4],
+      targetKeysTemp:[1, 3,4],//儲存未存檔的內容
+    }
+  },
+  computed: {
+  },
+  created() {
+    
+  },
+  methods: {
+    ok() {
+      // this.$Message.info('Clicked ok');
+      this.$emit('SyncClassClose',false);
+    },
+    cancel() {
+      this.targetKeys = this.targetKeysTemp;
+      this.$emit('SyncClassClose',false);
+    },
+    handleChange(newTargetKeys, direction, moveKeys) {
+      if(direction == 'left'){
+        this.$Modal.confirm({
+            title: '你確定要取消同步嗎?',
+            content: '<p>一旦取消同步,雖然分享的班級課綱會保留,但保留的課綱將無法重新與本課綱同步。</p>',
+            onOk: () => {
+                this.$Message.info('Clicked ok');
+                this.changeToTemp(newTargetKeys);
+            },
+            onCancel: () => {
+                this.$Message.info('Clicked cancel');
+            }
+        });
+      }
+      console.log(newTargetKeys);
+      console.log(direction);
+      console.log(moveKeys);
+      
+    },
+    changeToTemp(newTargetKeys){
+      this.targetKeysTemp = this.targetKeys;
+      this.targetKeys = newTargetKeys;
+    }
+  },
+  watch: {
+    SyncClassCtl(newVal) {
+      this.SyncClassBtn= newVal;
+    }
+  }
+}
+</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>

+ 14 - 0
TEAMModelOS/ClientApp/src/view/mycourse/syllabus/SyllabusIndex.less

@@ -0,0 +1,14 @@
+.stu-action-wrap{
+    position: absolute;
+    right: 20px;
+    top: 7px;
+}
+
+.changePage-body{
+    margin: 40px;
+    .title{
+        font-size: 20px;
+        font-weight:bold;
+        margin-bottom: 10px;
+    }
+}

+ 137 - 0
TEAMModelOS/ClientApp/src/view/mycourse/syllabus/SyllabusIndex.vue

@@ -0,0 +1,137 @@
+<template>
+  <div class="SyllabusIndex-container">
+    <div v-if="changePage == 'mainPage'" class="stu-action-wrap common-save-btn"><!--右上角:更多功能下拉選單-->
+      <Dropdown style="margin-top:5px;margin-right:8px">
+        <a href="javascript:void(0)">
+          {{$t('cusMgt.moreFn')}}
+          <Icon type="ios-arrow-down"></Icon>
+        </a>
+        <DropdownMenu slot="list">
+          <DropdownItem @click.native="SortingCtl=true">
+            <span class="action-item">
+              <Icon type="md-settings" />
+              {{$t('cusMgt.cusTab14')}}
+            </span>
+          </DropdownItem>
+          <DropdownItem @click.native="AddCtl=true">
+            <span class="action-item">
+              <Icon type="md-add" />
+              {{$t('cusMgt.cusTab15')}}
+            </span>
+          </DropdownItem>
+        </DropdownMenu>
+      </Dropdown>
+    </div>
+    <div class="changePage-body" v-if="changePage == 'AddSyllabus'">
+      <div class="title">新增課綱</div>
+      <AddSyllabus  class="RadioGroup" :addBtnCtl="true"></AddSyllabus>
+    </div>
+    <div v-else-if="changePage == 'mainPage'" style="display: flex; ">
+      <SyllabusMenu :gradeParams="gradeParams" :grouplistId="grouplistId" :listType="listType" @PageData="PageDatafun"
+        :PageData="PageData"></SyllabusMenu>
+      <SyllabusResource v-if="PageData.mode==='Resource' || PageData.level === 'Title'" 
+        :PageData="PageData" :getResourceImage="getResourceImage">
+      </SyllabusResource>
+      <SyllabusSchedule v-else-if="PageData.mode==='Schedule' && PageData.level !== 'Title'" 
+        :PageData="PageData" :getResourceImage="getResourceImage">
+      </SyllabusSchedule>      
+    </div>
+    <SyllabusSorting :SortingCtl="SortingCtl" @SortingClose="SortingCtl=false"></SyllabusSorting>
+    <SyllabusAdd :AddCtl="AddCtl" @AddClose="AddCtl=false"></SyllabusAdd>
+  </div>
+</template>
+<script>
+import AddSyllabus from "./addSyllabus.vue"
+import SyllabusMenu from "./SyllabusMenu.vue"
+import SyllabusResource from "./SyllabusResource.vue"
+import SyllabusSchedule from "./SyllabusSchedule.vue"
+import SyllabusSorting from "./DialogBox/SyllabusSorting.vue"
+import SyllabusAdd from "./DialogBox/SyllabusAdd.vue"
+export default {
+  components: {
+    AddSyllabus, SyllabusMenu, SyllabusResource, SyllabusSchedule,SyllabusSorting,SyllabusAdd
+  },
+  props: {
+    //收藏id,双向绑定
+    value: {
+      default: () => {
+        return []
+      },
+    },
+    gradeParams: {
+      type: Object,
+      default: () => {
+        return []
+      }
+    },
+    grouplistId: {
+      type: Object,
+      default: () => {
+        return {}
+      }
+    },
+    listType: {
+      type: String,
+      default: () => {
+        return {}
+      }
+    },
+  },
+  data() {
+    return {
+      changePage: "mainPage",
+      //PageData:{mode:Resource資源/Schedule進度,level:Title標題,Chapter章節,Section資源卡片
+      PageData: { id: 1, mode: 'Resource', level: 'Title', 
+        data: { Title: '數學一年級上學期課綱(1)',Chapter:'',Section:'',type:'' } 
+      },
+      SortingCtl:false,//排序跳窗
+      AddCtl:false,//新增跳窗
+    }
+  },
+  computed: {
+  },
+  created() {
+  },
+  methods: {
+    PageDatafun(newValue) {
+      this.PageData = newValue;
+    },
+    getResourceImage(type) {// 將資源類型對應到圖像文件
+      const imageMap = {
+        image: require("../../../assets/source/image.png"),
+        doc: require("../../../assets/source/word.png"),
+        excel: require("../../../assets/source/excel.png"),
+        ppt: require("../../../assets/source/ppt.png"),
+        pdf: require("../../../assets/source/pdf.png"),
+        video: require("../../../assets/source/video.png"),
+        audio: require("../../../assets/source/audio.png"),
+        item: require("../../../assets/source/item.png"),
+        paper: require("../../../assets/source/folder.png"),
+        link: require("../../../assets/source/link.png"),
+        zip: require("../../../assets/source/zip.png"),
+        other: require("../../../assets/source/unknow.png"),
+      };    
+      return imageMap[type];
+    },
+  },
+  watch: {
+    grouplistId: {
+      deep: true,
+      immediate: true,
+      handler(n, o) {
+        this.tableInfo = {};//AddSyllabus、mainPage
+        this.changePage = "mainPage";//若在第二頁去,只要重新點選課程,一律跳回第一頁
+      }
+    }
+  }
+}
+</script>
+<style lang="less" scoped>
+@import "./SyllabusIndex.less";
+.SyllabusIndex-container {
+  height: 100%;
+}
+</style>
+<style lang="less">
+//.ivu-table.ivu-table-stripe .ivu-table-cell.demo-table-info-column
+</style>

+ 90 - 0
TEAMModelOS/ClientApp/src/view/mycourse/syllabus/SyllabusMenu.less

@@ -0,0 +1,90 @@
+.div-center{//水平置中
+    display: flex; 
+    justify-content: center; 
+    text-align:center;
+    width:100%;
+}
+.div-centerY{//垂直置中
+    display: flex; 
+    align-items: center;  
+}
+.pointer{//使其滑鼠滑入有小手可點
+    cursor: pointer;
+}
+.SyllabusBody-container{
+    background-color: #f2f2f2;
+    width: 300px;
+    height: calc(100vh - 140px); /* 將高度設置為視窗高度的 80% */
+    margin-top: 0px;
+    padding: 15px 10px 50px 10px;
+    overflow: auto;
+    
+    .title{
+        display: flex; 
+        align-items: center; 
+        // justify-content: space-between;
+
+        /deep/.ivu-select-selection {
+            border: none;
+            border-bottom: 1px solid transparent; /* 可以保留一個底線,可視情況調整顏色 */
+            border-radius: 0; /* 可以移除底線下方的角落 */
+            background-color: transparent; /* 設定背景色為透明 */      
+            font-weight: bold;
+        }
+        /deep/.ivu-select-selected-value {
+            font-size: 16px ;  
+        }
+    }
+    .modelctl{
+        display: flex;
+        justify-content: space-between;
+        .switch-location {
+            margin-right: auto; /* 使靠右方 */
+            margin-top: auto; /* 使自動靠下 */
+            display: flex;
+            align-items: center; //垂直置中
+            .txt{
+                font-size: 10px;
+            }
+        }
+        .poptip{
+            text-align: center;
+        }
+    }
+    .group{
+        cursor: pointer;
+        background-color: #dcdee2;
+        padding:2px 10px;
+        margin: 5px 2px;
+        display: flex;
+        justify-content: space-between;
+        align-items: center; //垂直置中
+
+        .icon1{            
+            margin-right: 5px;
+        }
+    }
+    .card{
+        cursor: pointer;
+        background-color: white;
+        width: 95%;
+        margin: 7px 2.5%;
+        min-height: 50px;
+        border-radius: 10px;
+        padding: 10px;
+        font-weight: bold; 
+        display: flex;
+        justify-content: space-between;
+    }
+    .card:hover,.addCard:hover{
+        border: 2px solid #2b85e4;
+    }    
+    .addCard{
+        cursor: pointer;
+        background-color: white;
+        width: 100%;
+        margin-top: 15px;
+        border-radius: 10px;
+        padding: 5px;
+    }
+}

+ 203 - 0
TEAMModelOS/ClientApp/src/view/mycourse/syllabus/SyllabusMenu.vue

@@ -0,0 +1,203 @@
+<template>
+  <div class="SyllabusBody-container">
+    <div class="title" @click="cardClick(chooseOne.id,'Title')">
+      <Icon v-if="chooseOne.type === 'share'" type="md-people" size="25" />
+      <Icon v-if="chooseOne.type === 'link'" type="ios-link" size="25" />
+      <!-- <Icon v-else type="" size="25" /> -->
+      <Select v-model="chooseOne.id" style="width:250px;" @on-change="selectSyllabus">
+        <Option v-for="item in syllabusList" :value="item.id" :key="item.id">
+          <Icon v-if="item.type ==='share'" type="md-people" size="25" />
+          <Icon v-if="item.type ==='link'" type="ios-link" size="25" />
+          {{ item.name }}
+        </Option>
+      </Select>
+    </div>
+    <div class="modelctl">
+      <div class="switch-location">
+        <span class="txt">資源/進度</span>
+        <i-Switch v-model="modeValue" size="small" @on-change="switchFun" />
+      </div>
+      <div class="poptip">
+        <Poptip title="課綱代碼" content="content" placement="bottom" v-model="poptipShow">
+          <Icon type="md-share" size="20" class="pointer" />
+          <template #content>
+            B148942356 
+            <Button type="primary" @click="poptipFun">複製</Button>
+          </template>
+        </Poptip>
+      </div>
+    </div>
+    <div v-for="(list,listIndex) in cardData" :key="list.id"><!--↓章節+資源卡片↓-->
+      <div class="group" @click="cardClick(list.id,'Chapter',list.type,list.name)" draggable="true"
+        @dragstart="dragStart($event, listIndex, 'Chapter')" @dragover="allowDrop" @drop="ChapterDrop($event, listIndex)"
+        @dragend="dragEnd"><!--↓章節↓-->
+        {{list.name}}
+        <div>
+          <Icon v-if="!modeValue && list.type==='order'" type="md-git-pull-request" size="20" class="icon1" />
+          <Icon v-if="list.btn" type="md-arrow-dropdown" size="20" @click.stop="list.btn=false" />
+          <Icon v-else type="md-arrow-dropup" size="20" @click.stop="list.btn=true" />
+        </div>
+      </div>
+      <div v-if="list.btn"><!--↓資源卡片↓-->
+        <div class="card div-centerY" v-for="(Resource,resourceIndex) in list.item" :key="Resource.id"
+          @click="cardClick(Resource.id,'Section',Resource.type,list.name,Resource.name)" draggable="true"          
+          @dragstart="dragStart($event, resourceIndex, 'Section', listIndex)" 
+          @dragover="allowDrop" @drop="SectionDrop($event, resourceIndex, 'Section', listIndex)"
+          @dragend="dragEnd" >
+          {{Resource.name}}
+          <div>
+            <div v-if="modeValue">100%</div>
+            <Icon v-else-if="!modeValue && Resource.type==='order'" type="md-git-pull-request" size="20"
+              class="icon1" />
+          </div>
+        </div>
+      </div>
+    </div>
+    <div v-if="chooseOne.type !== 'link'" class="addCard div-centerY div-center" @click="addCradBtn=true">
+      <Icon type="md-add-circle" :size="25" />
+    </div>
+    <AddCard :DialogBoxCtl="addCradBtn" :chooseOne="chooseOne" :cardData="cardData" @DialogBoxClose="addCradBtn=false">
+    </AddCard>
+  </div>
+</template>
+<script>
+import AddCard from "./DialogBox/AddCard.vue"
+export default {
+  components: {
+    AddCard
+  },
+  props: {
+    //收藏id,双向绑定
+    value: {
+      default: () => {
+        return []
+      },
+    },
+    gradeParams: Object,
+    grouplistId: Object,
+    listType: String,
+    PageData: Object,
+  },
+  data() {
+    return {
+      tableHeight: 0,//容器高度
+      addCradBtn: false,//新增卡片的跳窗
+      chooseOne: { id: 'a1', type: 'share', name: '數學一年級上學期課綱(1)', typeIcon: '' },
+      syllabusList: [//share同步他人,link被同步
+        { id: 'a1', name: '數學一年級上學期課綱(1)', type: 'share' },
+        { id: 'b2', name: '數學二年級上學期課綱(2)', type: 'link' },
+        { id: 'c3', name: '數學三年級上學期課綱(3)', type: '' },
+      ],
+      modeValue: false,//目前的模式
+      cardData: [//章節與資源卡片的data
+        //type:all全部一覽、order依序一覽
+        {
+          id: 1, sort: 1, name: '第一章節', btn: true, type: 'order', item: [{ id: 11, sort: 1, name: '四則運算', type: 'all' },
+          { id: 12, sort: 2, name: '加法與減法', type: 'all' }, { id: 13, sort: 3, name: '乘法', type: 'order' }]
+        },
+        { id: 2, sort: 2, name: '第二章節', btn: true, type: 'all', item: [{ id: 21, sort: 1, name: '幾何圖形', type: 'order' },] },
+        { id: 3, sort: 3, name: '第3章節', btn: true, type: 'order', item: [{ id: 31, sort: 1, name: '幾何圖形3', type: 'all' },] },
+        { id: 4, sort: 4, name: '第4章節', btn: true, type: 'all', item: [{ id: 41, sort: 1, name: '幾何圖形4', type: 'all' },] },
+        { id: 5, sort: 5, name: '第5章節', btn: true, type: 'all', item: [{ id: 51, sort: 1, name: '幾何圖形5', type: 'order' },] },
+      ],
+      poptipShow: false,//分享窗開關
+      dragItemId: null,
+    }
+  },
+  computed: {
+  },
+  created() {
+  },
+  mounted() {
+  },
+  methods: {
+    selectSyllabus(index) {
+      this.chooseOne.id = index;
+      this.chooseOne.type = this.syllabusList.find(item => item.id === index).type;
+      this.chooseOne.name = this.syllabusList.find(item => item.id === index).name;
+      this.PageData.data.Title = this.syllabusList.find(item => item.id === index).name;
+      this.PageData.type = this.chooseOne.type;
+      this.$emit('PageData', this.PageData);
+    },
+    switchFun() {
+      this.PageData.mode = this.modeValue ? 'Schedule' : 'Resource';
+      this.$emit('PageData', this.PageData);
+    },
+    cardClick(id, level, type, Chapter, Section) {
+      let mode = this.modeValue ? 'Schedule' : 'Resource';
+      let mydata = { Title: this.chooseOne.name, Chapter: Chapter, Section: Section, type: type };
+      //{id:id,mode:mode,level:level,type:是否是被同步的狀態,data:mydata})
+      this.$emit('PageData', { id: id, mode: mode, level: level, type: this.chooseOne.type, data: mydata });
+    },
+    poptipFun() {
+      this.poptipShow = this.poptipShow === true ? false : true;
+      this.$Message.success('已複製');
+    },
+    //拖曳相關--str---↓↓--
+    allowDrop(e) {//取消默認行為
+      e.preventDefault();
+    },
+    dragStart(e, index, type, chapterIndex = null) {//拖曳開始
+      e.dataTransfer.setData('Text', JSON.stringify({ index, type, chapterIndex }));
+    },
+    ChapterDrop(e, listIndex) {//章節放置
+      this.allowDrop(e);
+      const data = JSON.parse(e.dataTransfer.getData('Text'));
+      const { index, type, chapterIndex } = data;
+
+      if(type==='Chapter'){//章節與章節換
+        let arr = this.cardData.concat([]);
+        let temp = arr.splice(index, 1);
+        arr.splice(listIndex, 0, temp[0]);
+        this.cardData = arr;        
+        this.updateSort();// 更新排序
+      }else if(type==='Section'){//資源卡片與章節換
+        const sourceChapter = this.cardData[chapterIndex];
+        const movedResource = sourceChapter.item.splice(index, 1)[0];
+        const targetChapter = this.cardData[listIndex];
+        targetChapter.item.splice(0, 0, movedResource);
+        this.updateSort();
+      }      
+    },
+    SectionDrop(e, targetIndex, targetType, targetChapterIndex = null) {//資源小節放置
+      this.allowDrop(e);
+      const data = JSON.parse(e.dataTransfer.getData('Text'));
+      const { index, type, chapterIndex } = data;
+      if (type === 'Section') {
+        // 处理资源拖拽
+        const sourceChapter = this.cardData[chapterIndex];
+        const movedResource = sourceChapter.item.splice(index, 1)[0];
+        const targetChapter = this.cardData[targetChapterIndex];
+        targetChapter.item.splice(targetIndex, 0, movedResource);
+      }      
+      this.updateSort();// 更新排序
+    },
+    updateSort() {// 更新排序
+      this.cardData.forEach((chapter, chapterIndex) => {
+        chapter.sort = chapterIndex + 1;
+        chapter.item.forEach((resource, resourceIndex) => {
+          resource.sort = resourceIndex + 1;
+        });
+      });
+    },
+    dragEnd() {//放置結束
+      //console.log("dragEnd");
+    },  
+    //拖曳相關--end---↑↑--
+  },
+  watch: {
+    grouplistId: {
+      deep: true,
+      immediate: true,
+      handler(n, o) {
+      }
+    }
+  }
+}
+</script>
+<style lang="less" scoped>
+@import "./SyllabusMenu.less";
+</style>
+<style lang="less">
+//.ivu-table.ivu-table-stripe .ivu-table-cell.demo-table-info-column
+</style>

+ 146 - 0
TEAMModelOS/ClientApp/src/view/mycourse/syllabus/SyllabusResource.less

@@ -0,0 +1,146 @@
+
+.between{//分兩邊
+    display: flex;
+    justify-content: space-between;
+}
+.center {//中心
+    text-align: center;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+}
+.pointer{//使其滑鼠滑入有小手可點
+    cursor: pointer;
+}
+
+.SyllabusResource-container
+{
+    width: calc(100% - 300px);
+    padding: 10px;
+
+    .SaveDelDiv
+    {
+        .ivu-btn
+        {
+            margin-left: 10px;
+        }
+        .saveDiv
+        {
+            width: 100%;
+            display: flex;
+            justify-content: flex-end;
+
+            /* 將內容向右推送 */
+            .save-info-ok
+            {
+                text-align: right;
+                display: flex;
+                align-items: center;
+                color: #808695;
+            }
+
+            .save-info-erro
+            {
+                text-align: right;
+                display: flex;
+                align-items: center;
+                color: #ed4014;
+            }
+        }
+    }
+
+    .fontSize10
+    {
+        font-size: 10px;
+    }
+
+    .list-name
+    {
+        width: 90%;
+        color: #2d8cf0;
+        padding: 2px 5px;
+        border: 1px solid #e8eaec;
+    }
+    .list-name2
+    {
+        width: 90%;
+        padding: 2px 5px;
+        border: 1px solid #e8eaec;
+    }
+
+    .body
+    {
+        overflow: auto;
+        margin: 0px 10px 10px 10px;
+        margin-top: 0px;
+        
+        .top10{
+            margin-top: 15px;
+        }
+        .linkFor{
+            margin-top: 5px;
+            margin-bottom: 20px;
+        }
+        .group
+        {
+            .title
+            {
+                margin-bottom: 5px;
+            }
+
+            .switch
+            {
+                margin-left: 5px;
+            }
+            .ivu-radio-wrapper{
+                margin-right: 30px;
+            }
+        }
+
+        .showClass-div
+        {
+            margin-top: 10px;
+
+            .showClass
+            {
+                color: #2d8cf0;
+                padding: 5px;
+                border: 1px solid #2d8cf0;
+                border-radius: 5px;
+                margin: 5px;
+            }
+        }
+        .thisTitle{
+            background-color: #e8eaec;
+            padding:2px 20px;
+        }
+        .add-card{
+            padding: 5px 10px;
+            margin: 10px 5px;
+        }
+        .cardRow{
+            overflow: auto;
+            height: calc(100vh - 400px);
+            margin-top: 5px;
+            .card{
+                background-color: white;
+                border-radius: 8px;            
+                // font-weight: 900;
+                border: 1px solid #e8eaec;
+                box-shadow: 0 0 3px #00000011;
+            }
+            .rCard{                
+                margin: 5px;       
+                padding: 10px 10px;
+                .left{                
+                    display: flex;
+                    align-items: center;
+                    img{
+                        height: 20px;
+                        margin-right: 10px;
+                    }
+                }            
+            }
+        }        
+    }
+}

+ 258 - 0
TEAMModelOS/ClientApp/src/view/mycourse/syllabus/SyllabusResource.vue

@@ -0,0 +1,258 @@
+<template>
+  <div class="SyllabusResource-container">
+    <div class="SaveDelDiv between"><!--路徑與存檔刪除鈕-->
+      <div v-if="PageData.level === 'Title'" class="fontSize10">{{PageData.data.Title}}</div>
+      <div v-else-if="PageData.level === 'Chapter'" class="fontSize10">{{PageData.data.Title}} /
+        {{PageData.data.Chapter}}</div>
+      <div v-else-if="PageData.level === 'Section'" class="fontSize10">{{PageData.data.Title}} /
+        {{PageData.data.Chapter}} / {{PageData.data.Section}}</div>
+      <div>
+        <div>
+          <Button @click="deleteFun(PageData.level)">刪除</Button>
+          <Button @click="saveFun(PageData.level)" type="primary">儲存</Button>
+        </div>
+        <div class="saveDiv">
+          <div v-if="isSave" class="save-info-ok">
+            <Icon type="md-checkmark-circle" />已存檔
+          </div>
+          <div v-if="!isSave" class="save-info-erro">
+            <Icon type="md-alert" />未存檔
+          </div>
+        </div>
+      </div>
+    </div>
+    <div v-if="PageData.level === 'Title'">
+      <div class="body" style="height: calc(100vh - 280px);">
+        <div v-if="PageData.type === 'link'" class="group fontSize10 linkFor" ><!--同步於-->
+          同步於 
+          <span class="showClass-div">
+            <span  class="showClass">
+              {{Synchronize[0].name}}
+            </span>
+          </span> 
+          {{Synchronize[0].title}}
+        </div>
+        <div class="group"><!--課綱名稱-->
+          <div class="title">課綱名稱</div>
+          <input v-if="PageData.type !== 'link'" type="text" v-model="titleData.name" 
+            placeholder="" class="list-name" maxlength="50" />
+          <input v-else-if="PageData.type === 'link'" type="text" v-model="titleData.name" 
+            placeholder="" class="list-name2" maxlength="50" disabled/>
+        </div>
+        <div class="group top10"><!--課綱說明-->
+          <div class="title">課綱說明</div>
+          <textarea v-if="PageData.type !== 'link'" type="text" rows="10" cols="30" datatype="*" 
+            class="list-name" placeholder="請輸入..." v-model="titleData.info"></textarea>
+          <textarea v-else-if="PageData.type === 'link'" type="text" rows="10" cols="25" datatype="*" 
+            class="list-name2"  v-model="titleData.info" disabled></textarea>
+        </div>
+        <div class="group top10"><!--上架狀況-->
+          <div class="title">上架狀況</div>
+          <i-Switch v-model="release" size="large" class="switch" :before-change="releaseChangeFun">
+            <template #open>
+              <span>ON</span>
+            </template>
+            <template #close>
+              <span>OFF</span>
+            </template>
+          </i-Switch>
+        </div>
+        <div v-if="PageData.type !== 'link'" class="group top10"><!--同步對象-->
+          <div>同步對象
+            <Icon type="md-settings" size="15" class="pointer" @click="SyncClassCtl=true"/>
+          </div>
+          <div class="showClass-div">
+            <span v-for="value in Synchronize" :key="value.id" class="showClass">
+              {{value.name}}
+            </span>
+          </div>
+        </div>
+      </div>
+    </div>
+    <div v-else>
+      <div class="body"><!--上半部分發佈類型與課綱-->
+        <div class="group">
+          <div>發佈類型
+            <Icon type="md-help-circle" size="15" />
+          </div>
+          <RadioGroup v-model="PageData.data.type" >
+            <Radio label="all" v-if="PageData.type !== 'link' ||PageData.type === 'link' && PageData.data.type ==='all'">
+              <span>全部一覽</span>
+            </Radio>
+            <Radio label="order" v-if="PageData.type !== 'link' ||PageData.type === 'link' && PageData.data.type ==='order'">
+              <span>依序一覽</span>
+              <Icon type="md-git-pull-request" size="17" />
+            </Radio>
+          </RadioGroup>
+        </div>
+        <div class="group top10">
+          <div class="title">課綱名稱</div>
+          <input v-if="PageData.type !== 'link'" type="text" v-model="titleData.name" placeholder="" class="list-name" maxlength="50" />
+          <input v-else-if="PageData.type === 'link'" type="text" v-model="titleData.name" placeholder="" class="list-name2" maxlength="50" disabled/>
+        </div>
+      </div>
+      <div v-if="PageData.level === 'Section'">
+        <div class="body">
+          <div class="thisTitle">本項資源</div>
+          <div class="cardRow">
+            <div v-if="PageData.type !== 'link'" class="card add-card center pointer" @click="AddResourcesBtn=true">
+              <Icon type="md-add-circle" :size="25" />
+            </div>
+            <div v-for="(item,index) in Resource" :key="index" class="card rCard between"
+              draggable="true" @dragstart="dragStart($event, index)" @dragover="allowDrop" 
+              @drop="drop($event, index)" @dragend="dragEnd">
+              <div class="left">
+                <img :src="getResourceImage(item.type)"/>                
+                {{item.name}}
+              </div>
+              <Icon type="ios-trash" v-if="PageData.type !== 'link'" size="20" style="cursor: pointer"></Icon>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+    <SyncClass :SyncClassCtl="SyncClassCtl" @SyncClassClose="SyncClassCtl=false"></SyncClass>
+    <ReleaseSyllabus :ReleaseCtl="ReleaseCtl" @ReleaseFun="ReleaseCtl=false" @ReleaseOpenFun="ReleaseOpenFun"></ReleaseSyllabus>
+    <AddResources :DialogBoxCtl="AddResourcesBtn" @DialogBoxClose="AddResourcesBtn=false"></AddResources>
+  </div>
+</template>
+<script>
+import SyncClass from "./DialogBox/SyncClass.vue"
+import ReleaseSyllabus from "./DialogBox/ReleaseSyllabus.vue"
+import AddResources from "./DialogBox/AddResources.vue"
+export default {
+  components: {
+    SyncClass,ReleaseSyllabus,AddResources
+  },
+  props: {
+    //收藏id,双向绑定    
+    PageData: Object,
+    getResourceImage:Function,
+  },
+  data() {
+    return {
+      isSave: true,//存檔
+      treeShow: '',//路徑樹
+      titleData: { name: '', info: '課綱說明的內容' },
+      release: false,//發佈
+      SyncClassCtl:false,//同步班級的跳窗開關
+      ReleaseCtl:false,//上架跳窗
+      AddResourcesBtn:false,//新增資源跳窗
+      Synchronize: [//同步對象,如果有title是被同步於哪個課綱的課綱
+        { id: 1, name: '七年一班',title:'數學一年級上學期課綱(1)' },
+        { id: 2, name: '七年七班' },
+      ],
+      Resource: [//資源
+        //type類型:
+        //圖示:image、doc、excel、ppt、video、audio、item、paper、link、zip、other
+        //文字類:教材、評量、活動、投票、問卷
+        { id: 1,sort:1, type: 'image', name: '蘋果有幾個?' },
+        { id: 2,sort: 2, type: 'doc', name: '大樹與小數' },
+        { id: 3,sort: 3, type: 'excel', name: '試算表' },
+        { id: 4,sort: 4, type: 'ppt', name: '投影片' },
+        { id: 5,sort: 5, type: 'video', name: '影片' },
+        { id: 6,sort: 6, type: 'audio', name: '音樂' },
+        { id: 7,sort: 7, type: 'item', name: '物件' },
+        { id: 8,sort: 8, type: 'paper', name: '紙張' },
+        { id: 9,sort: 9, type: 'link', name: '連結' },
+        { id: 10,sort: 10, type: 'zip', name: '壓縮檔' },
+        { id: 11,sort: 11, type: 'other', name: '其它' },
+      ],
+      
+    }
+  },
+  computed: {
+  },
+  created() {
+  },
+  methods: {
+    deleteFun(type) {//刪除按鈕
+      if (type === 'Title') {//標題
+
+      } else if (type === 'Chapter') {//章節
+
+      } else if (type === 'Section') {//資源卡片
+
+      }
+    },
+    saveFun(type) {//存檔案鈕
+      if (type === 'Title') {//標題
+
+      } else if (type === 'Chapter') {//章節
+
+      } else if (type === 'Section') {//資源卡片
+
+      }
+      this.isSave = true;
+    },
+    releaseChangeFun(){
+      return new Promise((resolve) => {
+        if(!this.release)  this.ReleaseCtl=true;
+        else{//下架確認
+          this.$Modal.confirm({
+              title: '詢問',
+              content: '確定要下架嗎?',
+              onOk: () => {
+                  resolve();
+              }
+          });
+        }
+      });
+    },
+    ReleaseOpenFun(value){
+      this.ReleaseCtl=false; 
+      this.release=value;
+    },
+    //拖曳相關--str---↓↓--
+    allowDrop(e) {//取消默認行為
+      e.preventDefault();
+    },
+    dragStart(e, index) {//拖曳開始
+      let tar = e.target;
+      e.dataTransfer.setData('Text', index);
+      if (tar.tagName.toLowerCase() == 'li') {
+        // console.log('drag start')
+        // console.log('drag Index: ' + index)
+      }
+    },
+    drop(e, index) {//放置
+      this.allowDrop(e);
+      let arr = this.Resource.concat([]),
+      dragIndex = e.dataTransfer.getData('Text');
+      let temp = arr.splice(dragIndex, 1);  
+      arr.splice(index, 0, temp[0]);
+      this.Resource = arr;
+
+      //↓改正Resource資料的sort
+      for (let i = 0; i < this.Resource.length; i++) {
+        this.Resource[i].sort = this.Resource.length - i;
+      }   
+    },
+    dragEnd() {//放置結束
+      console.log("dragEnd");
+    },
+    //拖曳相關--end---↑↑--
+  },
+  watch: {
+    PageData: {
+      deep: true,
+      immediate: true,
+      handler(newVal, oldVal) {
+        if (this.PageData.level === 'Title') {
+          this.titleData.name = this.PageData.data.Title;
+        } else if (this.PageData.level === 'Chapter') {
+          this.titleData.name = this.PageData.data.Chapter;
+        } else if (this.PageData.level === 'Section') {
+          this.titleData.name = this.PageData.data.Section;
+        }
+      }
+    },
+  }
+}
+</script>
+<style lang="less" scoped>
+@import "./SyllabusResource.less";
+</style>
+<style lang="less">
+//.ivu-table.ivu-table-stripe .ivu-table-cell.demo-table-info-column
+</style>

+ 39 - 0
TEAMModelOS/ClientApp/src/view/mycourse/syllabus/SyllabusSchedule.less

@@ -0,0 +1,39 @@
+.between{//分兩邊
+    display: flex;
+    justify-content: space-between;
+}
+.imgS{
+    height: 20px;
+    margin-right: 10px;
+}
+.SyllabusSchedule-container{
+    width: calc(100% - 300px);
+    padding: 10px;
+
+    .fontSize10{
+        font-size: 10px;
+    }
+    .body
+    {
+        margin: 10px;
+        margin-top: 20px;
+        .title{
+            margin: 10px 0px;
+            img{
+                height: 20px;
+                margin-right: 10px;
+            }
+            .Option{
+                display : flex;
+                align-items: center;
+                img{
+                    height: 20px;
+                    margin-right: 10px;
+                }
+            }            
+        }
+        .tag{
+            margin: 5px ;
+        }        
+    }
+}

+ 155 - 0
TEAMModelOS/ClientApp/src/view/mycourse/syllabus/SyllabusSchedule.vue

@@ -0,0 +1,155 @@
+<template>
+  <div class="SyllabusSchedule-container">
+    <div class="SaveDelDiv between"><!--路徑-->
+      <div v-if="PageData.level === 'Title'" class="fontSize10">{{PageData.data.Title}}</div>
+      <div v-else-if="PageData.level === 'Chapter'" class="fontSize10">{{PageData.data.Title}} /
+        {{PageData.data.Chapter}}</div>
+      <div v-else-if="PageData.level === 'Section'" class="fontSize10">{{PageData.data.Title}} /
+        {{PageData.data.Chapter}} / {{PageData.data.Section}}</div>
+    </div>
+    <div v-if="PageData.level === 'Chapter'"><!--章節完成%-->
+      <div class="body">
+        <DataTable :value="ChapterData" tableStyle="min-width: calc(100% - 20px);" stripedRows 
+          :scrollHeight="tableHeight.toString()+'px'" scrollable sortField="irs" :sortOrder="1" removableSort>
+          <Column field="irs" header="IRS" sortable
+            :styles="{ 'min-width': '120px',   'display': 'flex', 'align-items': 'center', 'justify-content': 'center' }"
+            ></Column>
+          <Column field="id" header="學號" sortable
+            :styles="{ 'min-width': '120px',   'display': 'flex', 'align-items': 'center', 'justify-content': 'center' }"
+            ></Column>
+          <Column field="name" header="姓名" sortable
+            :styles="{ 'min-width': '120px',   'display': 'flex', 'align-items': 'center', 'justify-content': 'center' }"
+            ></Column>
+          <Column field="rate" header="章節完成度" sortable
+            :styles="{ 'min-width': '120px',   'display': 'flex', 'align-items': 'center', 'justify-content': 'center' }"
+            ></Column>
+        </DataTable>
+      </div>      
+    </div>
+    <div v-else-if="PageData.level === 'Section'"><!--資源進度-->
+      <div class="body">
+        <div class="between title">
+          <div>
+            資源名稱:
+            <Select v-model="selectNow.id"  style="width:200px" @on-change="selectResource">
+              <template #prefix>
+                <img :src="getResourceImage(selectNow.type)" />
+              </template>
+              <Option v-for="item in resourceTitle" :value="item.id" :key="item.id" class="Option">
+                <img :src="getResourceImage(item.type)" />
+                {{ item.name }}
+              </Option>
+            </Select>
+            <span class="tag">
+              <Tag v-if="stateTag==='start'" color="success">進行中</Tag>
+              <Tag v-else-if="stateTag==='end'" color="warning">已結束</Tag>
+            </span>            
+          </div>
+          <Button type="primary" ghost>分析</Button>
+        </div>
+        <DataTable :value="ChapterData" tableStyle="min-width: calc(100% - 20px); " stripedRows 
+          :scrollHeight="tableHeight.toString()+'px'" scrollable sortField="irs" :sortOrder="1" removableSort>
+          <Column field="irs" header="IRS" sortable
+            :styles="{ 'min-width': '120px',   'display': 'flex', 'align-items': 'center', 'justify-content': 'center' }"
+            ></Column>
+          <Column field="id" header="學號" sortable
+            :styles="{ 'min-width': '120px',   'display': 'flex', 'align-items': 'center', 'justify-content': 'center' }"
+            ></Column>
+          <Column field="name" header="姓名" sortable
+            :styles="{ 'min-width': '120px',   'display': 'flex', 'align-items': 'center', 'justify-content': 'center' }"
+            ></Column>
+          <Column field="rate" header="完成度" sortable
+            :styles="{ 'min-width': '120px',   'display': 'flex', 'align-items': 'center', 'justify-content': 'center' }"
+            >
+            <template #body="rowData">
+              <div>
+                <div v-if="rowData.data.rate" style="color:#19be6b;">
+                  <Icon type="md-checkmark-circle" />
+                  已完成
+                </div>
+                <div v-else style="color:#ed4014;">
+                  <Icon type="md-close-circle" />
+                  未完成
+                </div>
+              </div>
+            </template>  
+          </Column>
+        </DataTable>
+      </div>
+    </div>
+  </div>
+</template>
+<script>
+import DataTable from 'primevue/datatable'
+import Column from 'primevue/column'
+import 'primevue/resources/themes/saga-blue/theme.css'; // 根據你喜好的主題選擇
+import 'primevue/resources/primevue.min.css';
+
+export default {
+  components: {
+    Column, DataTable,
+  },
+  props: {
+    //收藏id,双向绑定    
+    PageData: Object,
+    getResourceImage:Function,
+  },
+  data() {
+    return {
+      tableHeight: 0,
+      ChapterData: [
+        { irs: 1234, id: 123456, name: 'Jhon', rate: false },
+        { irs: 1235, id: 223456, name: 'Mary', rate: false },
+        { irs: 1236, id: 323456, name: 'Alex', rate: true },
+        { irs: 1234, id: 123456, name: 'Jhon', rate: true },
+        { irs: 1235, id: 223456, name: 'Mary', rate: true },
+        { irs: 1236, id: 323456, name: 'Alex', rate: false },
+        { irs: 1234, id: 123456, name: 'Jhon', rate: false },
+        { irs: 1235, id: 223456, name: 'Mary', rate: true },
+        { irs: 1236, id: 323456, name: 'Alex', rate: true },
+      ],
+      resourceTitle: [//state狀態:start、end
+        { id: 1, name: '蘋果有幾個?',state:'start',type:'image' },
+        { id: 2, name: '大樹與小數',state:'end',type:'doc' },
+      ],
+      selectNow:{id:1,name:'',type:''},//現在選擇的資源
+      stateTag:'start',
+    }
+  },
+  computed: {
+  },
+  created() {
+  },
+  methods: {    
+    selectResource(id) {//選擇資源選項觸發
+      this.selectNow.id = id;
+      this.selectNow.type =  this.resourceTitle.find(item => item.id === id).type;
+      this.selectNow.name = this.resourceTitle.find(item => item.id === id).name;
+    },
+    getTableHeight() {
+      let num = this.PageData.level === 'Section' ? 280 : 240;
+      this.tableHeight = window.innerHeight - num;
+    },
+  },
+  mounted() {
+    window.addEventListener('resize', this.getTableHeight);
+    this.getTableHeight();
+  },
+  watch: {
+    PageData: {
+      deep: true,
+      immediate: true,
+      handler(n, o) {
+        this.getTableHeight();
+        this.selectResource(this.resourceTitle[0].id);//預設顯示第一個資源
+      }
+    }
+  }
+}
+</script>
+<style lang="less" scoped>
+@import "./SyllabusSchedule.less";
+</style>
+<style lang="less">
+//.ivu-table.ivu-table-stripe .ivu-table-cell.demo-table-info-column
+</style>

+ 15 - 0
TEAMModelOS/ClientApp/src/view/mycourse/syllabus/addSyllabus.less

@@ -0,0 +1,15 @@
+.addButton{
+    margin-top: 10px;
+    // position: absolute;
+    // right: 20px;
+}
+.body{
+    margin-left: 20px;
+    .selectGroup{
+        margin-left: 20px ;
+        margin-bottom: 10px;
+        .ivu-select{
+            margin: 0px 3px;
+        }
+    }
+}

+ 100 - 0
TEAMModelOS/ClientApp/src/view/mycourse/syllabus/addSyllabus.vue

@@ -0,0 +1,100 @@
+<template>
+  <div class="addSyllabus-container">
+    <div class="body">
+      <RadioGroup v-model="addData.chooseWay" vertical>
+        <Radio label="Empty">
+          <span>新增空課綱</span>
+        </Radio>
+        <Radio label="Code">
+          <span>貼上課綱代碼:</span>
+          <Input v-model="addData.codeData" placeholder="Enter something..." style="width: 300px" />
+        </Radio>
+        <Radio label="Choose" :tabindex="0">
+          <span>選擇課綱:</span>
+        </Radio>
+        <div class="selectGroup">
+          <Select v-model="model" style="width:100px" placeholder="來源">
+            <Option v-for="item in cityList" :value="item.id" :key="item.id">{{ item.name }}</Option>
+          </Select>
+          <Select v-model="model" style="width:150px" placeholder="課程">
+            <Option v-for="item in cityList" :value="item.id" :key="item.id">{{ item.name }}</Option>
+          </Select>
+          <Select v-model="model" style="width:150px" placeholder="班級">
+            <Option v-for="item in cityList" :value="item.id" :key="item.id">{{ item.name }}</Option>
+          </Select>
+          <Select v-model="model" style="width:150px" placeholder="課綱">
+            <Option v-for="item in cityList" :value="item.id" :key="item.id">{{ item.name }}</Option>
+          </Select>
+        </div>
+        <Button v-if="addBtn" type="primary" class="addButton" @click="addSyllabusFun">新增</Button>
+      </RadioGroup>
+    </div>
+  </div>
+</template>
+<script>
+// import SyllabusBody from "./SyllabusBody.vue"
+export default {
+  components: {
+    // SyllabusBody
+  },
+  props: {
+    //收藏id,双向绑定
+    value: {
+      default: () => {
+        return []
+      },
+    },
+    grouplistId: {
+      type: Object,
+      default: () => {
+        return {}
+      }
+    },
+    addBtnCtl:Boolean,
+  },
+  data() {
+    return {
+      addBtn:true,//控制新增按鈕是否顯示
+      addData: {//新增課綱的相關內容
+        chooseWay: 'Empty',
+        codeData: '',
+      },
+      cityList: [
+        {
+          id: 'New York',
+          name: 'New York'
+        },
+        {
+          id: 'London',
+          name: 'London'
+        },
+      ],
+      model: '',
+    }
+  },
+  computed: {
+  },
+  created() {
+  },
+  methods: {
+    addSyllabusFun() {
+      console.log("addSyllabusFun 新增新課綱");
+    }
+  },
+  watch: {
+    addBtnCtl: {
+      deep: true,
+      immediate: true,
+      handler(n, o) {
+        this.addBtn = n;
+      }
+    }
+  }
+}
+</script>
+<style lang="less" scoped>
+@import "./addSyllabus.less";
+</style>
+<style lang="less">
+//.ivu-table.ivu-table-stripe .ivu-table-cell.demo-table-info-column
+</style>

+ 34 - 33
TEAMModelOS/Controllers/Both/ScoreCalcController.cs

@@ -736,7 +736,7 @@ namespace TEAMModelOS.Controllers
                             zeroScores.Add(0);
                         }
                         for (int k = 0; k < scoreCalcActivityBaseList_copy.Count; k++)
-                        {                         
+                        {
                             switch (scoreCalcActivityBaseList_copy[k].type)
                             {
                                 case "lessonrecord":
@@ -773,12 +773,12 @@ namespace TEAMModelOS.Controllers
                                 case "homework":
                                     // ===設定作業活動===
                                     await setCopyAct(scoreCalcActivityBaseList_copy[k].id, teammodelId, scoreCalcActivity_homework, zeroScores);
-                                    scoreCalcActivity_homework = await clientTeacher.CreateItemAsync(scoreCalcActivity_homework, new PartitionKey($"{scoreCalcActivity_homework.code}"));                                   
+                                    scoreCalcActivity_homework = await clientTeacher.CreateItemAsync(scoreCalcActivity_homework, new PartitionKey($"{scoreCalcActivity_homework.code}"));
                                     break;
                                 case "exam":
                                     // ===設定評量活動===
                                     await setCopyAct(scoreCalcActivityBaseList_copy[k].id, teammodelId, scoreCalcActivity_eaxm, zeroScores);
-                                    scoreCalcActivity_eaxm = await clientTeacher.CreateItemAsync(scoreCalcActivity_eaxm, new PartitionKey($"{scoreCalcActivity_eaxm.code}"));                                   
+                                    scoreCalcActivity_eaxm = await clientTeacher.CreateItemAsync(scoreCalcActivity_eaxm, new PartitionKey($"{scoreCalcActivity_eaxm.code}"));
                                     break;
                                 case "custom":
                                     // ===自訂項目===
@@ -793,21 +793,21 @@ namespace TEAMModelOS.Controllers
                                     scoreCalcActivity.editScores = editScores;
                                     scoreCalcActivity.code = scoreCalcActivityBaseList_copy[k].pk + "-" + teammodelId;
                                     await setCopyAct(scoreCalcActivityBaseList_copy[k].id, teammodelId, scoreCalcActivity, zeroScores);
-                                    
+
                                     // 新增項目及子項目
-                                    scoreCalcActivity = await clientTeacher.CreateItemAsync(scoreCalcActivity, new PartitionKey($"{scoreCalcActivity.pk + "-" + teammodelId}"));                                                                     
+                                    scoreCalcActivity = await clientTeacher.CreateItemAsync(scoreCalcActivity, new PartitionKey($"{scoreCalcActivity.pk + "-" + teammodelId}"));
                                     break;
                             }
                         }
                     }
-                    else 
+                    else
                     {// 沒有複製id的資料  走原本的新增動作
-                        isCopyMode = false;                        
-                    }                                      
+                        isCopyMode = false;
+                    }
                 }
                 else
                 {// 沒有複製id  走原本的新增動作
-                    isCopyMode = false;                    
+                    isCopyMode = false;
                 }
 
                 if (!isCopyMode) // 不是複製模式  走原本的新增動作
@@ -829,7 +829,7 @@ namespace TEAMModelOS.Controllers
                     await foreach (var item in clientTeacher.GetItemQueryIterator<ScoreCalcFunc>(queryText: sql_FormulaCopy))
                     {
                         scoreCalcFuncList_copy.Add(item);
-                    }                   
+                    }
 
 
                     for (int i = 0; i < scoreCalcFuncList_copy.Count; i++)
@@ -1224,29 +1224,6 @@ namespace TEAMModelOS.Controllers
                 // 比對資料  決定哪些公式及項目表要更新
                 for (int i = 0; i < updateFormulaRq.scoreCalcFunc.Count; i++)
                 {
-                    #region 更新項目表公式id
-                    if (updateFormulaRq.scoreCalcFunc[i].use)
-                    {
-                        // 更新項目使用的公式id
-                        // 取課堂紀錄資料
-                        // ToDo...以後可能需要其他活動也可以用公式
-                        ScoreCalcLsRecord scoreCalcLsRecord = await clientTeacher.ReadItemAsync<ScoreCalcLsRecord>(updateFormulaRq.scoreCalcActId, new PartitionKey($"ScoreCalcAct-{teammodelId}"));
-                        switch (updateFormulaRq.method)
-                        {
-                            case "attend":
-                                scoreCalcLsRecord.stuAttendFunctionId = updateFormulaRq.scoreCalcFunc[i].id;
-                                break;
-                            case "point":
-                                scoreCalcLsRecord.stuPointFunctionId = updateFormulaRq.scoreCalcFunc[i].id;
-                                break;
-                            case "interaction":
-                                scoreCalcLsRecord.stuItactFunctionId = updateFormulaRq.scoreCalcFunc[i].id;
-                                break;
-                        }
-                        scoreCalcLsRecord = await clientTeacher.ReplaceItemAsync(scoreCalcLsRecord, $"{scoreCalcLsRecord.id}", new PartitionKey(scoreCalcLsRecord.code));
-                    }
-                    #endregion
-
                     #region 更新 or 新增公式
                     for (int j = 0; j < scoreCalcFuncs.Count; j++)
                     {
@@ -1264,6 +1241,7 @@ namespace TEAMModelOS.Controllers
                         ScoreCalcFunc scoreCalcFunc = new ScoreCalcFunc();
                         scoreCalcFunc.code = $"ScoreCalcActFormula-{teammodelId}";
                         scoreCalcFunc.id = Guid.NewGuid().ToString();
+                        updateFormulaRq.scoreCalcFunc[i].id = scoreCalcFunc.id;// 同時設定要新增的公式Id給Act
                         scoreCalcFunc.name = updateFormulaRq.scoreCalcFunc[i].name;
                         scoreCalcFunc.scorecalcActId = updateFormulaRq.scoreCalcActId;
                         // ToDo...以後可能需要其他活動也可以用公式
@@ -1275,6 +1253,29 @@ namespace TEAMModelOS.Controllers
                         scoreCalcFunc = await clientTeacher.CreateItemAsync(scoreCalcFunc, new PartitionKey($"{scoreCalcFunc.code}"));
                     }
                     #endregion
+
+                    #region 更新項目表公式id
+                    if (updateFormulaRq.scoreCalcFunc[i].use)
+                    {
+                        // 更新項目使用的公式id
+                        // 取課堂紀錄資料
+                        // ToDo...以後可能需要其他活動也可以用公式
+                        ScoreCalcLsRecord scoreCalcLsRecord = await clientTeacher.ReadItemAsync<ScoreCalcLsRecord>(updateFormulaRq.scoreCalcActId, new PartitionKey($"ScoreCalcAct-{teammodelId}"));
+                        switch (updateFormulaRq.method)
+                        {
+                            case "attend":
+                                scoreCalcLsRecord.stuAttendFunctionId = updateFormulaRq.scoreCalcFunc[i].id;
+                                break;
+                            case "point":
+                                scoreCalcLsRecord.stuPointFunctionId = updateFormulaRq.scoreCalcFunc[i].id;
+                                break;
+                            case "interaction":
+                                scoreCalcLsRecord.stuItactFunctionId = updateFormulaRq.scoreCalcFunc[i].id;
+                                break;
+                        }
+                        scoreCalcLsRecord = await clientTeacher.ReplaceItemAsync(scoreCalcLsRecord, $"{scoreCalcLsRecord.id}", new PartitionKey(scoreCalcLsRecord.code));
+                    }
+                    #endregion
                 }