Przeglądaj źródła

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

CrazyIter_Bin 9 miesięcy temu
rodzic
commit
4051914680

+ 3 - 1
TEAMModelOS.Function/IESServiceBusTrigger.cs

@@ -40,6 +40,7 @@ using Newtonsoft.Json;
 using System.Security.Policy;
 using System.Net.Http.Headers;
 using Microsoft.AspNetCore.Hosting;
+using Newtonsoft.Json.Linq;
 
 namespace TEAMModelOS.Function
 {
@@ -2585,7 +2586,8 @@ namespace TEAMModelOS.Function
                                 //{
                                 //    //取得模板ID ※國際站就用繁體,大陸站就用簡體 [待定]
                                 //    string location = Environment.GetEnvironmentVariable("Option:Location");
-                                //    string tid = (location.Contains("China")) ? "" : "d-833d40ac6397414b852b91e2fa45850a";
+                                //    //string tid = (location.Contains("China")) ? "" : "d-833d40ac6397414b852b91e2fa45850a";
+                                //    string tid = string.Empty;
                                 //    string lang = (location.Contains("China")) ? "zh-cn" : "zh-tw";
                                 //    //取得通知內文
                                 //    string mailTitle = string.Empty;

+ 2 - 0
TEAMModelOS.SDK/Models/Cosmos/School/ExamInfo.cs

@@ -125,6 +125,8 @@ namespace TEAMModelOS.SDK.Models
         //統測ID [測試中]
         public string jointExamId { get; set; }
         public string moofenCode { get; set; }
+        //不可重複作答
+        public bool overwriteDisable { get; set; }
     }
     public class Custom {
         public string id { get; set; }

+ 8 - 3
TEAMModelOS.SDK/Models/Cosmos/Teacher/JointEvent.cs

@@ -35,7 +35,7 @@ namespace TEAMModelOS.SDK.Models
             public string id { get; set; } //活動進程ID
             public string name { get; set; } //進程名稱
             public string type { get; set; } //"exam":評量 "join":報名 "other":其他
-            public string examType { get; set; } //評量類型 "regular":一般競賽 "custom":挑戰
+            public string examType { get; set; } //評量類型 "regular":一般競賽 "custom":
             public bool examOverwrite { get; set; } //評量可否重複作答 true:可 false:不可
             public List<JointEventScheduleBlob> blobs { get; set; } = null; //說明文件存放Blob
             public string location { get; set; } //地點
@@ -58,11 +58,14 @@ namespace TEAMModelOS.SDK.Models
     public class JointEventGroupBase
     {
         public string scope { get; set; } //school:學校(選課班) private:個人 
+        public string type { get; set; } //類型 "regular":報名名單 "custom":決賽用名單
         public string creatorId { get; set; }
         public string creatorName { get; set; }
         public string creatorEmail { get; set; }
-        public string schoolId { get; set; } //老師個人課程時為null
+        public string schoolId { get; set; } //老師教育雲綁定的學校ID
+        public string schoolName { get; set; } //老師教育雲綁定的學校名稱
         public string periodId { get; set; } //老師個人課程時為null
+        public string jointScheduleId { get; set; } //活動進程ID 決賽用名單才有值,其餘為null
         public List<JointEventGroupCourse> courseLists { get; set; } = new();
         public class JointEventGroupCourse
         {
@@ -104,8 +107,10 @@ namespace TEAMModelOS.SDK.Models
     /// </summary>
     public class JointEventClassBase
     {
+        public string type { get; set; } //類型 "regular":報名名單 "custom":決賽用名單
         public string schoolId { get; set; }
         public string schoolName { get; set; }
+        public string jointScheduleId { get; set; } //活動進程ID 決賽用名單才有值,其餘為null
         public List<JointEventClassSchoolClass> classLists { get; set; } = new();
         public class JointEventClassSchoolClass
         {
@@ -138,7 +143,7 @@ namespace TEAMModelOS.SDK.Models
         public string jointGroupId { get; set; }
         public string creatorId { get; set; }
         public string name { get; set; }
-        public string examType { get; set; } //評量類型 "regular":一般競賽 "custom":挑戰
+        public string examType { get; set; } //評量類型 "regular":一般競賽 "custom":
         public bool examOverwrite { get; set; } //評量可否重複作答 true:可 false:不可
         public List<JointEventClassBase> classes { get; set; } = new(); //班級列表 ※examType="custom"才填入,其餘評量班級從報名班級(DB)取得
         public List<JointEventGroupBase> stuLists { get; set; } = new(); //個人課程列表 ※examType="custom"才填入,其餘評量班級從老師報名課程(DB)取得

+ 4 - 2
TEAMModelOS.SDK/Models/Service/JointService.cs

@@ -32,9 +32,9 @@ namespace TEAMModelOS.SDK.Models.Service
                 List<JointEventGroupBase> stuLists = new List<JointEventGroupBase>();
                 //取得JointCourse ※examType == "custom" 之後再處理
                 List<JointEventGroupDb> jointCourses = new List<JointEventGroupDb>();
-                if (!jointExam.examType.Equals("custom"))
+                if (!jointExam.examType.Equals("custom")) //老師報名名單
                 {
-                    string jointCourseSql = $"SELECT * FROM c WHERE c.jointEventId = '{jointExam.jointEventId}' AND c.jointGroupId = '{jointExam.jointGroupId}'";
+                    string jointCourseSql = $"SELECT * FROM c WHERE c.jointEventId = '{jointExam.jointEventId}' AND c.jointGroupId = '{jointExam.jointGroupId}' AND ( IS_DEFINED(c.type) = false OR c.type = 'regular' )";
                     await foreach (var item in client.GetContainer("TEAMModelOS", "Teacher").GetItemQueryIteratorSql<JointEventGroupDb>(queryText: jointCourseSql, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"JointCourse") }))
                     {
                         jointCourses.Add(item);
@@ -93,6 +93,8 @@ namespace TEAMModelOS.SDK.Models.Service
                             actExamInfo.year = DateTimeOffset.UtcNow.Year;
                             actExamInfo.startTime = jointExam.startTime;
                             actExamInfo.endTime = jointExam.endTime;
+                            ///是否重複作答
+                            actExamInfo.overwriteDisable = (jointExam.examOverwrite.Equals(false)) ? true : false;
 
                             examList.Add(actExamInfo);
                         }

+ 15 - 3
TEAMModelOS/ClientApp/public/lang/en-US.js

@@ -1647,6 +1647,7 @@ const LANG_EN_US = {
         secretTip2: 'Checking the box means that only the school administrators and I can see this exam file',
         cpTip1: 'Pick from the question bank',
         cpTip2: 'Pick from the exam paper',
+        cpTip3: '从课纲挑选',
         paperPickTip1: 'Currently picked',
         paperPickTip2: 'questions',
         paperPickTip3: 'There are currently no exam paper available! ',
@@ -3170,6 +3171,7 @@ const LANG_EN_US = {
             action: 'Operate',
             addTeaTitle: 'Add Marking Teacher',
             markNum: 'Marking Quantity',
+            totalMarkNum: '总阅卷量',
             noPublish: 'No marking task yet',
             publish: 'Start Task',
             startTime: 'Start Time',
@@ -7994,7 +7996,8 @@ const LANG_EN_US = {
         activitiesMustBeCompletedInOrder:'Events must be completed in order',
         stage:'Stage',
         description:'Description',
-        Eventlayout:'Event layout',
+        Eventlayout: 'Event type',
+        examType: 'Is regular exam',
         examCanBeChecked:'Exam are repeatable',
         operation:'Operation',        
         confirm:'Save',
@@ -8003,9 +8006,18 @@ const LANG_EN_US = {
         delete:'Delete',
         registrationGroup:'Registration group',
         export:'Export',
-        signup:'Sign up',
+        signup: 'Sign up',
+        exam: 'Exam',
+        other: 'Other',
         activityPlace:'Place',        
-        fileUrl:'FileUrl',
+        fileUrl: 'FileUrl',
+        joinStatus: 'Registration status',
+        tmid: 'TEAM Model ID',
+        teacherName: 'Name',
+        jointGroup: 'Group',
+        course: 'Course',
+        courseGroup: 'Class',
+        schoolName: 'School',
     },
     activity: {
         scoreWord: {

+ 16 - 4
TEAMModelOS/ClientApp/public/lang/zh-CN.js

@@ -1646,6 +1646,7 @@ const LANG_ZH_CN = {
         secretTip2:'勾选后保存的试卷仅有管理员及本人能看到',
         cpTip1: '从题库挑选',
         cpTip2: '从试卷挑选',
+        cpTip3: '从课纲挑选',
         paperPickTip1: '当前已挑选',
         paperPickTip2: '题',
         paperPickTip3: '当前无试卷可选择!',
@@ -1662,7 +1663,7 @@ const LANG_ZH_CN = {
         canChoose: '道题可选',
         xkw: '学科网',
         xkwMode: '学科网组卷',
-        df: '多分',
+        df: '多分',
         importItems: '试题导入',
         composePaper: '组成试卷',
         syncItems: '同步试题到题库',
@@ -3169,6 +3170,7 @@ const LANG_ZH_CN = {
             action: '操作',
             addTeaTitle: '添加阅卷老师',
             markNum: '阅卷量',
+            totalMarkNum: '总阅卷量',
             noPublish: '暂未发布阅卷任务',
             publish: '发布任务',
             startTime: '开始时间',
@@ -7995,7 +7997,8 @@ const LANG_ZH_CN = {
         activitiesMustBeCompletedInOrder:'必须按顺序完成活动',
         stage:'阶段',
         description:'说明',
-        Eventlayout:'活动布置',
+        Eventlayout: '活动类型',
+        examType: '是否为基本评量类型',
         examCanBeChecked:'评量可重复',
         operation:'操作',        
         confirm:'保存',
@@ -8004,9 +8007,18 @@ const LANG_ZH_CN = {
         delete:'删除',
         registrationGroup:'报名组别',
         export:'汇出',
-        signup:'报名',
+        signup: '报名',
+        exam: '评量',
+        other: '其他',
         activityPlace:'地点',        
-        fileUrl:'档案连结',
+        fileUrl: '档案连结',
+        joinStatus: '报名状况',
+        tmid: '醍摩豆帐号',
+        teacherName: '老师姓名',
+        jointGroup: '报名群组',
+        course: '课程',
+        courseGroup: '课程名单',
+        schoolName: '学校',
     },
     activity: {
         scoreWord: {

+ 16 - 4
TEAMModelOS/ClientApp/public/lang/zh-TW.js

@@ -1649,6 +1649,7 @@ const LANG_ZH_TW = {
         secretTip2: '勾選後儲存的試卷僅有管理員及本人能看到',
         cpTip1: '從題庫挑選',
         cpTip2: '從試卷挑選',
+        cpTip3: '从课纲挑选',
         paperPickTip1: '當前已挑選',
         paperPickTip2: '題',
         paperPickTip3: '當前無試卷可選擇! ',
@@ -1665,7 +1666,7 @@ const LANG_ZH_TW = {
         canChoose: '道題可選',
         xkw: '學科網',
         xkwMode: '學科網組卷',
-        df: '多分',
+        df: '多分',
         importItems: '試題匯入',
         composePaper: '組成試卷',
         syncItems: '同步試題到題庫',
@@ -3172,6 +3173,7 @@ const LANG_ZH_TW = {
             action: '操作',
             addTeaTitle: '增加閱卷教師',
             markNum: '閱卷量',
+            totalMarkNum: '总阅卷量',
             noPublish: '暫未發布閱卷任務',
             publish: '發布任務',
             startTime: '開始時間',
@@ -7994,7 +7996,8 @@ const LANG_ZH_TW = {
         activitiesMustBeCompletedInOrder:'必須按順序完成活動',
         stage:'階段',
         description:'說明',
-        Eventlayout:'活動布置',
+        Eventlayout: '活動類型',
+        examType: '是否為基本評量類型',
         examCanBeChecked:'評量可重複',
         operation:'操作',        
         confirm:'保存',
@@ -8004,9 +8007,18 @@ const LANG_ZH_TW = {
         delete:'刪除',
         registrationGroup:'報名組別',
         export:'匯出',
-        signup:'報名',
+        signup: '報名',
+        exam: '評量',
+        other: '其他',
         activityPlace:'地點',        
-        fileUrl:'檔案連結',
+        fileUrl: '檔案連結',
+        joinStatus: '報名狀況',
+        tmid: '醍摩豆帳號',
+        teacherName: '老師姓名',
+        jointGroup: '報名群組',
+        course: '課程',
+        courseGroup: '課程名單',
+        schoolName: '學校',
     },
     activity: {
         scoreWord: {

+ 4 - 3
TEAMModelOS/ClientApp/src/api/htcommunity.js

@@ -28,8 +28,9 @@ export default {
     jointExamFind: function(data) {
         return post('/joint/exam/find', data)
     },
-   
-   
-   
+    // 取得報名教師課程
+    jointCourseFind: function (data) {
+        return post('/joint/course/find', data)
+    },
 	
 }

+ 1 - 1
TEAMModelOS/ClientApp/src/common/BaseLayout.vue

@@ -1614,7 +1614,7 @@
 								role: "teacher|admin",
 								permission: "",
 								subName: "unifiedPurchasingPlatform",
-								isShow: this.IES5Menu,
+								isShow: this.IES5Menu && this.isGlobalSite,
 								child: [
 									{
 										icon: "iconfont icon-basic-setting",

+ 317 - 163
TEAMModelOS/ClientApp/src/view/htcommunity/htMgtHome.vue

@@ -1,171 +1,193 @@
 <template>
-  <div id="app" class="app-container">
-    <el-button type="primary" class="btn-new" @click="newEvent">{{this.$t("htcommunity.addEvent")}}</el-button>
-    <el-table :data="activities" class="table-main" border v-loading="mainLoading">
-      <el-table-column prop="index" :label="this.$t('htcommunity.serialNumber')" min-width="50"></el-table-column>
-      <el-table-column prop="name" :label="this.$t('htcommunity.activityName')"></el-table-column>
-      <el-table-column prop="time" :label="this.$t('htcommunity.schedule')" min-width="150"></el-table-column>
-      <el-table-column prop="admin" :label="this.$t('htcommunity.administrator')"></el-table-column>
-      <el-table-column :label="this.$t('htcommunity.details')">
-        <template slot-scope="scope">
-          <el-button type="text" @click="viewDetails(scope.row)">{{$t("htcommunity.view")}}</el-button>
-        </template>
-      </el-table-column>
-      <el-table-column :label="this.$t('htcommunity.eventExam')">
-        <template slot-scope="scope">
-          <!-- <el-button type="text" @click="viewExams(scope.row)">{{$t("htcommunity.view")}}</el-button> -->
-          <el-button type="text" @click="return false">{{$t("htcommunity.view")}}</el-button>
-        </template>
-      </el-table-column>
-    </el-table>
-
-    <!-- 詳細內容彈出視窗 -->
-    <el-dialog :title="this.$t('htcommunity.details')" :visible.sync="showDetailsModal" width="50%">
-      <p>{{this.$t("htcommunity.activityName")}}: {{ selectedActivity.name }}</p>
-      <p>{{this.$t("htcommunity.schedule")}}: {{ selectedActivity.time }}</p>
-      <p>{{this.$t("htcommunity.administrator")}}: {{ selectedActivity.admin }}</p>
-      <p>原始資料: {{ selectedActivity.originalData }}</p>
-      <span slot="footer" class="dialog-footer">
-        <el-button @click="showDetailsModal = false">{{this.$t("htcommunity.cancel")}}</el-button>
-      </span>
-    </el-dialog>
-
-    <!-- 新增活動彈出視窗 -->
-    <el-dialog :title="this.$t('htcommunity.addEvent')" :visible.sync="showAddModal" width="90%"
-      :close-on-click-modal="false" :close-on-press-escape="false" :close="resetNewActivity" top="6vh">
-      <el-form :model="newActivity" label-width="100px" :rules="rules" ref="newActivity">
-        <div class="form-container">
-          <div class="form-left">
-            <el-form-item :label="this.$t('htcommunity.activityName')" prop="name">
-              <el-input v-model="newActivity.name"></el-input>
-            </el-form-item>
-            <el-form-item :label="this.$t('htcommunity.eventArea')">
-              <el-select v-model="newActivity.region" :placeholder="this.$t('htcommunity.pleaseChoose')">
-                <!-- <el-option v-for="(name,code) in twCityArr" :label="name" :value="code" :key="code"></el-option>        -->
-                <el-option v-for="item in twCityArr" :key="item.code" :label="item.name" :value="item.code" />
-              </el-select>
-            </el-form-item>
-            <el-form-item :label="this.$t('htcommunity.administrator')">
-              <el-input v-model="newActivity.admin"></el-input>
-            </el-form-item>
-            <el-form-item :label="this.$t('htcommunity.schedule')" prop="time">
-              <el-date-picker v-model="newActivity.time" type="datetimerange" start-placeholder="Start"
-                end-placeholder="End"  style="width: 400px;">
-              </el-date-picker>
-            </el-form-item>                   
-          </div>
-          <div class="form-right">
-            <!-- 組別表格 -->           
-            <el-form-item :label="this.$t('htcommunity.group')">
-              <!-- <el-tooltip content="指定評審,請檢查確認為正確醍摩豆ID" placement="bottom" effect="light"> -->
-              <el-tooltip placement="bottom" effect="light">
-                <div slot="content" style="color: red;font-size: 1.2em;">指定評審,請輸入醍摩豆用戶編號,檢查確認為正確醍摩豆ID</div>
-                <i class="el-icon-warning-outline" style="font-size: 2em;"></i>
-              </el-tooltip>
-              <!-- <span>指定評審請輸入<span class="point">手機號碼</span>、<span class="point">醍摩豆用戶編號</span>或<span class="point">電子信箱</span>等資訊進行搜尋</span> -->
-              <el-table :data="newActivity.groups" style="width: 100%;" border :cell-style="{ textAlign: 'center' }"
-                :header-cell-style="{ textAlign: 'center' }" v-loading="groupsLoading">
-                <el-table-column prop="groupName" :label="this.$t('htcommunity.groupName')" min-width="180">
-                  <template slot-scope="scope">
-                    <el-input v-model="scope.row.groupName"></el-input>
-                  </template>
-                </el-table-column>
-                <el-table-column prop="groupJudge" :label="this.$t('htcommunity.designatedReviewer')" min-width="245">
-                  <template slot-scope="scope">                   
-                    <el-input v-model="scope.row.groupJudge" style="width: 200px;margin-right: 10px"></el-input>
-                    <el-button type="primary" icon="el-icon-search" size="mini" @click="checkTmdid(scope)"
-                    style="margin-top: 5px;">檢查</el-button>
-                    <span v-if="scope.row.valid">✔️</span>                  
-
-                  </template>
-                </el-table-column>
-                <el-table-column :label="this.$t('htcommunity.operation')" min-width="60">
-                  <template slot-scope="scope">
-                    <el-button type="text" @click="removeGroup(scope)">{{$t("htcommunity.delete")}}</el-button>
-                  </template>
-                </el-table-column>
-              </el-table>
-              <el-button type="primary" class="btn-add" icon="el-icon-plus" @click="addGroup"></el-button>
-            </el-form-item>
-            
-          </div>
-          
-        </div>
-        <div>
-          <p style="font-size: medium; margin-bottom: 10px;">{{this.$t("htcommunity.importantTimetable")}}:</p>
-            <el-checkbox v-model="newActivity.requireOrderCompletion">{{this.$t("htcommunity.activitiesMustBeCompletedInOrder")}}</el-checkbox>
-            <!-- 計畫內容表格 -->
-            <el-table :data="newActivity.planContent" style="width: 100%;" border :cell-style="{ textAlign: 'center' }"
-              :header-cell-style="{ textAlign: 'center' }" v-loading="scheduleLoading">
-              <el-table-column prop="step" label="step" min-width="30px">
-                <template slot-scope="scope">
-                  {{ scope.$index + 1 }}
-                </template>
-              </el-table-column>
-              <el-table-column prop="schedule" :label="this.$t('htcommunity.schedule')" min-width="235px">
-                <template slot-scope="scope">                  
-                  <el-date-picker v-model="scope.row.schedule" type="datetimerange" start-placeholder="Start"
-                    end-placeholder="End"  style="width: 100%;" :disabled="scheduleDisabled(scope, '')">
-                  </el-date-picker>
-                  <span style="color:red;font-size: small;" v-show="scope.row.progress==='going'">進行中時程不可編輯</span>
-                </template>
-              </el-table-column>
-              <el-table-column prop="name" :label="this.$t('htcommunity.stage')" min-width="100px">
-                <template slot-scope="scope">                  
-                  <el-input v-model="scope.row.name" :disabled="scheduleDisabled(scope, '')"></el-input>
-                </template>
-              </el-table-column>              
-              <el-table-column prop="location" :label="this.$t('htcommunity.activityPlace')" min-width="100px">
-                <template slot-scope="scope">                  
-                  <el-input v-model="scope.row.location" :disabled="scheduleDisabled(scope, '')"></el-input>
-                </template>
-              </el-table-column>
-              <el-table-column prop="description" :label="this.$t('htcommunity.description')" min-width="150px">
-                <template slot-scope="scope">                  
-                  <el-input  type="textarea" autosize v-model="scope.row.description" :disabled="scheduleDisabled(scope, '')"></el-input>
-                </template>
-              </el-table-column>
-              <el-table-column prop="blobs" :label="this.$t('htcommunity.fileUrl')" min-width="150px">
-                <template slot-scope="scope">                  
-                  <el-input type="textarea" autosize v-model="scope.row.blobs" :disabled="scheduleDisabled(scope, '')"></el-input>
-                </template>
-              </el-table-column>
-              <el-table-column prop="type" :label="this.$t('htcommunity.signup')" min-width="33px">
+    <div id="app" class="app-container">
+        <el-button type="primary" class="btn-new" @click="newEvent">{{this.$t("htcommunity.addEvent")}}</el-button>
+        <el-table :data="activities" class="table-main" border v-loading="mainLoading">
+            <el-table-column prop="index" :label="this.$t('htcommunity.serialNumber')" min-width="50"></el-table-column>
+            <el-table-column prop="name" :label="this.$t('htcommunity.activityName')"></el-table-column>
+            <el-table-column prop="time" :label="this.$t('htcommunity.schedule')" min-width="150"></el-table-column>
+            <el-table-column prop="admin" :label="this.$t('htcommunity.administrator')"></el-table-column>
+            <el-table-column :label="this.$t('htcommunity.details')">
                 <template slot-scope="scope">
-                  <el-checkbox v-model="scope.row.type" :disabled="scheduleDisabled(scope, '')"></el-checkbox>                 
+                    <el-button type="text" @click="viewDetails(scope.row)">{{$t("htcommunity.view")}}</el-button>
                 </template>
-              </el-table-column>
-              <el-table-column prop="examType" :label="this.$t('htcommunity.Eventlayout')" min-width="50px">
+            </el-table-column>
+            <el-table-column :label="this.$t('htcommunity.joinStatus')">
                 <template slot-scope="scope">
-                  <el-checkbox v-model="scope.row.examType" :disabled="scheduleDisabled(scope, 'examType')"></el-checkbox>
+                    <el-button type="text" @click="viewJoinStatus(scope.row)">{{$t("htcommunity.view")}}</el-button>
                 </template>
-              </el-table-column>
-              <el-table-column prop="evaluation" :label="this.$t('htcommunity.examCanBeChecked')" min-width="60px">
+            </el-table-column>
+            <el-table-column :label="this.$t('htcommunity.eventExam')">
                 <template slot-scope="scope">
-                  <el-checkbox v-model="scope.row.evaluation" :disabled="scheduleDisabled(scope, 'evaluation')"></el-checkbox>
+                    <!-- <el-button type="text" @click="viewExams(scope.row)">{{$t("htcommunity.view")}}</el-button> -->
+                    <el-button type="text" @click="return false">{{$t("htcommunity.view")}}</el-button>
                 </template>
-              </el-table-column>
-              <el-table-column :label="this.$t('htcommunity.operation')" min-width="33px">
-                <template slot-scope="scope">
-                  <el-button type="text" @click="removePlanContent(scope)">{{$t("htcommunity.delete")}}</el-button>
-                </template>
-              </el-table-column>
+            </el-table-column>
+        </el-table>
+
+        <!-- 詳細內容彈出視窗 -->
+        <el-dialog :title="this.$t('htcommunity.details')" :visible.sync="showDetailsModal" width="50%">
+            <p>{{this.$t("htcommunity.activityName")}}: {{ selectedActivity.name }}</p>
+            <p>{{this.$t("htcommunity.schedule")}}: {{ selectedActivity.time }}</p>
+            <p>{{this.$t("htcommunity.administrator")}}: {{ selectedActivity.admin }}</p>
+            <p>原始資料: {{ selectedActivity.originalData }}</p>
+            <span slot="footer" class="dialog-footer">
+                <el-button @click="showDetailsModal = false">{{this.$t("htcommunity.cancel")}}</el-button>
+            </span>
+        </el-dialog>
+
+        <!-- 新增活動彈出視窗 -->
+        <el-dialog :title="this.$t('htcommunity.addEvent')" :visible.sync="showAddModal" width="90%"
+                   :close-on-click-modal="false" :close-on-press-escape="false" :close="resetNewActivity" top="6vh">
+            <el-form :model="newActivity" label-width="100px" :rules="rules" ref="newActivity">
+                <div class="form-container">
+                    <div class="form-left">
+                        <el-form-item :label="this.$t('htcommunity.activityName')" prop="name">
+                            <el-input v-model="newActivity.name"></el-input>
+                        </el-form-item>
+                        <el-form-item :label="this.$t('htcommunity.eventArea')">
+                            <el-select v-model="newActivity.region" :placeholder="this.$t('htcommunity.pleaseChoose')">
+                                <!-- <el-option v-for="(name,code) in twCityArr" :label="name" :value="code" :key="code"></el-option>        -->
+                                <el-option v-for="item in twCityArr" :key="item.code" :label="item.name" :value="item.code" />
+                            </el-select>
+                        </el-form-item>
+                        <el-form-item :label="this.$t('htcommunity.administrator')">
+                            <el-input v-model="newActivity.admin"></el-input>
+                        </el-form-item>
+                        <el-form-item :label="this.$t('htcommunity.schedule')" prop="time">
+                            <el-date-picker v-model="newActivity.time" type="datetimerange" start-placeholder="Start"
+                                            end-placeholder="End" style="width: 400px;">
+                            </el-date-picker>
+                        </el-form-item>
+                    </div>
+                    <div class="form-right">
+                        <!-- 組別表格 -->
+                        <el-form-item :label="this.$t('htcommunity.group')">
+                            <!-- <el-tooltip content="指定評審,請檢查確認為正確醍摩豆ID" placement="bottom" effect="light"> -->
+                            <el-tooltip placement="bottom" effect="light">
+                                <div slot="content" style="color: red;font-size: 1.2em;">指定評審,請輸入醍摩豆用戶編號,檢查確認為正確醍摩豆ID</div>
+                                <i class="el-icon-warning-outline" style="font-size: 2em;"></i>
+                            </el-tooltip>
+                            <!-- <span>指定評審請輸入<span class="point">手機號碼</span>、<span class="point">醍摩豆用戶編號</span>或<span class="point">電子信箱</span>等資訊進行搜尋</span> -->
+                            <el-table :data="newActivity.groups" style="width: 100%;" border :cell-style="{ textAlign: 'center' }"
+                                      :header-cell-style="{ textAlign: 'center' }" v-loading="groupsLoading">
+                                <el-table-column prop="groupName" :label="this.$t('htcommunity.groupName')" min-width="180">
+                                    <template slot-scope="scope">
+                                        <el-input v-model="scope.row.groupName"></el-input>
+                                    </template>
+                                </el-table-column>
+                                <el-table-column prop="groupJudge" :label="this.$t('htcommunity.designatedReviewer')" min-width="245">
+                                    <template slot-scope="scope">
+                                        <el-input v-model="scope.row.groupJudge" style="width: 200px;margin-right: 10px"></el-input>
+                                        <el-button type="primary" icon="el-icon-search" size="mini" @click="checkTmdid(scope)"
+                                                   style="margin-top: 5px;">檢查</el-button>
+                                        <span v-if="scope.row.valid">✔️</span>
+
+                                    </template>
+                                </el-table-column>
+                                <el-table-column :label="this.$t('htcommunity.operation')" min-width="60">
+                                    <template slot-scope="scope">
+                                        <el-button type="text" @click="removeGroup(scope)">{{$t("htcommunity.delete")}}</el-button>
+                                    </template>
+                                </el-table-column>
+                            </el-table>
+                            <el-button type="primary" class="btn-add" icon="el-icon-plus" @click="addGroup"></el-button>
+                        </el-form-item>
+
+                    </div>
+
+                </div>
+                <div>
+                    <p style="font-size: medium; margin-bottom: 10px;">{{this.$t("htcommunity.importantTimetable")}}:</p>
+                    <el-checkbox v-model="newActivity.requireOrderCompletion">{{this.$t("htcommunity.activitiesMustBeCompletedInOrder")}}</el-checkbox>
+                    <!-- 計畫內容表格 -->
+                    <el-table :data="newActivity.planContent" style="width: 100%;" border :cell-style="{ textAlign: 'center' }"
+                              :header-cell-style="{ textAlign: 'center' }" v-loading="scheduleLoading">
+                        <el-table-column prop="step" label="step" min-width="30px">
+                            <template slot-scope="scope">
+                                {{ scope.$index + 1 }}
+                            </template>
+                        </el-table-column>
+                        <el-table-column prop="schedule" :label="this.$t('htcommunity.schedule')" min-width="235px">
+                            <template slot-scope="scope">
+                                <el-date-picker v-model="scope.row.schedule" type="datetimerange" start-placeholder="Start"
+                                                end-placeholder="End" style="width: 100%;" :disabled="scheduleDisabled(scope, '')">
+                                </el-date-picker>
+                                <span style="color:red;font-size: small;" v-show="scope.row.progress==='going'">進行中時程不可編輯</span>
+                            </template>
+                        </el-table-column>
+                        <el-table-column prop="name" :label="this.$t('htcommunity.stage')" min-width="100px">
+                            <template slot-scope="scope">
+                                <el-input v-model="scope.row.name" :disabled="scheduleDisabled(scope, '')"></el-input>
+                            </template>
+                        </el-table-column>
+                        <el-table-column prop="location" :label="this.$t('htcommunity.activityPlace')" min-width="100px">
+                            <template slot-scope="scope">
+                                <el-input v-model="scope.row.location" :disabled="scheduleDisabled(scope, '')"></el-input>
+                            </template>
+                        </el-table-column>
+                        <el-table-column prop="description" :label="this.$t('htcommunity.description')" min-width="150px">
+                            <template slot-scope="scope">
+                                <el-input type="textarea" autosize v-model="scope.row.description" :disabled="scheduleDisabled(scope, '')"></el-input>
+                            </template>
+                        </el-table-column>
+                        <el-table-column prop="blobs" :label="this.$t('htcommunity.fileUrl')" min-width="150px">
+                            <template slot-scope="scope">
+                                <el-input type="textarea" autosize v-model="scope.row.blobs" :disabled="scheduleDisabled(scope, '')"></el-input>
+                            </template>
+                        </el-table-column>
+                        <el-table-column prop="type" :label="this.$t('htcommunity.Eventlayout')" min-width="100px">
+                            <template slot-scope="scope">
+                                <el-select v-model="scope.row.type" placeholder="Please select" :disabled="scheduleDisabled(scope, '')">
+                                    <el-option v-for="item in eventScheduleTypes" :key="item.code" :label="item.name" :value="item.code">
+                                    </el-option>
+                                </el-select>
+                                <!--<el-checkbox v-model="scope.row.type" :disabled="scheduleDisabled(scope, '')"></el-checkbox>-->
+                            </template>
+                        </el-table-column>
+                        <el-table-column prop="examType" :label="this.$t('htcommunity.examType')" min-width="50px">
+                            <template slot-scope="scope">
+                                <el-checkbox v-model="scope.row.examType" :disabled="scheduleDisabled(scope, 'examType')"></el-checkbox>
+                            </template>
+                        </el-table-column>
+                        <el-table-column prop="evaluation" :label="this.$t('htcommunity.examCanBeChecked')" min-width="60px">
+                            <template slot-scope="scope">
+                                <el-checkbox v-model="scope.row.evaluation" :disabled="scheduleDisabled(scope, 'evaluation')"></el-checkbox>
+                            </template>
+                        </el-table-column>
+                        <el-table-column :label="this.$t('htcommunity.operation')" min-width="33px">
+                            <template slot-scope="scope">
+                                <el-button type="text" @click="removePlanContent(scope)">{{$t("htcommunity.delete")}}</el-button>
+                            </template>
+                        </el-table-column>
+                    </el-table>
+                    <el-button type="primary" class="btn-add" icon="el-icon-plus" @click="addPlanContent"></el-button>
+                </div>
+            </el-form>
+            <span slot="footer" class="dialog-footer">
+                <el-button @click="cancelUpsert">{{this.$t("htcommunity.cancel")}}</el-button>
+                <el-button type="primary" @click="saveNewEvent">{{this.$t("htcommunity.confirm")}}</el-button>
+            </span>
+        </el-dialog>
+        <!-- 報名狀況彈出視窗 -->
+        <el-dialog :title="this.$t('htcommunity.joinStatus')" :visible.sync="showJoinModal" width="70%">
+            <el-button type="primary" class="btn-new" size="small" style="margin-top: 0" @click="exportExcel('teacherCourse')">{{this.$t("htcommunity.export")}}</el-button>
+            <el-table :data="joinTeacherCourseArr" class="table-main" border v-loading="teacherCourseLoading" @sort-change="onSortChangeCourse">
+                <el-table-column prop="jointGroupName" :label="this.$t('htcommunity.jointGroup')" sortable="custom" min-width="50"></el-table-column>
+                <el-table-column prop="schoolName" :label="this.$t('htcommunity.schoolName')" sortable="custom" min-width="50"></el-table-column>
+                <el-table-column prop="creatorName" :label="this.$t('htcommunity.teacherName')" sortable="custom" min-width="50"></el-table-column>
+                <el-table-column prop="creatorId" :label="this.$t('htcommunity.tmid')" min-width="50"></el-table-column>
+                <el-table-column prop="courseName" :label="this.$t('htcommunity.course')" min-width="50"></el-table-column>
+                <el-table-column prop="groupName" :label="this.$t('htcommunity.courseGroup')" min-width="50"></el-table-column>
             </el-table>
-            <el-button type="primary" class="btn-add" icon="el-icon-plus" @click="addPlanContent"></el-button>
-          </div>
-      </el-form>
-      <span slot="footer" class="dialog-footer">
-        <el-button @click="cancelUpsert">{{this.$t("htcommunity.cancel")}}</el-button>
-        <el-button type="primary" @click="saveNewEvent">{{this.$t("htcommunity.confirm")}}</el-button>
-      </span>
-    </el-dialog>
-  </div>
+        </el-dialog>
+    </div>
 </template>
 
 <script>
 //import twTCitys from '@/static/twJsonT.js'
 import option_gl from '@/static/region_gl.json'
 import { forEach } from 'jszip';
+import excel from '@/utils/excel.js'
 export default {
   data() {
     return {
@@ -183,7 +205,12 @@ export default {
         //   time: "2024-07-01~2024-07-31",
         //   admin: "管理員A"
         // }
-      ],
+        ],
+        eventScheduleTypes: [
+            { code: 'join', name: this.$t("htcommunity.signup") },
+            { code: 'exam', name: this.$t("htcommunity.exam") },
+            { code: 'other', name: this.$t("htcommunity.other") }
+        ],
       showDetailsModal: false,
       showAddModal: false,
       selectedActivity: {},
@@ -212,17 +239,21 @@ export default {
             required: true, message: '請輸入時程'
           }
         ],
-      }
+      },
+      showJoinModal: false, //報名狀況子視窗
+      teacherCourseLoading: false,
+      joinTeacherCourseArr: [],
     };
   },
   methods: {
     // 時程編輯的啟用及關閉  以及  動態切換活動布置及評量可重複啟用及關閉
     scheduleDisabled(scope, column) {
+          console.log('scheduleDisabled scope:', scope)
       if (scope.row.progress === "going") {
         return true;
       } else {
         if (column === 'examType' || column === 'evaluation') {
-          if (scope.row.type) {
+          if (scope.row.type != 'exam') {
             scope.row.examType = false;
             scope.row.evaluation = false;
             return true;
@@ -390,19 +421,107 @@ export default {
           step: item.orderby,
           schedule: [this.$jsFn.secondTimeFormat(item.startTime), this.$jsFn.secondTimeFormat(item.endTime)],
           name: item.name,
-          type: item.type === "join" ? true : false,
+          type: item.type,
           examType: item.examType === "regular" ? true : false,
           evaluation: item.examOverwrite,
           progress: item.progress,
           description: item.description,
           location: item.location,
           blobs: item.blobs === null ? "" : item.blobs[0].blob,
-        }        
+        }
+          console.log('activity.originalData.schedule', schedule);
         this.newActivity.planContent.push(schedule);
       });
 
       // #endregion
     },
+    //查看報名狀況
+    viewJoinStatus(activity) {
+        this.showJoinModal = true;
+        this.getJoinList(activity);
+    },
+    // 取得報名老師資料
+    getJoinList(activity) {
+        this.teacherCourseLoading = true;
+        //先清除列表
+        this.joinTeacherCourseArr.splice(0, this.joinTeacherCourseArr.length);
+          try {
+              let param = {
+                  jointEventId: activity.id
+              };
+              //  取得報名老師資料API
+              this.$api.htcommunity.jointCourseFind(param).then(
+                  res => {
+                      if (res) {
+                          let index = 1;
+                          console.log('res.data', res.data)
+                          res.data.forEach(item => {
+                              //群組名
+                              let groupName = '';
+                              activity.originalData.groups.forEach((g) => {
+                                  if (g.id == item.jointGroupId) {
+                                      groupName = g.name;
+                                  }
+                              });
+                              //課程
+                              let courseId = '';
+                              let courseName = '';
+                              let courseGroupId = '';
+                              let courseGroupName = '';
+                              item.courseLists.forEach((c) => {
+                                  courseId = c.courseId;
+                                  courseName = c.courseName;
+                                  c.groupLists.forEach((g) => {
+                                      courseGroupId = g.id;
+                                      courseGroupName = g.name;
+                                      //資料生成
+                                      let jointCourseRow = {
+                                          id: item.id,
+                                          index: index,
+                                          jointGroupId: item.jointGroupId,
+                                          jointGroupName: groupName,
+                                          schoolId: item.schoolId,
+                                          schoolName: item.schoolName,
+                                          creatorId: item.creatorId,
+                                          creatorName: item.creatorName,
+                                          courseId: courseId,
+                                          courseName: courseName,
+                                          groupId: courseGroupId,
+                                          groupName: courseGroupName,
+                                      }
+                                      this.joinTeacherCourseArr.push(jointCourseRow);
+                                      index++;
+                                  });
+                              });
+                          });
+                      } else {
+
+                      }
+                  },
+                  err => { console.log("API error : " + err); }
+              )
+          } catch (error) {
+              console.log("API error : " + error);
+          } finally {
+              this.teacherCourseLoading = false;
+          }
+    },
+    //匯出Excel資料
+      exportExcel(type) {
+        //老師報名資料
+          if (type == 'teacherCourse') {
+              let titles = [this.$t('htcommunity.jointGroup'), this.$t('htcommunity.schoolName'), this.$t('htcommunity.teacherName'), this.$t('htcommunity.tmid'), this.$t('htcommunity.course'), this.$t('htcommunity.courseGroup')];
+              let keys = ['jointGroupName', 'schoolName', 'creatorName', 'creatorId', 'courseName', 'groupName'];
+              const params = {
+                  title: titles,
+                  key: keys,
+                  data: this.joinTeacherCourseArr,
+                  autoWidth: true,
+                  filename: this.$t('htcommunity.joinStatus')
+              }
+              excel.export_array_to_excel(params)
+          }
+    },
     //查看活動評量
     viewExams(activity) {
       // let params = {
@@ -489,9 +608,11 @@ export default {
                         ]
                       };
                       this.newActivity.planContent.forEach(item => {                        
-                        let pType = item.type === true ? "join" : "exam";
-                        //let pExamType = item.type === true ? "" : (item.examType === true ? "regular" : "custom");
-                        let pExamType = item.examType === true ? "regular" : "custom";
+                        let pType = item.type;
+                        let pExamType = null;
+                        if (item.type == 'exam') {
+                            pExamType = item.examType === true ? "regular" : "custom";
+                        }
                         let pStartTime = new Date(item.schedule[0]);
                         let pEndTime = new Date(item.schedule[1]);
                         let upItem = {
@@ -650,7 +771,40 @@ export default {
         }       
       }).catch(() => {       
       });       
-    }
+    },
+    // 排序操作
+    onSortChangeCourse(data) {
+        let order = data.order // 当前排序方式 升序、降序、正常
+        let key = data.prop // 当前排序依据
+        switch (order) {
+            case 'ascending':
+                if (key == 'jointGroupName' || key == 'creatorName' || key == 'schoolName') {
+                    this.joinTeacherCourseArr = this.joinTeacherCourseArr.sort((a, b) => {
+                        return a[key].toLowerCase().localeCompare(b[key].toLowerCase());
+                    })
+                }
+                else {
+                    this.joinTeacherCourseArr = this.joinTeacherCourseArr.sort((a, b) => {
+                        return Number(a[key]) - Number(b[key])
+                    })
+                }
+                break
+            case 'descending':
+                if (key == 'jointGroupName' || key == 'creatorName' || key == 'schoolName') {
+                    this.joinTeacherCourseArr = this.joinTeacherCourseArr.sort((a, b) => {
+                        return b[key].toLowerCase().localeCompare(a[key].toLowerCase());
+                    })
+                }
+                else {
+                    this.joinTeacherCourseArr = this.joinTeacherCourseArr.sort((a, b) => {
+                        return Number(b[key]) - Number(a[key])
+                    })
+                }
+                break
+            default:
+                break
+        }
+    },
   },
   created () {
     this.twCityArr = option_gl[0].children;      

Plik diff jest za duży
+ 5 - 3
TEAMModelOS/Controllers/Client/HiTeachController.cs


+ 96 - 47
TEAMModelOS/Controllers/Teacher/JointEventController.cs

@@ -22,6 +22,7 @@ using System.Linq;
 using TEAMModelOS.SDK.Models.Service;
 using Azure.Messaging.ServiceBus;
 using Azure.Storage.Sas;
+using Microsoft.Azure.Cosmos.Linq;
 
 namespace TEAMModelOS.Controllers.Common
 {
@@ -502,6 +503,7 @@ namespace TEAMModelOS.Controllers.Common
                 StringBuilder stringBuilder = new($"SELECT * FROM c WHERE 1=1 ");
                 string jointEventId = (request.TryGetProperty("jointEventId", out JsonElement _jointEventId)) ? _jointEventId.ToString() : string.Empty;
                 string jointGroupId = (request.TryGetProperty("jointGroupId", out JsonElement _jointGroupId)) ? _jointGroupId.ToString() : string.Empty;
+                string type = (request.TryGetProperty("type", out JsonElement _type)) ? _type.ToString() : "regular"; //預設取得報名名單
                 if (string.IsNullOrWhiteSpace(jointEventId)) return BadRequest();
                 //取得活動
                 JointEvent jointEvent = await client.GetContainer(Constant.TEAMModelOS, "Teacher").ReadItemAsync<JointEvent>(jointEventId, new PartitionKey("JointEvent"));
@@ -520,6 +522,14 @@ namespace TEAMModelOS.Controllers.Common
                 {
                     stringBuilder.Append($" AND c.creatorId = '{creatorId}' ");
                 }
+                if(type.Equals("regular"))
+                {
+                    stringBuilder.Append($" AND (c.type = 'regular' OR NOT IS_DEFINED(c.type) OR IS_NULL(c.type)) ");
+                }
+                else
+                {
+                    stringBuilder.Append($" AND c.type = '{type}' ");
+                }
                 await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "Teacher").GetItemQueryStreamIteratorSql(queryText: stringBuilder.ToString(), requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("JointCourse") }))
                 {
                     using var json = await JsonDocument.ParseAsync(item.Content);
@@ -534,7 +544,7 @@ namespace TEAMModelOS.Controllers.Common
                                 {
                                     foreach(JointEventSchedule schedule in jointEvent.schedule)
                                     {
-                                        string scheduleStatus = await GetGroupFinishJointSchedule(jointEventId, jointGroupId, group.id, schedule);
+                                        string scheduleStatus = await GetGroupFinishJointSchedule(jointEventGroupDto.jointEventId, jointEventGroupDto.jointGroupId, group.id, schedule);
                                         group.schedule.Add(new {id = schedule.id, name = schedule.name, status = scheduleStatus });
                                     }
                                 }
@@ -568,6 +578,8 @@ namespace TEAMModelOS.Controllers.Common
                 string creatorId = (request.TryGetProperty("creatorId", out JsonElement _creatorId)) ? _creatorId.ToString() : string.Empty;
                 string creatorName = (request.TryGetProperty("creatorName", out JsonElement _creatorName)) ? _creatorName.ToString() : string.Empty;
                 string creatorEmail = (request.TryGetProperty("creatorEmail", out JsonElement _creatorEmail)) ? _creatorEmail.ToString() : string.Empty;
+                string schoolId = (request.TryGetProperty("schoolId", out JsonElement _schoolId)) ? _schoolId.ToString() : string.Empty;
+                string schoolName = (request.TryGetProperty("schoolName", out JsonElement _schoolName)) ? _schoolName.ToString() : string.Empty;
                 string scope = (request.TryGetProperty("scope", out JsonElement _scope)) ? _scope.ToString() : string.Empty;
                 List<JointEventGroupBase.JointEventGroupCourse> courseLists = (request.TryGetProperty("courseLists", out JsonElement _courseLists)) ? _courseLists.ToObject<List<JointEventGroupBase.JointEventGroupCourse>>() : null;
                 if (string.IsNullOrWhiteSpace(jointEventId) || string.IsNullOrWhiteSpace(jointGroupId) || string.IsNullOrWhiteSpace(creatorId) || courseLists == null || string.IsNullOrWhiteSpace(scope))
@@ -608,9 +620,12 @@ namespace TEAMModelOS.Controllers.Common
                         jointCourse.creatorId = creatorId;
                         jointCourse.creatorName = creatorName;
                         jointCourse.creatorEmail = creatorEmail;
+                        jointCourse.schoolId = schoolId;
+                        jointCourse.schoolName = schoolName;
                     }
                     //修改
                     jointCourse.courseLists = courseLists;
+                    jointCourse.type = "regular"; //教師報名名單
                     await client.GetContainer(Constant.TEAMModelOS, "Teacher").UpsertItemAsync<JointEventGroupDb>(jointCourse, new PartitionKey("JointCourse"));
                 }
 
@@ -871,34 +886,39 @@ namespace TEAMModelOS.Controllers.Common
                 {
                     foreach(JointExam jointExam in jointExamInfo)
                     {
-                        if (jointExam.examType.Equals("regular")) //一般競賽 => 編輯stuLists、classes
+                        List<JointEventGroupDb> stuLists = new List<JointEventGroupDb>();
+                        List<JointEventClassDb> classes = new List<JointEventClassDb>();
+                        if (jointExam.examType.Equals("regular")) //一般競賽
                         {
-                            //private (stuLists)
-                            List<JointEventGroupDb> stuLists = jointEventGroup.Where(g => g.jointEventId.Equals(jointExam.jointEventId) && g.jointGroupId.Equals(jointExam.jointGroupId)).ToList();
-                            if (stuLists.Count > 0)
+                            stuLists = jointEventGroup.Where(g => g.jointEventId.Equals(jointExam.jointEventId) && g.jointGroupId.Equals(jointExam.jointGroupId) && (g.type == null || g.type.Equals("regular"))).ToList();
+                            classes = jointEventClass.Where(g => g.jointEventId.Equals(jointExam.jointEventId) && g.jointGroupId.Equals(jointExam.jointGroupId) && (g.type == null || g.type.Equals("regular"))).ToList();
+                        }
+                        else if(jointExam.examType.Equals("custom")) //決賽
+                        {
+                            stuLists = jointEventGroup.Where(g => g.jointEventId.Equals(jointExam.jointEventId) && g.jointGroupId.Equals(jointExam.jointGroupId) && (g.type.Equals("custom"))).ToList();
+                            classes = jointEventClass.Where(g => g.jointEventId.Equals(jointExam.jointEventId) && g.jointGroupId.Equals(jointExam.jointGroupId) && (g.type.Equals("custom"))).ToList();
+                        }
+                        //private (stuLists)
+                        if (stuLists.Count > 0)
+                        {
+                            foreach (JointEventGroupDb stu in stuLists)
                             {
-                                foreach (JointEventGroupDb stu in stuLists)
-                                {
-                                    JointEventGroupBase stuListRow = new JointEventGroupBase();
-                                    stuListRow.scope = "private";
-                                    stuListRow.creatorId = stu.creatorId;
-                                    stuListRow.creatorName = stu.creatorName;
-                                    stuListRow.courseLists = stu.courseLists;
-
-                                    jointExam.stuLists.Add(stuListRow);
-                                }
+                                JointEventGroupBase stuListRow = new JointEventGroupBase();
+                                stuListRow.scope = "private";
+                                stuListRow.creatorId = stu.creatorId;
+                                stuListRow.creatorName = stu.creatorName;
+                                stuListRow.courseLists = stu.courseLists;
+                                jointExam.stuLists.Add(stuListRow);
                             }
-                            //school (classes)
-                            List<JointEventClassDb> classes = jointEventClass.Where(g => g.jointEventId.Equals(jointExam.jointEventId) && g.jointGroupId.Equals(jointExam.jointGroupId)).ToList();
-                            if (classes.Count > 0)
+                        }
+                        //school (classes)
+                        if (classes.Count > 0)
+                        {
+                            foreach (JointEventClassDb clas in classes)
                             {
-
-                                foreach (JointEventClassDb clas in classes)
-                                {
-                                    JointEventClassBase classRow = new JointEventClassBase();
-                                    //樣式未定
-                                    jointExam.classes.Add(classRow);
-                                }
+                                JointEventClassBase classRow = new JointEventClassBase();
+                                //樣式未定
+                                jointExam.classes.Add(classRow);
                             }
                         }
                     }
@@ -938,6 +958,7 @@ namespace TEAMModelOS.Controllers.Common
                 string creatorId = (request.TryGetProperty("creatorId", out JsonElement _creatorId)) ? _creatorId.ToString() : string.Empty;
                 string name = (request.TryGetProperty("name", out JsonElement _name)) ? _name.ToString() : string.Empty;
                 string source = (request.TryGetProperty("source", out JsonElement _source)) ? _source.ToString() : string.Empty;
+                string scope = (request.TryGetProperty("scope", out JsonElement _scope)) ? _scope.ToString() : "private";
                 List<PaperSimple> papers = (request.TryGetProperty("papers", out JsonElement _papers)) ? _papers.ToObject<List<PaperSimple>>() : null;
                 List<JointEventGroupBase> stuLists = (request.TryGetProperty("stuLists", out JsonElement _stuLists)) ? _stuLists.ToObject<List<JointEventGroupBase>>() : null;
                 List<JointEventClassBase> classes = (request.TryGetProperty("classes", out JsonElement _classes)) ? _classes.ToObject<List<JointEventClassBase>>() : null;
@@ -992,16 +1013,16 @@ namespace TEAMModelOS.Controllers.Common
                         return Ok(new { errCode = "9", err = "JointExam is going, can't be changed." });
                     }
                     if (!string.IsNullOrWhiteSpace(name)) jointExam.name = name;
-                    if (string.IsNullOrWhiteSpace(source)) jointExam.source = source;
-                    if (jointEventSchedule.examType.Equals("custom")) //冠軍賽,要填classes或stuLists擇一必須
-                    {
-                        if (classes == null || stuLists == null)
-                        {
-                            return Ok(new { errCode = "8", err = "Invalid classes or stuLists." });
-                        }
-                        if (classes != null) jointExam.classes = classes;
-                        if (stuLists != null) jointExam.stuLists = stuLists;
-                    }
+                    if (!string.IsNullOrWhiteSpace(source)) jointExam.source = source;
+                    //if (jointEventSchedule.examType.Equals("custom")) //賽,要填classes或stuLists擇一必須
+                    //{
+                    //    if (classes == null || stuLists == null)
+                    //    {
+                    //        return Ok(new { errCode = "8", err = "Invalid classes or stuLists." });
+                    //    }
+                    //    if (classes != null) jointExam.classes = classes;
+                    //    if (stuLists != null) jointExam.stuLists = stuLists;
+                    //}
                     if (papers != null) jointExam.papers = papers;
                 }
                 else //新建
@@ -1010,15 +1031,15 @@ namespace TEAMModelOS.Controllers.Common
                     if (string.IsNullOrWhiteSpace(creatorId)) return Ok(new { errCode = "8", err = "Invalid creatorId." });
                     if (string.IsNullOrWhiteSpace(name)) return Ok(new { errCode = "9", err = "Invalid name." });
                     if (string.IsNullOrWhiteSpace(source)) return Ok(new { errCode = "10", err = "Invalid source." });
-                    if (jointEventSchedule.examType.Equals("custom")) //冠軍賽,要填classes或stuLists擇一必須
-                    {
-                        if (classes == null || stuLists == null)
-                        {
-                            return Ok(new { errCode = "8", err = "Invalid classes or stuLists." });
-                        }
-                        if (classes != null) jointExam.classes = classes;
-                        if (stuLists != null) jointExam.stuLists = stuLists;
-                    }
+                    //if (jointEventSchedule.examType.Equals("custom")) //賽,要填classes或stuLists擇一必須
+                    //{
+                    //    if (classes == null || stuLists == null)
+                    //    {
+                    //        return Ok(new { errCode = "8", err = "Invalid classes or stuLists." });
+                    //    }
+                    //    if (classes != null) jointExam.classes = classes;
+                    //    if (stuLists != null) jointExam.stuLists = stuLists;
+                    //}
                     jointExam.pk = "JointExam";
                     jointExam.code = "JointExam";
                     jointExam.id = jointExamId;
@@ -1030,6 +1051,7 @@ namespace TEAMModelOS.Controllers.Common
                     jointExam.source = source;
                     jointExam.papers = papers;
                     jointExam.createTime = now;
+                    jointExam.scope = scope;
                 }
                 //共通
                 jointExam.examType = jointEventSchedule.examType;
@@ -1060,7 +1082,7 @@ namespace TEAMModelOS.Controllers.Common
             {
                 //報名
                 case "join":
-                    StringBuilder stringBuilder = new($"SELECT DISTINCT ccg.id FROM c JOIN cc IN c.courseLists JOIN ccg IN cc.groupLists WHERE c.jointEventId = '{jointEventId}' AND c.jointGroupId = '{jointGroupId}' AND ccg.id = '{groupId}' ");
+                    StringBuilder stringBuilder = new($"SELECT DISTINCT ccg.id FROM c JOIN cc IN c.courseLists JOIN ccg IN cc.groupLists WHERE c.jointEventId = '{jointEventId}' AND c.jointGroupId = '{jointGroupId}' AND (c.type = 'regular' OR NOT IS_DEFINED(c.type) OR IS_NULL(c.type)) AND ccg.id = '{groupId}' ");
                     await foreach (var item in client.GetContainer(Constant.TEAMModelOS, container).GetItemQueryStreamIteratorSql(queryText: stringBuilder.ToString(), requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("JointCourse") }))
                     {
                         using var json = await JsonDocument.ParseAsync(item.Content);
@@ -1078,6 +1100,28 @@ namespace TEAMModelOS.Controllers.Common
                     break;
                 //競賽
                 case "exam":
+                    //取得老師報名課程或決賽老師課程
+                    List<JointEventGroupDb> jointEventGroup = new List<JointEventGroupDb>(); //個人課程
+                    StringBuilder stringBuilderEventGroup = new($"SELECT * FROM c WHERE c.jointEventId = '{jointEventId}' AND c.jointGroupId = '{jointGroupId}' ");
+                    if (schedule.examType.Equals("regular")) //熱身賽
+                    {
+                        stringBuilderEventGroup.Append($" AND (c.type = 'regular' OR NOT IS_DEFINED(c.type) OR IS_NULL(c.type)) ");
+                    }
+                    else if(schedule.examType.Equals("custom")) //決賽
+                    {
+                        stringBuilderEventGroup.Append($" AND c.type = 'custom' AND c.jointScheduleId = '{schedule.id}' ");
+                    }
+                    await foreach (var item in client.GetContainer(Constant.TEAMModelOS, Constant.Teacher).GetItemQueryStreamIteratorSql(queryText: stringBuilderEventGroup.ToString(), requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("JointCourse") }))
+                    {
+                        using var json = await JsonDocument.ParseAsync(item.Content);
+                        if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
+                        {
+                            foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
+                            {
+                                jointEventGroup.Add(obj.ToObject<JointEventGroupDb>());
+                            }
+                        }
+                    }
                     //取得本Schedule的所有JointExam
                     List<string> jointExamIdList = new List<string>();
                     StringBuilder stringBuilderJointExam = new($"SELECT DISTINCT VALUE c.id FROM c WHERE c.jointEventId = '{jointEventId}' AND c.jointGroupId = '{jointGroupId}' AND c.jointScheduleId = '{schedule.id}' ");
@@ -1119,7 +1163,11 @@ namespace TEAMModelOS.Controllers.Common
                         }
                     }
                     //結果判斷
-                    if (jointExamIdList.Count > 0 && jointExamIdList.Count.Equals(finishExamIdList.Count))
+                    if(jointEventGroup.Count.Equals(0)) //資格不符
+                    {
+                        result = "disqualify";
+                    }
+                    else if (jointExamIdList.Count > 0 && jointExamIdList.Count.Equals(finishExamIdList.Count))
                     {
                         result = "complete";
                     }
@@ -1282,7 +1330,8 @@ namespace TEAMModelOS.Controllers.Common
             public string scope { get; set; } //school:學校(選課班) private:個人 
             public string creatorId { get; set; }
             public string creatorName { get; set; }
-            public string schoolId { get; set; } //老師個人課程時為null
+            public string schoolId { get; set; } //老師教育雲綁定的學校ID
+            public string schoolName { get; set; } //老師教育雲綁定的學校名稱
             public string periodId { get; set; } //老師個人課程時為null
             public List<JointEventGroupCourseDto> courseLists { get; set; } = new();
             public class JointEventGroupCourseDto