浏览代码

解決衝突

upon 1 年之前
父节点
当前提交
f4111b6aa6
共有 73 个文件被更改,包括 6089 次插入1023 次删除
  1. 18 13
      TEAMModelBI/ClientApp/src/view/areaServe/areamanage.vue
  2. 2 2
      TEAMModelBI/ClientApp/src/view/schoolServe/school.vue
  3. 72 33
      TEAMModelBI/ClientApp/src/view/userInquire/details.vue
  4. 2 2
      TEAMModelBI/ClientApp/src/view/userInquire/socrates.vue
  5. 7 0
      TEAMModelOS.FunctionV4/CosmosDB/TriggerExam.cs
  6. 118 1
      TEAMModelOS.SDK/Context/Constant/ResponseCode.cs
  7. 118 0
      TEAMModelOS.SDK/Helper/Common/ReflectorExtensions/ObjectCopyConvert.cs
  8. 12 3
      TEAMModelOS.SDK/Models/Cosmos/Common/Activity.cs
  9. 40 0
      TEAMModelOS.SDK/Models/Cosmos/Common/IotTeachingData.cs
  10. 3 0
      TEAMModelOS.SDK/Models/Cosmos/Normal/TMDOrder.cs
  11. 5 0
      TEAMModelOS.SDK/Models/Cosmos/School/Elegant.cs
  12. 2 1
      TEAMModelOS.SDK/Models/Cosmos/Student/ArtAttachment.cs
  13. 26 2
      TEAMModelOS.SDK/Models/Service/BI/BIProdAnalysis.cs
  14. 6 2
      TEAMModelOS/ClientApp/public/lang/en-US.js
  15. 6 2
      TEAMModelOS/ClientApp/public/lang/zh-CN.js
  16. 6 2
      TEAMModelOS/ClientApp/public/lang/zh-TW.js
  17. 1 0
      TEAMModelOS/ClientApp/public/mw6hwxpgKz.txt
  18. 4 2
      TEAMModelOS/ClientApp/src/components/questionnaire/BaseJudge.vue
  19. 3 1
      TEAMModelOS/ClientApp/src/components/questionnaire/BaseMultiple.vue
  20. 3 2
      TEAMModelOS/ClientApp/src/components/questionnaire/BaseSingle.vue
  21. 4 0
      TEAMModelOS/ClientApp/src/components/student-web/HomeView/HomeView.less
  22. 39 17
      TEAMModelOS/ClientApp/src/components/student-web/HomeView/HomeView.vue
  23. 3 3
      TEAMModelOS/ClientApp/src/router/routes.js
  24. 5 1
      TEAMModelOS/ClientApp/src/view/joinSchool/JoinSchool.vue
  25. 1 1
      TEAMModelOS/ClientApp/src/view/login/Index.vue
  26. 1 0
      TEAMModelOS/ClientApp/src/view/mycourse/MyCourse.less
  27. 8 2
      TEAMModelOS/ClientApp/src/view/mycourse/MyCourse.vue
  28. 5 0
      TEAMModelOS/ClientApp/src/view/mycourse/exam/Exam.less
  29. 24 2
      TEAMModelOS/ClientApp/src/view/mycourse/exam/Exam.vue
  30. 6 0
      TEAMModelOS/ClientApp/src/view/mycourse/record/Record.less
  31. 22 19
      TEAMModelOS/ClientApp/src/view/mycourse/record/Record.vue
  32. 4 4
      TEAMModelOS/ClientApp/src/view/mycourse/score/AddProject.vue
  33. 0 4
      TEAMModelOS/ClientApp/src/view/mycourse/score/DialogBox.less
  34. 7 1
      TEAMModelOS/ClientApp/src/view/mycourse/score/DialogBoxAddTable.vue
  35. 65 58
      TEAMModelOS/ClientApp/src/view/mycourse/score/DialogBoxStudentScore.vue
  36. 86 70
      TEAMModelOS/ClientApp/src/view/mycourse/score/ScoreBody.vue
  37. 80 0
      TEAMModelOS/ClientApp/src/view/mycourse/syllabus/DialogBox/AddCard.vue
  38. 102 0
      TEAMModelOS/ClientApp/src/view/mycourse/syllabus/DialogBox/AddResources.vue
  39. 70 0
      TEAMModelOS/ClientApp/src/view/mycourse/syllabus/DialogBox/DialogBox.less
  40. 50 0
      TEAMModelOS/ClientApp/src/view/mycourse/syllabus/DialogBox/DialogBoxTemp.vue
  41. 63 0
      TEAMModelOS/ClientApp/src/view/mycourse/syllabus/DialogBox/ReleaseSyllabus.vue
  42. 319 0
      TEAMModelOS/ClientApp/src/view/mycourse/syllabus/DialogBox/Resources/A_Evaluate.vue
  43. 273 0
      TEAMModelOS/ClientApp/src/view/mycourse/syllabus/DialogBox/Resources/A_Homework.vue
  44. 356 0
      TEAMModelOS/ClientApp/src/view/mycourse/syllabus/DialogBox/Resources/A_Questionnaire.vue
  45. 312 0
      TEAMModelOS/ClientApp/src/view/mycourse/syllabus/DialogBox/Resources/A_Vote.vue
  46. 60 0
      TEAMModelOS/ClientApp/src/view/mycourse/syllabus/DialogBox/Resources/C_CloudContent.vue
  47. 78 0
      TEAMModelOS/ClientApp/src/view/mycourse/syllabus/DialogBox/Resources/C_LocalFile.vue
  48. 99 0
      TEAMModelOS/ClientApp/src/view/mycourse/syllabus/DialogBox/Resources/C_URL.vue
  49. 454 0
      TEAMModelOS/ClientApp/src/view/mycourse/syllabus/DialogBox/Resources/Resources.less
  50. 192 0
      TEAMModelOS/ClientApp/src/view/mycourse/syllabus/DialogBox/Resources/part/BaseQnForm.vue
  51. 52 0
      TEAMModelOS/ClientApp/src/view/mycourse/syllabus/DialogBox/SyllabusAdd.vue
  52. 103 0
      TEAMModelOS/ClientApp/src/view/mycourse/syllabus/DialogBox/SyllabusSorting.vue
  53. 90 0
      TEAMModelOS/ClientApp/src/view/mycourse/syllabus/DialogBox/SyncClass.vue
  54. 14 0
      TEAMModelOS/ClientApp/src/view/mycourse/syllabus/SyllabusIndex.less
  55. 137 0
      TEAMModelOS/ClientApp/src/view/mycourse/syllabus/SyllabusIndex.vue
  56. 90 0
      TEAMModelOS/ClientApp/src/view/mycourse/syllabus/SyllabusMenu.less
  57. 203 0
      TEAMModelOS/ClientApp/src/view/mycourse/syllabus/SyllabusMenu.vue
  58. 146 0
      TEAMModelOS/ClientApp/src/view/mycourse/syllabus/SyllabusResource.less
  59. 258 0
      TEAMModelOS/ClientApp/src/view/mycourse/syllabus/SyllabusResource.vue
  60. 39 0
      TEAMModelOS/ClientApp/src/view/mycourse/syllabus/SyllabusSchedule.less
  61. 155 0
      TEAMModelOS/ClientApp/src/view/mycourse/syllabus/SyllabusSchedule.vue
  62. 15 0
      TEAMModelOS/ClientApp/src/view/mycourse/syllabus/addSyllabus.less
  63. 100 0
      TEAMModelOS/ClientApp/src/view/mycourse/syllabus/addSyllabus.vue
  64. 178 529
      TEAMModelOS/ClientApp/src/view/signupActivity/createActivity.vue
  65. 306 143
      TEAMModelOS/ClientApp/src/view/signupActivity/infoActivity copy.vue
  66. 119 0
      TEAMModelOS/ClientApp/src/view/signupActivity/setActivity.vue
  67. 14 1
      TEAMModelOS/ClientApp/src/view/student-web/AppiView.less
  68. 4 14
      TEAMModelOS/Controllers/Both/CourseBaseController.cs
  69. 34 33
      TEAMModelOS/Controllers/Both/ScoreCalcController.cs
  70. 628 51
      TEAMModelOS/Controllers/Client/AClassONEController.cs
  71. 131 0
      TEAMModelOS/Controllers/Common/ActivityController.cs
  72. 30 2
      TEAMModelOS/Controllers/System/WeChatPayController.cs
  73. 1 0
      TEAMModelOS/Controllers/Teacher/InitController.cs

+ 18 - 13
TEAMModelBI/ClientApp/src/view/areaServe/areamanage.vue

@@ -866,24 +866,29 @@ export default {
     function updateShortcode(data,index){
       let shortCodes=data;let dataIndex=index
       console.log(shortCodes.length,'长度')
-      if(shortCodes.length !==6 && shortCodes.length !==0){
-        ElMessage.error('简码需6位字母或数字,请重新填写')
-        optionData.value[dataIndex].shortCode=curTarget.value.text
-      }else if(shortCodes.length ===0){
-        let data={id:curTarget.value.id,name:curTarget.value.name,shortCode:shortCodes}
+      // if(shortCodes.length !==6 && shortCodes.length !==0){
+      //   ElMessage.error('简码需6位字母或数字,请重新填写')
+      //   optionData.value[dataIndex].shortCode=curTarget.value.text
+      // }else if(shortCodes.length ===0){
+      //   let data={id:curTarget.value.id,name:curTarget.value.name,shortCode:shortCodes}
+      //   proxy.$api.updateAreacode(data).then((res)=>{
+      //     console.log(res,'updateCode back')
+      //     res.state === 200 ? ElMessage.success('更新成功'):ElMessage.error('操作成功')
+      //   })
+      //   curTarget.value.index=-1
+      // }else{
+      //   let data={id:curTarget.value.id,name:curTarget.value.name,shortCode:shortCodes}
+      //   proxy.$api.updateAreacode(data).then((res)=>{
+      //     console.log(res,'updateCode back')
+      //     res.state === 200 ? ElMessage.success('更新成功'):ElMessage.error('操作成功')
+      //   })
+      //   curTarget.value.index=-1
+      // } let data={id:curTarget.value.id,name:curTarget.value.name,shortCode:shortCodes}
         proxy.$api.updateAreacode(data).then((res)=>{
           console.log(res,'updateCode back')
           res.state === 200 ? ElMessage.success('更新成功'):ElMessage.error('操作成功')
         })
         curTarget.value.index=-1
-      }else{
-        let data={id:curTarget.value.id,name:curTarget.value.name,shortCode:shortCodes}
-        proxy.$api.updateAreacode(data).then((res)=>{
-          console.log(res,'updateCode back')
-          res.state === 200 ? ElMessage.success('更新成功'):ElMessage.error('操作成功')
-        })
-        curTarget.value.index=-1
-      }
     }
     //table按钮
     async function operation (index, row, state) {

+ 2 - 2
TEAMModelBI/ClientApp/src/view/schoolServe/school.vue

@@ -1503,7 +1503,7 @@ export default {
         ElMessage.error('API异常,规模版本排序失败')
       })
     }
-     // ※版本判定:1.學校管理(IPDYZYLC) + 學情分析(YMPCVCIM)=「標準版」 
+  // ※版本判定:1.學校管理(IPDYZYLC) + 學情分析(YMPCVCIM)=「標準版」 
     //           2.學校管理(IPDYZYLC) + 學情分析(YMPCVCIM) + 五育看板(YPXSJ6NJ) =「專業版」  
     function versionsEstimate (val) {
       console.log(shouldFilter.value)
@@ -1527,7 +1527,7 @@ export default {
        // 自訂版
        arrState.custom ? originalData.value.forEach((item) => { 
         item.edition !== null && item.edition.scaleVersion !== null && item.edition.scaleVersion !== "" ? customArr.push(item) : '' }) : ''           
-      
+
       let versionArr = [...marjorArr, ...standardArr, ...basicsArr, ...customArr]
       console.log(versionArr, '合并结果')
       tableData.value = versionArr

+ 72 - 33
TEAMModelBI/ClientApp/src/view/userInquire/details.vue

@@ -32,15 +32,10 @@
                             <p class="item-title">{{item.title}}</p>
                             <div class="item-content" v-show="item.state">
                                 <div class="content-left">
-                                    <p>{{item.subhead1}}</p>
-                                    <span>{{item.subhead1Value}}</span>
-                                </div>
-                                <div class="content-left">
-                                    <p>{{item.subhead2}}</p>
-                                    <span>{{item.subhead2Value}}</span>
+                                    <span>上次登陆时间:{{item.subhead2Value}}</span>
                                 </div>
                             </div>
-                            <div class="notenabled-title" v-show="!item.state">未使用</div>
+                            <div class="notenabled-title" v-show="!item.state">近期未使用</div>
                         </div>
                     </div>
         </div>
@@ -73,27 +68,34 @@
                     <div>空间与权益</div>
                     <div class="expire">到期日:XXXXX</div>
                 </div>
-                <el-divider />
-                <div class="size-label">
-                    <div class="size-item" v-for="item in gaugelabels" :key="item.id">
-                        <div class="size-value" :style="{'color':item.color}">{{item.value}}%</div>
-                        <div class="size-title">{{item.title}}</div>
+                <el-divider /> 
+                <div class="sizestate" v-if="sizeandequity ==='size'">
+                    <div class="size-label">
+                        <div class="size-item" v-for="item in gaugelabels" :key="item.id">
+                            <div class="size-value" :style="{'color':item.color}">{{item.value}}%</div>
+                            <div class="size-title">{{item.title}}</div>
+                        </div>
+                         </div>
+                        <el-divider />
+                        <div class="login-echart">
+                            <commonGaugeVue :gaugeData="gaugedata"></commonGaugeVue>
                     </div>
                 </div>
-                <el-divider />
-                <div class="login-echart">
-                    <commonGaugeVue :gaugeData="gaugedata"></commonGaugeVue>
-                </div>
-                <el-divider />
-                <div class="rightsbox">
-                    <p class="rightsbox-title">权益列表:</p>
-                    <div class="rightsbox-content">
-                        <div class="rightsbox-item" v-for="item in rightsdata" :key="item.key">
-                            <div class="rightsbox-item-name">{{item.name}}</div>
-                            <div class="rightsbox-item-time">到期日:{{item.time}}</div>
+                <div class="equitystate" v-else>
+                    <div class="rightsbox">
+                        <p class="rightsbox-title">权益列表:</p>
+                        <div class="rightsbox-content">
+                            <div class="rightsbox-item" v-for="item in rightsdata" :key="item.key">
+                                <div class="rightsbox-item-name">{{item.name}}</div>
+                                <div class="rightsbox-item-time">到期日:{{item.time}}</div>
+                            </div>
                         </div>
                     </div>
                 </div>
+                <div class="tab-state">
+                    <div :class="[sizeandequity ==='size' ? 'opts':'','tab-state-btn']" @click="sizeandequity='size'">我的空间</div>
+                    <div :class="[sizeandequity !=='size' ? 'opts':'','tab-state-btn']" @click="sizeandequity='equity'">我的权益</div>
+                </div>
             </div>
             <div class="iesdiv">
                 <div  class="common-header-title">
@@ -148,6 +150,7 @@ let { proxy } = getCurrentInstance()
 let activeName = ref('basics')
 let userdetailState=ref(false)
 let tabPosition = ref('left')
+let sizeandequity=ref('size')
 let usernames=ref('')
 let userdata = ref([
     { id: 1, icon: '#icon-ic_idcard', value: '0', title: '',key:'integral',check:false ,hint:'用户ID'},
@@ -158,10 +161,10 @@ let userdata = ref([
     { id: 6, icon: '#icon-shijian2', value: '2023-04-23', title: '', key: 'time', check: false ,hint:'上次登录时间' },
 ])
 let productdata = ref([
-    { id: 1, title: 'HiTeach', subhead1: '课堂总时长', subhead1Value: '13H', subhead2: '课堂数量', subhead2Value: 25,key:'HiTeach',state: false },
-    { id: 2, title: 'HiTA', subhead1: '使用时长', subhead1Value: '13H', subhead2: '协助课堂', subhead2Value: 199, key:'HiTA',state: false },
-    { id: 3, title: 'IES', subhead1: '使用资源课程', subhead1Value: '159', subhead2: '贡献资源数量', subhead2Value: 13, key:'IES', state: false },
-    { id: 4, title: '苏格拉底', subhead1: '上传影片', subhead1Value: '9', subhead2: '议课数量', subhead2Value: 15,key:'Socrates', state: false },
+    { id: 1, title: 'HiTeach', value:0,key:'HiTeach',state: false },
+    { id: 2, title: 'HiTA',value:0, key:'HiTA',state: false },
+    { id: 3, title: 'IES', value:0,key:'IES', state: false },
+    { id: 4, title: '苏格拉底', value:0,key:'Socrates', state: false },
 ])
 let tableData = ref([
     { time: '2023-07-05 15:33', ip: '222.209.14.199', location: '四川省成都市',platform:'HiTeach' },
@@ -443,11 +446,15 @@ function initdata() {
     userdata.value[4].value = 'XXXX'
     userdata.value[5].value = 'XXXX-XX-XX'
     //产品使用及安装情况
-    let {hiteach,hita,ies5,sokrates}=transmitData
-    transmitData.hasOwnProperty('hiteach') && Object.keys(hiteach).length !== 0 ? (productdata.value[0].state=true,productdata.value[0].subhead1Value=hiteach.subhead1Value,productdata.value[0].subhead2Value=hiteach.subhead2Value):productdata.value[0].state=false
-    transmitData.hasOwnProperty('hita') && Object.keys(hita).length !== 0 ? (productdata.value[1].state=true,productdata.value[1].subhead1Value=hita.subhead1Value,productdata.value[1].subhead2Value=hita.subhead2Value):productdata.value[1].state=false
-    transmitData.hasOwnProperty('ies5') && Object.keys(ies5).length !== 0 ? (productdata.value[2].state=true,productdata.value[2].subhead1Value='XXX',productdata.value[2].subhead2Value='XXX'):productdata.value[2].state=false
-    transmitData.hasOwnProperty('sokrates') && Object.keys(sokrates).length !== 0 ? (productdata.value[3].state=true,productdata.value[3].subhead1Value='XXX',productdata.value[3].subhead2Value='XXX'):productdata.value[3].state=false
+    let {login}=transmitData
+    if(login.length >0){
+       login.forEach(item => {
+         let typename=item.product
+         productdata.value.forEach(items=>{
+            typename === items.key ? items.value === 0 ? (items.value=item.time,items.state=true): item.time > items.value ? (items.value=item.time,items.state=true):'':''
+         })
+       })
+    }
     //空间与权益
     let {usedSize,teachSize,totalSize,surplusSize}=transmitData.ies5
     let usePercentum=parseInt(((Number(bytesToGB(usedSize))+Number(bytesToGB(teachSize)))/Number(bytesToGB(totalSize)))*100);let useGsize=proxy.$common.convertSize(usedSize)
@@ -569,7 +576,7 @@ onMounted(() => {
 .item-content{
     width:100%;
     display: flex;
-    justify-content: space-between;
+    justify-content: center;
 }
 .content-left,.content-right{
     margin-top:5px;
@@ -644,6 +651,38 @@ onMounted(() => {
     background-color: #fff;
     border-radius: 5px;
     box-shadow: rgba(0, 0, 0, 0.3) 0px 3px 8px;
+    position: relative;
+}
+.tab-state{
+    position:absolute;
+    width:18px;
+    right:-18px;
+    top:0px;
+    display: flex;
+    flex-wrap: wrap;
+}
+.tab-state-btn{
+    width:100%;
+    line-height: 14px;
+    font-size:14px;
+    padding: 2px;
+    border: 1px solid #ccc; /* 设置整个边框 */
+    border-left: none; /* 取消左边边框 */
+    border-radius: 0 5px 5px 0px;
+    color:#bbbecd;
+    cursor: pointer;
+    background-color: #c8c9cc;
+    color:#fff;
+    margin-top:1px;
+}
+.tab-state-btn:nth-child(2){
+    margin-top: 5px;
+}
+.opts{
+    background-color:#79bbff;
+    outline:none;
+    border-color:#79bbff;
+    color:#fff;
 }
 .size-label{
     width:100%;

+ 2 - 2
TEAMModelBI/ClientApp/src/view/userInquire/socrates.vue

@@ -189,9 +189,9 @@ let auxiliary=ref([
  function initdatas(){
     console.log(props,'苏格拉底')
     primary.value[0].value='XXX'
-    primary.value[1].value=props.sokratesdatas.t_green
+    primary.value[1].value='XXX'
     primary.value[2].value='XXX'
-    primary.value[3].value=props.sokratesdatas.t_data
+    primary.value[3].value='XXX'
 
     auxiliary.value[0].value='XXX'
     auxiliary.value[1].value='XXX'

+ 7 - 0
TEAMModelOS.FunctionV4/CosmosDB/TriggerExam.cs

@@ -1542,6 +1542,10 @@ namespace TEAMModelOS.FunctionV4
                 errorItems.Add(item);
             }
             try {
+               /* bool isError =  examClassResults.SelectMany(c => c.status).ToList().Exists(z => z == 1);
+                if () { 
+                
+                }*/
                 if (errorItems.Count == 0)
                 {
                     // 新增逻辑 收集错题内容
@@ -1654,6 +1658,9 @@ namespace TEAMModelOS.FunctionV4
                             }
                             index_item++;
                         }
+                        if (index.Count == 0) {
+                            continue;
+                        }
                         //int[] item_index = ss[n];
                         foreach (var item in index)
                         {

+ 118 - 1
TEAMModelOS.SDK/Context/Constant/ResponseCode.cs

@@ -58,6 +58,123 @@ namespace TEAMModelOS
         /// 响应超时
         /// </summary>
         public readonly static int TIMEOUT_ERROR = 503;
-        
+
+        #region 返回状态码
+        /// <summary>
+        /// 成功  200
+        /// </summary>
+        public readonly static int _200Ok = 200;
+
+        /// <summary>
+        /// 部分创建成功  201
+        /// </summary>
+        public readonly static int _201Created = 201;
+
+        /// <summary>
+        /// 已接受请求,处理未完成  202
+        /// </summary>
+        public readonly static int _202Accepted = 202;
+
+        /// <summary>
+        /// 非授权信息  203
+        /// </summary>
+        public readonly static int _203NonUnauthorizedInfo = 203;
+
+        /// <summary>
+        ///  服务器成功处理了请求,但没有返回任何内容。  204
+        /// </summary>
+        public readonly static int _204NoContent = 204;
+
+        /// <summary>
+        /// 参数错误  400
+        /// </summary>
+        public readonly static int _400ParamsError = 400;
+
+        /// <summary>
+        /// 未授权 请求要求身份验证  401
+        /// </summary>
+        public readonly static int _401Unauthorized = 401;
+
+        /// <summary>
+        /// 需要付费  402
+        /// </summary>
+        public readonly static int _402PaymentRequired = 402;
+
+        /// <summary>
+        /// 服务器拒绝/禁用  403
+        /// </summary>
+        public readonly static int _403Forbidden = 403;
+
+        /// <summary>
+        /// 密码错误
+        /// </summary>
+        public readonly static int _40301ForbiddenPwd = 40301;
+
+        /// <summary>
+        /// 未找到    404
+        /// </summary>
+        public readonly static int _404NotFound = 404;
+
+        /// <summary>
+        /// 方法不可用  405
+        /// </summary>
+        public readonly static int _405NotAllow = 405;
+
+        /// <summary>
+        /// 请求超时   408
+        /// </summary>
+        public readonly static int _408RespondTimeOut = 408;
+
+        /// <summary>
+        /// 已存在   409
+        /// </summary>
+        public readonly static int _409Conflict = 409;
+
+        /// <summary>
+        /// 过期    410
+        /// </summary>
+        public readonly static int _410Gone = 410;
+
+        /// <summary>
+        /// 异常 411
+        /// </summary>
+        public readonly static int _411Abnormal = 411;
+
+        /// <summary>
+        /// 不支持的媒体类型  415
+        /// </summary>
+        public readonly static int _415NoMediaType = 415;
+
+        /// <summary>
+        /// 创建失败
+        /// </summary>
+        public readonly static int _417CreateFailed = 417;
+
+        /// <summary>
+        /// 服务器错误  500
+        /// </summary>
+        public readonly static int _500Error = 500;
+
+        /// <summary>
+        /// 服务器拒绝
+        /// </summary>
+        public readonly static int _501Refuse = 501;
+
+        /// <summary>
+        /// 网关错误 502
+        /// </summary>
+        public readonly static int _502GatewayError = 502;
+
+        /// <summary>
+        /// 网关超时  504
+        /// </summary>
+        public readonly static int _504GatewayTimeOut = 504;
+
+        /// <summary>
+        /// 存储不足   507
+        /// </summary>
+        public readonly static int _507InsufficientStorage = 507;
+        #endregion
+
     }
 }

+ 118 - 0
TEAMModelOS.SDK/Helper/Common/ReflectorExtensions/ObjectCopyConvert.cs

@@ -0,0 +1,118 @@
+using HTEXLib.COMM.Helpers;
+using Microsoft.IdentityModel.Tokens;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Text.Json;
+using System.Threading.Tasks;
+using TEAMModelOS.SDK.Extension;
+
+namespace SDK.Helpers
+{
+    public static class ObjectCopyConvert
+    {
+        /// <summary>
+        /// 
+        /// </summary>
+        /// <typeparam name="ST"></typeparam>
+        /// <param name="json"></param>
+        /// <param name="target"></param>
+        /// <param name="filter">只修改部分指定的属性</param>
+        public static void CopyToSameProperty<ST>(this JsonElement json, ST target, List<string>? filter = null) {
+            ST? source = json.ToObject<ST>();
+            PropertyInfo[] sourceProperties = source!.GetType().GetProperties();
+            PropertyInfo[] targetProperties = target!.GetType().GetProperties();
+
+            foreach (PropertyInfo sourceProperty in sourceProperties)
+            {
+                //数据类型相同,属性名称相同,可能存在赋值关系。
+                PropertyInfo? targetProperty = targetProperties.FirstOrDefault(p => p.Name == sourceProperty.Name && p.PropertyType == sourceProperty.PropertyType);
+                var data_source = sourceProperty.GetValue(source);
+                if (targetProperty != null && sourceProperty.GetValue(source) != null)
+                {
+                    // JsonElement element_source = data_source!.ToJsonString().ToObject<JsonElement>();
+                    //var data_target = targetProperty.GetValue(target);
+                    //JsonElement element_target = data_target != null ? data_target.ToJsonString().ToObject<JsonElement>() : default;
+                    JsonElement temp = default;
+                    if (json.TryGetProperty(sourceProperty.Name, out temp)
+                        //驼峰命名,首字母小写
+                        || json.TryGetProperty($"{sourceProperty.Name.Substring(0, 1).ToLower()}{sourceProperty.Name.Substring(1)}", out temp))
+                    {
+                        if (filter.IsEmpty())
+                        {
+                            //只要传递了值才会运行此处
+                            targetProperty.SetValue(target, sourceProperty.GetValue(source));
+                        }
+                        else
+                        {
+                            if (filter!.Contains(targetProperty.Name))
+                            {
+                                targetProperty.SetValue(target, sourceProperty.GetValue(source));
+                            }
+                        }
+                    }
+                    else
+                    {
+                        // Console.WriteLine("默认值,如数字,bool类型下的默认值");
+                    }
+                    //var json=  sourceProperty.GetValue(source)!.ToJsonString().ToObject<JsonElement>();
+                    //判断数字类型,如果前后数字相同,则不赋值,防止出现0这种。
+                    //判断True ,如果相同,也不赋值,防止出现
+                }
+            }
+        }
+        /// <summary>
+        /// 复制类型
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <typeparam name="V"></typeparam>
+        /// <param name="json"></param>
+        /// <param name="target"></param>
+        /// <param name="filter"></param>
+        public static void CopyToSameProperty<S, T>(this JsonElement json, T target, List<string>? filter = null)
+        {
+            S? source=json.ToObject<S>();
+            PropertyInfo[] sourceProperties = source!.GetType().GetProperties();
+            PropertyInfo[] targetProperties = target!.GetType().GetProperties();
+
+            foreach (PropertyInfo sourceProperty in sourceProperties)
+            {
+                //数据类型相同,属性名称相同,可能存在赋值关系。
+                PropertyInfo? targetProperty = targetProperties.FirstOrDefault(p => p.Name == sourceProperty.Name  && p.PropertyType==sourceProperty.PropertyType );
+                var data_source = sourceProperty.GetValue(source);
+                if (targetProperty != null && sourceProperty.GetValue(source) != null)
+                {
+                    // JsonElement element_source = data_source!.ToJsonString().ToObject<JsonElement>();
+                    //var data_target = targetProperty.GetValue(target);
+                    //JsonElement element_target = data_target != null ? data_target.ToJsonString().ToObject<JsonElement>() : default;
+                    JsonElement temp = default;
+                    if (json.TryGetProperty(sourceProperty.Name, out temp)
+                        //驼峰命名,首字母小写
+                        || json.TryGetProperty($"{sourceProperty.Name.Substring(0, 1).ToLower()}{sourceProperty.Name.Substring(1)}", out temp))
+                    {
+                        if (filter.IsEmpty())
+                        {
+                            //只要传递了值才会运行此处
+                            targetProperty.SetValue(target, sourceProperty.GetValue(source));
+                        }
+                        else {
+                            if (filter!.Contains(targetProperty.Name))
+                            {
+                                targetProperty.SetValue(target, sourceProperty.GetValue(source));
+                            }
+                        }
+                    }
+                    else {
+                       // Console.WriteLine("默认值,如数字,bool类型下的默认值");
+                    }
+                    //var json=  sourceProperty.GetValue(source)!.ToJsonString().ToObject<JsonElement>();
+                    //判断数字类型,如果前后数字相同,则不赋值,防止出现0这种。
+                    //判断True ,如果相同,也不赋值,防止出现
+                   
+                }
+            }
+        }
+    }
+}

+ 12 - 3
TEAMModelOS.SDK/Models/Cosmos/Common/Activity.cs

@@ -2,10 +2,11 @@
 using System.Collections.Generic;
 using System.ComponentModel.DataAnnotations;
 using System.Linq;
+using System.Net.Mail;
 using System.Text;
 using System.Threading.Tasks;
 
-namespace TEAMModelOS.SDK.Models.Cosmos.Common
+namespace TEAMModelOS.SDK.Models
 {
     public class Activity : CosmosEntity
     {
@@ -14,27 +15,32 @@ namespace TEAMModelOS.SDK.Models.Cosmos.Common
         public Activity() {
             pk="Activity";
         }
+        [Required(ErrorMessage = "Required")]
         public string name { get; set; }
+        [Required(ErrorMessage = "Required")]
         public string subject { get; set; }
         public string description { get; set; }
         public string address { get; set; }
         public long stime { get; set; }
         public long etime { get; set; }
         public string poster { get; set; }
-        public List<string> attachment { get; set; } = new List<string>();
+        public List<Attachment> attachment { get; set; } = new List<Attachment>();
         public List<string> zb { get; set; } = new List<string>();
         public List<string> cb { get; set; } = new List<string>();
         /// <summary>
         /// "hbcn/区级id,areaId",
         /// </summary>
+        [Required(ErrorMessage = "Required")]
         public string owner { get; set; }
         /// <summary>
         /// "public公开/area区级/school校级",
         /// </summary>
+        [Required(ErrorMessage = "Required")]
         public string scope { get; set; }
         /// <summary>
         /// "enroll/报名制,invite/邀请制",
         /// </summary>
+        [Required(ErrorMessage = "Required")]
         public string joinMode { get; set; }
         /// <summary>
         /// //区级活动时允许参与的学校,如果为空则全部学校
@@ -60,7 +66,10 @@ namespace TEAMModelOS.SDK.Models.Cosmos.Common
         /// 创建时间
         /// </summary>
         public long createTime {  get; set; }
-
+        /// <summary>
+        /// 创建者
+        /// </summary>
+        public string creatorId { get; set; }
     }
 
     /// <summary>

+ 40 - 0
TEAMModelOS.SDK/Models/Cosmos/Common/IotTeachingData.cs

@@ -105,6 +105,38 @@ namespace TEAMModelOS.SDK.Models.Cosmos.Common
         /// 是否送出小數據或SOK服務 0:false 1:true
         /// </summary>
         public string sendSok { get; set; }
+        /// <summary>
+        /// 學習型態-互評 0:false 1:true
+        /// </summary>
+        public string learnPeer { get; set; }
+        /// <summary>
+        /// 學習型態-協作 0:false 1:true
+        /// </summary>
+        public string learnCoop { get; set; }
+        /// <summary>
+        /// 課堂中有使用文字雲 0:false 1:true
+        /// </summary>
+        public string useWordCloud { get; set; }
+        /// <summary>
+        /// 課堂中有使用clouDAS 0:false 1:true
+        /// </summary>
+        public string useClouDAS { get; set; }
+        /// <summary>
+        /// 課堂中有使用GPT 0:false 1:true
+        /// </summary>
+        public string useGPT { get; set; }
+        /// <summary>
+        /// 課堂中有使用IES5測驗模式 0:false 1:true
+        /// </summary>
+        public string useIes5Test { get; set; }
+        /// <summary>
+        /// 課堂中有使用紙本測驗模式 0:false 1:true
+        /// </summary>
+        public string usePaperTest { get; set; }
+        /// <summary>
+        /// 課堂中有使用Excel測驗模式 0:false 1:true
+        /// </summary>
+        public string useExcelTest { get; set; }
     }
 
     /// <summary>
@@ -148,5 +180,13 @@ namespace TEAMModelOS.SDK.Models.Cosmos.Common
         public int item { get; set; } //題數
         public int interact { get; set; } //互動總次數
         public int sendSok { get; set; } //送出小數據或SOK服務
+        public int learnPeer { get; set; } //學習型態-互評
+        public int learnCoop { get; set; } //學習型態-協作
+        public int useWordCloud { get; set; } //課堂中有使用文字雲
+        public int useClouDAS { get; set; } //課堂中有使用clouDAS
+        public int useGPT { get; set; } //課堂中有使用GPT
+        public int useIes5Test { get; set; } //課堂中有使用IES5測驗模式
+        public int usePaperTest { get; set; } //課堂中有使用紙本測驗模式
+        public int useExcelTest { get; set; } //課堂中有使用Excel測驗模式
     }
 }

+ 3 - 0
TEAMModelOS.SDK/Models/Cosmos/Normal/TMDOrder.cs

@@ -23,6 +23,9 @@ namespace TEAMModelOS.SDK.Models
         public string buyer_email { get; set; }
         public string return_url { get; set; }
         public string notify_url { get; set; }
+        public string ip { get; set; }
+        public string region { get; set; }
+        public string school { get; set; }
         /// <summary>
         /// 微信支付二维码
         /// </summary>

+ 5 - 0
TEAMModelOS.SDK/Models/Cosmos/School/Elegant.cs

@@ -46,6 +46,11 @@ namespace TEAMModelOS.SDK.Models
         /// </summary>
         public string type { get; set; }
         public List<string> classes { get; set; } = new List<string>();
+        public string target { get; set; }
         public List<Attachment> attachments { get; set; } = new List<Attachment>();
+        /// <summary>
+        /// 业务类型。elegant 德育, art 艺术
+        /// </summary>
+        public string bizCode { get; set; } = "elegant";
     }
 }

+ 2 - 1
TEAMModelOS.SDK/Models/Cosmos/Student/ArtAttachment.cs

@@ -50,7 +50,7 @@ namespace TEAMModelOS.SDK.Models.Cosmos.Student
         public string des { get; set; }
         //0 艺术作品 1 等级证书
         public int artType { get; set; }
-        // 0 校级 1 区级 2 市级 3 省级 4 国家级
+        // 0 校级 1 区级 2 市级 3 省级 4 国家级 5艺术体验 6艺术特长、7艺术考级 8观看演出 9社会学习 10 乐器演奏 11观看展览 12个人演唱 13书画创作 14合唱表演 15 艺术评比...
         public int level { get; set; }
         public long uploadTime { get; set; }
         public long updateTime { get; set; }
@@ -58,6 +58,7 @@ namespace TEAMModelOS.SDK.Models.Cosmos.Student
         /// 上传的文件
         /// </summary>
         public List<Attachment> files { get; set; }
+        public string uploadName { get; set; }
 
     }
 }

文件差异内容过多而无法显示
+ 26 - 2
TEAMModelOS.SDK/Models/Service/BI/BIProdAnalysis.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:'注意:新增的成績表將以當下系統中的現有成績作為基礎,新建的成績表不會受到其他功能的影響或連動',
     }
 
 

+ 1 - 0
TEAMModelOS/ClientApp/public/mw6hwxpgKz.txt

@@ -0,0 +1 @@
+b695ac74b5189d16782294274ab65282

+ 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)) 

+ 4 - 0
TEAMModelOS/ClientApp/src/components/student-web/HomeView/HomeView.less

@@ -132,6 +132,10 @@
                 color: #03966a;
             }
         }
+
+        .course-box {
+            display: inline-block;
+        }
     }
 
     .no-data {

+ 39 - 17
TEAMModelOS/ClientApp/src/components/student-web/HomeView/HomeView.vue

@@ -2,26 +2,28 @@
     <div class="home-view">
         <Loading v-show="isLoading" bgColor="rgba(0, 0, 0, 0.3)"></Loading>
         <div class="home-head home-card">
-            <div class="home-title-name">
+            <div class="home-title-name title-one">
                 {{ $t("studentWeb.home.title.joinClass") }}
                 <Icon type="md-add-circle" color="#02B35A" size="17" :title="$t('studentWeb.home.joinClass')" @click="isScale ? false : addCourse = true" v-if="getAllCourse.length && !onlySystem" />
-                <div class="course-type" v-if="getAllCourse.length && $store.state.userInfo.scope === 'student'">
-                    <RadioGroup v-model="courseType" @on-change="searchCourse" size="small" type="button" button-style="solid">
-                        <Radio label="all">{{ $t('evaluation.filter.all') }}</Radio>
-                        <Radio label="school">{{ $t('answerSheet.dp.school') }}</Radio>
-                        <Radio label="private">{{ $t('cusMgt.private') }}</Radio>
-                    </RadioGroup>
-                    <!-- <Space direction="vertical" size="large">
-                        <Space> -->
-                            <Input v-model="searchText" :placeholder="$t('studentWeb.home.title.search')" size="small" style="width: 200px">
-                                <template #suffix>
-                                    <Icon type="ios-search" @click="searchCourse" />
-                                </template>
-                            </Input>
-                        <!-- </Space>
-                    </Space> -->
+                <div class="course-box">
+                    <div class="course-type" v-if="getAllCourse.length && $store.state.userInfo.scope === 'student'">
+                        <RadioGroup v-model="courseType" @on-change="searchCourse" size="small" type="button" button-style="solid">
+                            <Radio label="all">{{ $t('evaluation.filter.all') }}</Radio>
+                            <Radio label="school">{{ $t('answerSheet.dp.school') }}</Radio>
+                            <Radio label="private">{{ $t('cusMgt.private') }}</Radio>
+                        </RadioGroup>
+                        <!-- <Space direction="vertical" size="large">
+                            <Space> -->
+                                <Input v-model="searchText" :placeholder="$t('studentWeb.home.title.search')" size="small" style="width: 200px">
+                                    <template #suffix>
+                                        <Icon type="ios-search" @click="searchCourse" />
+                                    </template>
+                                </Input>
+                            <!-- </Space>
+                        </Space> -->
+                    </div>
+                    <Button style="margin-left: 10px;" size="small" type="success" ghost @click="getReview">{{ $t('studentWeb.home.applyList') }}</Button>
                 </div>
-                <Button style="margin-left: 10px;" size="small" type="success" ghost @click="getReview">{{ $t('studentWeb.home.applyList') }}</Button>
             </div>
             <div class="home-join" v-if="allCourseShow.length">
                 <vuescroll>
@@ -951,4 +953,24 @@ export default {
         }
     }
 }
+
+@media screen and (max-width: 520px) {
+    .home-view{
+        .title-one {
+            height: 100px;
+        }
+        .course-box {
+            .course-type {
+                margin-left: 0;
+                .ivu-input-wrapper {
+                    margin: 5px 0;
+                }
+            }
+            .ivu-btn {
+                margin-left: 0 !important;
+            }
+        }
+    }
+}
+
 </style>

+ 3 - 3
TEAMModelOS/ClientApp/src/router/routes.js

@@ -1497,9 +1497,9 @@ export const routes = [{
     {
         path: 'activitySet',
         name: 'activitySet',
-        component: () => import('@/view/signupActivity/manageActivity.vue'),
+        component: () => import('@/view/signupActivity/setActivity.vue'),
         meta: {
-            activeName: 'activityManage',
+            activeName: 'activitySet',
         }
     }
     ]
@@ -1956,7 +1956,7 @@ export const routes = [{
         {
             path: 'areaActivitySet',
             name: 'areaActivitySet',
-            component: () => import('@/view/signupActivity/manageActivity.vue'),
+            component: () => import('@/view/signupActivity/setActivity.vue'),
             meta: {
                 activeName: 'areaActivityManage',
             }

+ 5 - 1
TEAMModelOS/ClientApp/src/view/joinSchool/JoinSchool.vue

@@ -56,7 +56,11 @@ export default {
             this.isJoin = true
             this.$Message.success(this.$t('cusMgt.join.joinOk'))
           } else {
-            this.$Message.error(res.msg)
+            if(res.error === 3){
+              this.$Message.warning(this.$t('home.schoolSuccess'))
+            }else{
+              this.$Message.error(this.$t('cusMgt.join.joinErr'))
+            }
           }
         },
         err => {

+ 1 - 1
TEAMModelOS/ClientApp/src/view/login/Index.vue

@@ -549,7 +549,7 @@ export default {
     }
 
     //TEAMModelID 社群登入資訊
-    this.userOauth.code = this.$route.query.code
+    this.userOauth.code = this.$route.query.code || this.$route.query.ticket
     this.userOauth.state = this.$route.query.state
 
     // 社群帳號登入

+ 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>

+ 178 - 529
TEAMModelOS/ClientApp/src/view/signupActivity/createActivity.vue

@@ -101,12 +101,24 @@
                                                 <FormItem label="填报信息">
                                                     <CheckboxGroup v-model="activityData.infoArr">
                                                         <Checkbox v-for="(item, index) in infoArr" :key="index" :label="item.value">{{ item.label }}</Checkbox>
+                                                        <Icon type="md-add-circle" @click="addInfoType = true" v-show="!addInfoType" size="18" />
+                                                        <div v-show="addInfoType">
+                                                            <Input v-model="addInfo" placeholder="自定义填报的信息" style="width: 300px"></Input>
+                                                            <Icon type="md-checkmark-circle" size="18" style="margin: 0 10px;" @click="addInform()" />
+                                                            <Icon type="md-close-circle" size="18" @click="addInfoType = false" />
+                                                        </div>
                                                     </CheckboxGroup>
                                                 </FormItem>
                                                 <FormItem label="报名方式">
                                                     <RadioGroup v-model="activityData.applicationType">
                                                         <Radio :label="1">{{ '报名制' }}</Radio>
                                                         <Radio :label="2">{{ '邀请制' }}</Radio>
+                                                        <Select v-model="schoolChange" multiple filterable v-show="activityData.applicationType === 2 && isArea === 'area'" style="width: 500px" placeholder="选择参加活动的学校">
+                                                            <Option v-for="format in schoolList" :value="format.code" :key="format.code">{{ format.name }}</Option>
+                                                        </Select>
+                                                        <Select v-model="schoolChange" multiple filterable v-show="activityData.applicationType === 2 && isArea === 'school'" style="width: 500px" placeholder="选择参加活动的学校">
+                                                            <Option v-for="format in teacherList" :value="format.code" :key="format.code">{{ format.name }}</Option>
+                                                        </Select>
                                                     </RadioGroup>
                                                 </FormItem>
                                                 <!-- 邀请制且团队赛,按照教研组分组,组长为教研组组长 -->
@@ -182,13 +194,14 @@
                                                     <DatePicker :editable="false" @on-change="getReviewTime" type="datetimerange" format="yyyy-MM-dd HH:mm" :placeholder="$t('train.create.timeHolder')" style="width: 500px"></DatePicker>
                                                 </FormItem>
                                                 <FormItem label="评审规则">
-                                                    <Select v-model="activityData.review" style="width:200px">
+                                                    <Select v-model="activityData.review" transfer style="width:200px">
                                                         <Option v-for="item in reviewList" :value="item.value" :key="item.value">
                                                             {{ item.label }}
-                                                            <span @click="ruleDrawer = true">预览</span>
+                                                            <span @click.stop="ruleDrawer = true">预览</span>
+                                                            <span @click.stop="reviewEditRule()">编辑</span>
                                                         </Option>
                                                     </Select>
-                                                    <Icon size="20" type="md-add-circle" @click="ruleDrawerAdd = true" />
+                                                    <Icon size="20" type="md-add-circle" @click="drawerRule()" />
                                                 </FormItem>
                                             </div>
                                         </div>
@@ -242,7 +255,7 @@
                 <Table row-key="id" :columns="ruleColumns" :data="ruleInfo.children"></Table>
             </div>
         </Drawer>
-        <Drawer title="添加评审规则" :width="50" :closable="false" v-model="ruleDrawerAdd">
+        <Drawer title="添加评审规则" :width="50" :closable="false" v-model="ruleDrawerAdd" class="light-iview-form">
             <Form :model="processInfo" :label-width="80" class="create-form">
                 <FormItem label="规则名称">
                     <Input v-model="processInfo.name" placeholder="请输入规则名称"></Input>
@@ -250,23 +263,33 @@
                 <FormItem label="规则描述">
                     <Input v-model="processInfo.describe" placeholder="请输入规则描述"></Input>
                 </FormItem>
-                <FormItem label="规则分值">
+                <!-- <FormItem label="规则分值">
                     <Input v-model="processInfo.score" placeholder="请输入分值"></Input>
-                </FormItem>
+                </FormItem> -->
                 <div class="rule-header">
                     <span>分项</span>
                     <span>描述</span>
                     <span>分值</span>
                     <span>操作</span>
                 </div>
-                <Tree :data="ruleTree"></Tree>
+                <el-tree :data="processInfo.rule" node-key="id" default-expand-all :expand-on-click-node="false">
+                    <div class="custom-tree-node" slot-scope="{ node, data }">
+                        <Input v-model="data.name" placeholder="分项名称" style="width: 200px;"></Input>
+                        <Input v-model="data.describe" placeholder="对分项内容进行描述"></Input>
+                        <InputNumber v-model="data.score" :min="1" style="width: 100px;" :disabled="(data.children && data.children.length) ? true : false" @on-change="scoreChange(node, data)" />
+                        <span>
+                            <Button size="small" style="margin-right: 10px;" :disabled="data.isThree" @click="append(node, data)">添加子项</Button>
+                            <Button size="small" type='error' @click="remove(node, data)">删除</Button>
+                        </span>
+                    </div>
+                </el-tree>
             </Form>
-            <div style="margin: 10px 0; text-align: right;">
-                <!-- <span>总分:0</span> -->
-                <Button @click="addRule">添加分项</Button>
+            <div style="margin: 10px 0; display: flex; justify-content: space-between; align-items: center;">
+                <span style="font-size: 20px;">总分:{{ processInfo.score }}</span>
+                <Button @click="addRule()">添加分项</Button>
             </div>
             <div>
-                <Button @click="save">仅本次活动使用</Button>
+                <Button @click="save()">仅本次活动使用</Button>
                 <Button>保存为模板</Button>
             </div>
         </Drawer>
@@ -588,12 +611,13 @@ export default {
                 },
                 {
                     title: '描述',
-                    key: 'describe'
+                    key: 'describe',
                 },
                 {
                     title: '分值',
                     key: 'score',
                     width: 100,
+                    align: 'center',
                 },
             ],
             ruleInfo: {
@@ -644,334 +668,94 @@ export default {
                                 name: '教学过程',
                                 describe: '',
                                 score: 5,
+                                parentId: '106',
                             },
                             {
                                 id: '10601',
                                 name: '教学设计',
                                 describe: '',
                                 score: 5,
+                                parentId: '106',
                             },
                             {
                                 id: '10602',
                                 name: '融合创新',
                                 describe: '',
                                 score: 5,
+                                parentId: '106',
                             },
                             {
                                 id: '10603',
                                 name: '技术应用',
                                 describe: '',
                                 score: 5,
+                                parentId: '106',
                             },
                             {
                                 id: '10604',
                                 name: '教学效果',
                                 describe: '',
                                 score: 5,
+                                parentId: '106',
                             },
                         ],
                     },
                 ],
             },
-            processInfo: {
-                rule: [
-                    {
+            processInfo: {},
+            timer: null,
+            treeType: '',
+            editRule: false,
+            dataRuleMould: [{
+                id: 1,
+                name: '',
+                describe: '',
+                score: null,
+                children: [{
+                    id: 2,
+                    name: '',
+                    describe: '',
+                    score: null,
+                    children: [{
+                        id: 3,
                         name: '',
                         describe: '',
                         score: null,
-                        nodeKey: 0,
-                        children: [
-                            {
-                                name: '',
-                                describe: '',
-                                score: null,
-                                nodeKey: 1,
-                                children: [
-                                    {
-                                        name: '',
-                                        describe: '',
-                                        score: null,
-                                    }
-                                ],
-                            }
-                        ],
-                    }
-                ]
-            },
-            ruleTree: [
+                        isThree: true,
+                    }]
+                }]
+            }],
+            addInfoType: false,
+            addInfo: '',
+            schoolList: [
                 {
-                    title: '1',
-                    expand: true,
-                    name: '',
-                    describe: '',
-                    score: null,
-                    render: (h, { root, node, data }) => {
-                        return h('div', [
-                            h('Input', {
-                                props: {
-                                    value: data.name,
-                                    placeholder: '一级分项',
-                                },
-                                style: {
-                                    width: '20%',
-                                    marginRight: '10px',
-                                },
-                                on: {
-                                    'on-change': value => {
-                                        console.log(data, value);
-                                        if(value.data !== null) {
-                                            that.inputChange(data, value, 'name')
-                                        }
-                                    }
-                                },
-                            }),
-                            h('Input', {
-                                props: {
-                                    value: data.describe,
-                                    placeholder: '对分项内容进行描述',
-                                },
-                                style: {
-                                    width: '65%',
-                                    marginRight: '10px',
-                                },
-                                on: {
-                                    'on-change': value => {
-                                        console.log(data, value);
-                                        if(value.data !== null) {
-                                            that.inputChange(data, value, 'describe')
-                                        }
-                                    }
-                                },
-                            }),
-                            h('Input', {
-                                props: {
-                                    value: data.score,
-                                },
-                                style: {
-                                    width: '9%',
-                                    marginRight: '10px',
-                                },
-                                on: {
-                                    'on-change': value => {
-                                        console.log(data, value);
-                                        if(value.data !== null) {
-                                            that.inputChange(data, value, 'score')
-                                        }
-                                    }
-                                },
-                            }),
-                            h('span', [
-                                h('Button', {
-                                    props: {
-                                        // type: 'error',
-                                        size: 'small',
-                                    },
-                                    style: {
-                                        marginRight: '10px',
-                                    },
-                                    on: {
-                                        click:() => {
-                                            that.append(data)
-                                        }
-                                    },
-                                }, '插入'),
-                                h('Button', {
-                                    props: {
-                                        type: 'error',
-                                        size: 'small',
-                                    },
-                                    /* style: {
-                                        width: '13%',
-                                    }, */
-                                    on: {
-                                        click:() => {
-                                            that.remove(root, node, data)
-                                        }
-                                    },
-                                }, '删除'),
-                            ])
-                        ], data.title)
-                    },
-                    children: [
-                        {
-                            title: '101',
-                            expand: true,
-                            name: '',
-                            describe: '',
-                            score: null,
-                            render: (h, { root, node, data }) => {
-                                return h('div', [
-                                    h('Input', {
-                                        props: {
-                                            value: data.name,
-                                            placeholder: '二级分项',
-                                        },
-                                        style: {
-                                            width: '20%',
-                                            marginRight: '10px',
-                                        },
-                                        on: {
-                                            'on-change': value => {
-                                                console.log(data, value);
-                                                if(value.data !== null) {
-                                                    that.inputChange(data, value, 'name')
-                                                }
-                                            }
-                                        },
-                                    }),
-                                    h('Input', {
-                                        props: {
-                                            value: data.describe,
-                                            placeholder: '对分项内容进行描述',
-                                        },
-                                        style: {
-                                            width: '62.9%',
-                                            marginRight: '10px',
-                                        },
-                                        on: {
-                                            'on-change': value => {
-                                                console.log(data, value);
-                                                if(value.data !== null) {
-                                                    that.inputChange(data, value, 'describe')
-                                                }
-                                            }
-                                        },
-                                    }),
-                                    h('Input', {
-                                        props: {
-                                            value: data.score,
-                                        },
-                                        style: {
-                                            width: '9%',
-                                            marginRight: '10px',
-                                        },
-                                        on: {
-                                            'on-change': value => {
-                                                console.log(data, value);
-                                                if(value.data !== null) {
-                                                    that.inputChange(data, value, 'score')
-                                                }
-                                            }
-                                        },
-                                    }),
-                                    h('span', [
-                                        h('Button', {
-                                            props: {
-                                                // type: 'error',
-                                                size: 'small',
-                                            },
-                                            style: {
-                                                marginRight: '10px',
-                                            },
-                                            on: {
-                                                click:() => {
-                                                    that.append(data, true)
-                                                }
-                                            },
-                                        }, '插入'),
-                                        h('Button', {
-                                            props: {
-                                                type: 'error',
-                                                size: 'small',
-                                            },
-                                            /* style: {
-                                                width: '13%',
-                                            }, */
-                                            on: {
-                                                click:() => {
-                                                    that.remove(root, node, data)
-                                                }
-                                            },
-                                        }, '删除'),
-                                    ])
-                                ])
-                            },
-                            children: [
-                                {
-                                    title: '101',
-                                    expand: true,
-                                    name: '',
-                                    describe: '',
-                                    score: null,
-                                    render: (h, { root, node, data }) => {
-                                        return h('div', [
-                                            h('Input', {
-                                                props: {
-                                                    value: data.name,
-                                                    placeholder: '三级分项',
-                                                },
-                                                style: {
-                                                    width: '20%',
-                                                    marginRight: '10px',
-                                                },
-                                                on: {
-                                                    'on-change': value => {
-                                                        console.log(data, value);
-                                                        if(value.data !== null) {
-                                                            that.inputChange(data, value, 'name')
-                                                        }
-                                                    }
-                                                },
-                                            }),
-                                            h('Input', {
-                                                props: {
-                                                    value: data.describe,
-                                                    placeholder: '对分项内容进行描述',
-                                                },
-                                                style: {
-                                                    width: '61.3%',
-                                                    marginRight: '10px',
-                                                },
-                                                on: {
-                                                    'on-change': value => {
-                                                        console.log(data, value);
-                                                        if(value.data !== null) {
-                                                            that.inputChange(data, value, 'describe')
-                                                        }
-                                                    }
-                                                },
-                                            }),
-                                            h('Input', {
-                                                props: {
-                                                    value: data.score,
-                                                },
-                                                style: {
-                                                    width: '9%',
-                                                    marginRight: '10px',
-                                                },
-                                                on: {
-                                                    'on-change': value => {
-                                                        console.log(data, value);
-                                                        if(value.data !== null) {
-                                                            that.inputChange(data, value, 'score')
-                                                        }
-                                                    }
-                                                },
-                                            }),
-                                            h('Button', {
-                                                props: {
-                                                    type: 'error',
-                                                    size: 'small',
-                                                },
-                                                style: {
-                                                    marginLeft: '50px',
-                                                },
-                                                on: {
-                                                    click:() => {
-                                                        that.remove(root, node, data)
-                                                    }
-                                                },
-                                            }, '删除'),
-                                        ])
-                                    },
-                                }
-                            ],
-                        }
-                    ],
+                    code: '111',
+                    name: '醍摩豆学校'
+                },
+                {
+                    code: '222',
+                    name: '研发学校'
                 },
             ],
-            timer: null,
-            treeType: '',
+            teacherList: [
+                {
+                    code: '111',
+                    name: '教研组1'
+                },
+                {
+                    code: '222',
+                    name: '教研组2'
+                },
+            ],
+            schoolChange: [],
+        }
+    },
+    created () {
+        this.processInfo = {
+            name: '',
+            describe: '',
+            score: 0,
+            rule: this.dataRuleMould
         }
     },
     computed: {
@@ -1094,245 +878,83 @@ export default {
             this.createData.file.splice(index, 1)
         },
         addRule() {
-            this.ruleTree.push({
-                title: this.ruleTree.length,
-                expand: true,
+            this.processInfo.rule.push({
+                id: Math.random() * 1000000,
                 name: '',
                 describe: '',
                 score: null,
-                render: (h, { root, node, data }) => {
-                    return h('div', [
-                        h('Input', {
-                            props: {
-                                value: data.name,
-                                placeholder: '一级分项',
-                            },
-                            style: {
-                                width: '20%',
-                                marginRight: '10px',
-                            },
-                            on: {
-                                'on-change': value => {
-                                    console.log(data, value);
-                                    if(value.data !== null) {
-                                        this.inputChange(data, value, 'name')
-                                    }
-                                }
-                            },
-                        }),
-                        h('Input', {
-                            props: {
-                                value: data.describe,
-                                placeholder: '对分项内容进行描述',
-                            },
-                            style: {
-                                width: '65%',
-                                marginRight: '10px',
-                            },
-                            on: {
-                                'on-change': value => {
-                                    console.log(data, value);
-                                    if(value.data !== null) {
-                                        this.inputChange(data, value, 'describe')
-                                    }
-                                }
-                            },
-                        }),
-                        h('Input', {
-                            props: {
-                                value: data.score,
-                            },
-                            style: {
-                                width: '9%',
-                                marginRight: '10px',
-                            },
-                            on: {
-                                'on-change': value => {
-                                    console.log(data, value);
-                                    if(value.data !== null) {
-                                        this.inputChange(data, value, 'score')
-                                    }
-                                }
-                            },
-                        }),
-                        h('span', [
-                            h('Button', {
-                                props: {
-                                    // type: 'error',
-                                    size: 'small',
-                                },
-                                style: {
-                                    marginRight: '10px',
-                                },
-                                on: {
-                                    click:() => {
-                                        this.append(data)
-                                    }
-                                },
-                            }, '插入'),
-                            h('Button', {
-                                props: {
-                                    type: 'error',
-                                    size: 'small',
-                                },
-                                /* style: {
-                                    width: '13%',
-                                }, */
-                                on: {
-                                    click:() => {
-                                        this.remove(root, node, data)
-                                    }
-                                },
-                            }, '删除'),
-                        ])
-                    ])
-                },
             })
-            this.$forceUpdate()
         },
-        append(data, isThree) {
+        append(node, data) {
+            console.log(node.parent.parent);
             const children = data.children || [];
             children.push({
-                title: '101',
-                expand: true,
+                id: Math.random() * 1000000,
                 name: '',
                 describe: '',
                 score: null,
-                render: (h, { root, node, data }) => {
-                    return h('div', [
-                        h('Input', {
-                            props: {
-                                value: data.name,
-                                placeholder: isThree ? '三级分项' : '二级分项',
-                            },
-                            style: {
-                                width: '20%',
-                                marginRight: '10px',
-                            },
-                            on: {
-                                'on-change': value => {
-                                    console.log(data, value);
-                                    if(value.data !== null) {
-                                        this.inputChange(data, value, 'name')
-                                    }
-                                }
-                            },
-                        }),
-                        h('Input', {
-                            props: {
-                                value: data.describe,
-                                placeholder: '对分项内容进行描述',
-                            },
-                            style: {
-                                width: isThree ? '61.3%' : '62.9%',
-                                marginRight: '10px',
-                            },
-                            on: {
-                                'on-change': value => {
-                                    console.log(data, value);
-                                    if(value.data !== null) {
-                                        this.inputChange(data, value, 'describe')
-                                    }
-                                }
-                            },
-                        }),
-                        h('Input', {
-                            props: {
-                                value: data.score,
-                            },
-                            style: {
-                                width: '9%',
-                                marginRight: '10px',
-                            },
-                            on: {
-                                'on-change': value => {
-                                    console.log(data, value);
-                                    if(value.data !== null) {
-                                        this.inputChange(data, value, 'score')
-                                    }
-                                }
-                            },
-                        }),
-                        h('span', {
-                            style: {
-                                display: isThree ? 'none' : 'inline-block'
-                            },
-                        }, [
-                            h('Button', {
-                                props: {
-                                    // type: 'error',
-                                    size: 'small',
-                                },
-                                style: {
-                                    marginRight: '10px',
-                                },
-                                on: {
-                                    click:() => {
-                                        this.append(data, true)
-                                    }
-                                },
-                            }, '插入'),
-                            h('Button', {
-                                props: {
-                                    type: 'error',
-                                    size: 'small',
-                                },
-                                /* style: {
-                                    width: '13%',
-                                }, */
-                                on: {
-                                    click:() => {
-                                        this.remove(root, node, data)
-                                    }
-                                },
-                            }, '删除'),
-                        ]),
-                        
-                        h('Button', {
-                            props: {
-                                type: 'error',
-                                size: 'small',
-                            },
-                            style: {
-                                marginLeft: '50px',
-                                display: isThree ? 'inline-block' : 'none'
-                            },
-                            on: {
-                                click:() => {
-                                    this.remove(root, node, data)
-                                }
-                            },
-                        }, '删除'),
-                    ])
-                },
-            });
+                isThree: node.parent.parent ? true : false
+            })
             this.$set(data, 'children', children)
         },
-        remove(root, node, data) {
-            console.log(root, node, data);
-            const parentKey = root.find(el => el === node).parent;
-            console.log(parentKey);
-            if(parentKey === undefined) {
-                // this.ruleTree = this.ruleTree.filter(i => i.nodeKey !== data.nodeKey)
-                let index = this.ruleTree.findIndex(i => i.nodeKey === data.nodeKey)
-                this.$delete(this.ruleTree,index)
-                console.log(JSON.stringify(this.ruleTree))
-            } else {
-                const parent = root.find(el => el.nodeKey === parentKey).node;
-                console.log(parent);
-                const index = parent.children.indexOf(data);
-                console.log(index);
-                parent.children.splice(index, 1);
-                console.log(parent);
-            }
+        remove(node, data) {
+            const parent = node.parent;
+            const children = parent.data.children || parent.data;
+            const index = children.findIndex(d => d.id === data.id);
+            children.splice(index, 1);
         },
         save() {
-            console.log(this.ruleTree);
-            console.log(JSON.stringify(this.ruleTree))
+            console.log(this.processInfo);
+            console.log(JSON.stringify(this.processInfo))
         },
-        inputChange(data, value, type) {
-            console.log(data, value.target._value, value.data);
-            data[type] = (value.target._value === null ? '' : value.target._value) + (value.data === null ? '' : value.data)
+        reviewEditRule() {
+            this.processInfo = {
+                name: this.ruleInfo.name,
+                describe: this.ruleInfo.describe,
+                score: this.ruleInfo.score,
+                rule: this.ruleInfo.children
+            }
+            this.ruleDrawerAdd = true
+        },
+        drawerRule() {
+            this.processInfo = {
+                name: '',
+                describe: '',
+                score: 0,
+                rule: this.dataRuleMould
+            }
+            this.ruleDrawerAdd = true
+        },
+        addInform() {
+            this.infoArr.push({
+                value: this.addInfo,
+                label: this.addInfo,
+            })
+            this.addInfoType = false
+            this.addInfo = ''
+        },
+        scoreChange(node, data) {
+            // console.log(node, data);
+            this.setParentScore(node)
+        },
+        setParentScore(node) {
+            const parent = node.parent
+            if(!Array.isArray(parent.data)) {
+                let score = 0
+                parent.childNodes.forEach(item => {
+                    score += item.data.score
+                })
+                parent.data.score = score
+            } else {
+                let proScore = 0
+                this.processInfo.score = parent.data.forEach(item => {
+                    proScore += item.score
+                })
+                this.processInfo.score = proScore
+            }
+            if(parent.parent) {
+                this.setParentScore(parent)
+            }
         },
     },
 }
@@ -1388,6 +1010,7 @@ export default {
         }
     }
 
+
 }
 </style>
 <style lang="less">
@@ -1420,4 +1043,30 @@ export default {
         padding-left: 10px;
     }
 }
+
+.rule-box {
+    .edit-title {
+        display: flex;
+        align-items: center;
+        margin-bottom: 10px;
+
+        &>span {
+            width: 100px;
+            text-align: right;
+        }
+    }
+}
+.el-tree-node__content {
+    height: 40px;
+}
+.custom-tree-node {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    width: 100%;
+    .ivu-input-wrapper,
+    .ivu-input-number {
+        margin-right: 10px;
+    }
+}
 </style>

+ 306 - 143
TEAMModelOS/ClientApp/src/view/signupActivity/infoActivity copy.vue

@@ -53,28 +53,32 @@
             <TabPane label="报名数据" name="1">
                 <div class="tab-header join-data">
                     <div>
-                        <p>报名制</p>
                         <p>报名方式</p>
+                        <p>报名制</p>
                     </div>
                     <div>
-                        <p>团队赛</p>
                         <p>参赛方式</p>
+                        <p>团队赛</p>
                     </div>
                     <div>
-                        <p>34/50</p>
                         <p>已报名</p>
+                        <p style="color: #14b53b;">
+                            34
+                            <span style="font-size: 16px; color: #737373;">/50</span>
+                        </p>
                     </div>
                     <div>
-                        <p>27</p>
                         <p>已上传</p>
+                        <p style="color: #4b93ff;">27</p>
                     </div>
                     <div>
-                        <p>10</p>
                         <p>已评审</p>
+                        <p style="color: #ff9900;">10</p>
                     </div>
                 </div>
-                <div>
-                    <Table :columns="applicationColumns" :data="applicationList">
+                <div class="data-box">
+                    <Input placeholder="搜索醍摩豆帐号" style="width: 300px" />
+                    <Table :columns="applicationColumns" :data="applicationList" row-key="id" stripe>
                         <template #action="{row, index}">
                             <Button type="error" size="small" @click="deleteApplica(row, index, 'join')">删除</Button>
                         </template>
@@ -83,7 +87,8 @@
             </TabPane>
             <TabPane label="评审管理" name="2">
                 <div class="tab-header">
-                    <Button>添加评审专家</Button>
+                    <Button @click="processShow = true">添加评审专家</Button>
+                    <Button style="margin-left: 20px;">自动分配评审作品</Button>
                     <Button style="float: right;" @click="ruleDrawer = true">评审规则</Button>
                 </div>
                 <div>
@@ -99,17 +104,37 @@
             </TabPane>
             <TabPane label="成绩统计" name="3">
                 <div class="tab-header">
-                    <Button>公示成绩</Button>
+                    <Button v-show="!awardsing" @click="setAwards()">设置奖项</Button>
+                    <Button v-show="awardsing" @click="awardsShow = true">批量设置</Button>
+                    <Button style="margin-left: 20px;">公示成绩</Button>
                 </div>
-                <div>
-                    <Table :columns="scoreColumns" :data="scoreList">
-                        <!-- <template #actions="{row, index}">
-                            <Button size="small" @click="deleteApplica(row, index, 'score')">修改</Button>
-                        </template> -->
+                <div style="height: 80%;">
+                    <Table :columns="scoreColumns" :data="scoreList" height="600">
+                        <template #awards="{row}">
+                            <span v-if="!awardsing">{{ row.awards }}</span>
+                            <div v-else>
+                                <Select v-model="row.awards" style="width:200px" :transfer="true">
+                                    <Option v-for="item in awardsList" :value="item.value" :key="item.value">{{ item.label }}</Option>
+                                </Select>
+                            </div>
+                        </template>
                     </Table>
                 </div>
             </TabPane>
         </Tabs>
+        <Modal v-model="processShow" title="邀请评审专家" width="800" :footer-hide="true">
+            <Table :columns="inviteColumns" :data="processList">
+                <template #process="{row, index}">
+                    <Progress :percent="25" :stroke-width="10" />
+                </template>
+                <template #actions="{row, index}">
+                    <Button type="error" size="small" @click="deleteApplica(row, index, 'process')">删除</Button>
+                </template>
+            </Table>
+            <div style="margin: 30px 0 10px 0; text-align: center;">
+                <Button type="primary" long>邀请</Button>
+            </div>
+        </Modal>
         <Drawer title="评审规则" :width="50" :closable="false" v-model="ruleDrawer">
             <p style="font-size: 18px; font-weight: bold;">{{ ruleInfo.name }}</p>
             <p>描述:{{ ruleInfo.describe }}</p>
@@ -118,17 +143,11 @@
                 <Table row-key="id" :columns="ruleColumns" :data="ruleInfo.children"></Table>
             </div>
         </Drawer>
-        <!-- <Drawer title="评审规则" :width="50" :closable="false" v-model="ruleDrawer">
-            <p>名称:{{ ruleInfo.name }}</p>
-            <p>描述:{{ ruleInfo.describe }}</p>
-            <p>总分:{{ ruleInfo.score }}</p>
-            <div>
-                <Tree :data="ruleTree" :render="renderContent" @on-contextmenu="handleContextMenu"></Tree>
-            </div>
-        </Drawer> -->
-        <!-- <Modal v-model="modal" title="修改成绩" @on-ok="ok" @on-cancel="cancel">
-            <p></p>
-        </Modal> -->
+        <Modal v-model="awardsShow" title="批量设置奖项">
+            <Select v-model="awardsSel" style="width:200px">
+                <Option v-for="item in awardsList" :value="item.value" :key="item.value">{{ item.label }}</Option>
+            </Select>
+        </Modal>
     </div>
 </template>
 
@@ -141,23 +160,30 @@ export default {
             applicationColumns: [
                 {
                     title: '姓名',
-                    key: 'name'
+                    key: 'name',
+                    tree: true,
+                    // width: 120,
+                    // fixed: 'left'
                 },
                 {
                     title: '性别',
-                    key: 'sex'
+                    key: 'sex',
+                    // width: 80,
                 },
                 {
                     title: '手机号码',
-                    key: 'phone'
+                    key: 'phone',
+                    // width: 100,
                 },
                 {
                     title: '电子邮箱',
-                    key: 'email'
+                    key: 'email',
+                    // width: 500,
                 },
                 {
                     title: '学校',
                     key: 'school',
+                    // width: 100,
                     filters: [
                         {
                             label: '醍摩豆学校',
@@ -175,23 +201,23 @@ export default {
                 },
                 {
                     title: '职务',
-                    key: 'zhiwu'
+                    key: 'zhiwu',
+                    // width: 100,
                 },
                 {
                     title: '学段',
-                    key: 'period'
+                    key: 'period',
+                    // width: 100,
                 },
                 {
                     title: '学科',
-                    key: 'subject'
-                },
-                {
-                    title: '报名时间',
-                    key: 'time'
+                    key: 'subject',
+                    // width: 100,
                 },
                 {
                     title: '组名',
                     key: 'team',
+                    // width: 100,
                     filters: [
                         {
                             label: '数学组',
@@ -210,6 +236,7 @@ export default {
                 {
                     title: '组内身份',
                     key: 'identity',
+                    // width: 100,
                     filters: [
                         {
                             label: '组长',
@@ -225,15 +252,23 @@ export default {
                         return row.identity === value
                     }
                 },
+                {
+                    title: '报名时间',
+                    key: 'time',
+                    // width: 100,
+                },
                 {
                     title: '作品',
-                    key: 'work'
+                    key: 'work',
+                    // width: 100,
+                    // fixed: 'right',
                 },
                 {
                     title: '操作',
                     slot: 'action',
-                    width: 150,
                     align: 'center',
+                    // width: 100,
+                    // fixed: 'right',
                 },
             ],
             processColumns: [
@@ -277,10 +312,41 @@ export default {
                     align: 'center',
                 },
             ],
+            inviteColumns: [
+                {
+                    type: 'selection',
+                    width: 60,
+                    align: 'center'
+                },
+                {
+                    title: '姓名',
+                    key: 'name'
+                },
+                {
+                    title: '性别',
+                    key: 'sex'
+                },
+                {
+                    title: '手机号码',
+                    key: 'phone'
+                },
+                {
+                    title: '电子邮箱',
+                    key: 'email'
+                },
+                /* {
+                    title: '擅长学段',
+                    key: 'period'
+                },
+                {
+                    title: '擅长学科',
+                    key: 'subject'
+                }, */
+            ],
             scoreColumns: [
                 {
                     title: '姓名/组名',
-                    key: 'name'
+                    key: 'name',
                 },
                 {
                     title: '专家评分',
@@ -290,6 +356,10 @@ export default {
                     title: '建议分数',
                     key: 'score1'
                 },
+                {
+                    title: '奖项',
+                    slot: 'awards'
+                },
                 /* {
                     title: '操作',
                     slot: 'actions',
@@ -299,6 +369,7 @@ export default {
             ],
             applicationList: [
                 {
+                    id: '000',
                     name: '张三',
                     sex: '男',
                     phone: '110',
@@ -311,22 +382,42 @@ export default {
                     team: '数学组',
                     identity: '组长',
                     work: '',
+                    _showChildren: true,
+                    children: [
+                        {
+                            id: '001',
+                            name: '李四',
+                            sex: '女',
+                            phone: '911',
+                            email: '331321@qq.com',
+                            school: '醍摩豆学校',
+                            zhiwu: '普通教师',
+                            period: '初中',
+                            subject: '数学',
+                            time: '2023-08-10',
+                            team: '数学组',
+                            identity: '组员',
+                            work: '',
+                        },
+                        {
+                            id: '002',
+                            name: '李四',
+                            sex: '女',
+                            phone: '911',
+                            email: '331321@qq.com',
+                            school: '醍摩豆学校',
+                            zhiwu: '普通教师',
+                            period: '初中',
+                            subject: '数学',
+                            time: '2023-08-10',
+                            team: '数学组',
+                            identity: '组员',
+                            work: '',
+                        },
+                    ],
                 },
                 {
-                    name: '李四',
-                    sex: '女',
-                    phone: '911',
-                    email: '331321@qq.com',
-                    school: '醍摩豆学校',
-                    zhiwu: '普通教师',
-                    period: '初中',
-                    subject: '数学',
-                    time: '2023-08-10',
-                    team: '数学组',
-                    identity: '组员',
-                    work: '',
-                },
-                {
+                    id: '010',
                     name: '柳五',
                     sex: '女',
                     phone: '110',
@@ -339,21 +430,40 @@ export default {
                     team: '语文组',
                     identity: '组长',
                     work: '',
+                    children: [
+                        {
+                            id: '011',
+                            name: '李四',
+                            sex: '女',
+                            phone: '911',
+                            email: '331321@qq.com',
+                            school: '研发学校',
+                            zhiwu: '普通教师',
+                            period: '初中',
+                            subject: '数学',
+                            time: '2023-08-10',
+                            team: '数学组',
+                            identity: '组员',
+                            work: '',
+                        },
+                        {
+                            id: '012',
+                            name: '李四',
+                            sex: '女',
+                            phone: '911',
+                            email: '331321@qq.com',
+                            school: '研发学校',
+                            zhiwu: '普通教师',
+                            period: '初中',
+                            subject: '数学',
+                            time: '2023-08-10',
+                            team: '数学组',
+                            identity: '组员',
+                            work: '',
+                        },
+                    ],
                 },
-                {
-                    name: '李四',
-                    sex: '女',
-                    phone: '911',
-                    email: '331321@qq.com',
-                    school: '研发学校',
-                    zhiwu: '普通教师',
-                    period: '初中',
-                    subject: '数学',
-                    time: '2023-08-10',
-                    team: '数学组',
-                    identity: '组员',
-                    work: '',
-                },
+                
             ],
             processList: [
                 {
@@ -380,11 +490,85 @@ export default {
                     name: '数学组',
                     score: '67',
                     score1: '85',
+                    awards: '',
+                },
+                {
+                    name: '语文组',
+                    score: '92',
+                    score1: '85',
+                    awards: '',
+                },
+                {
+                    name: '数学组',
+                    score: '67',
+                    score1: '85',
+                    awards: '',
+                },
+                {
+                    name: '语文组',
+                    score: '92',
+                    score1: '85',
+                    awards: '',
+                },
+                {
+                    name: '数学组',
+                    score: '67',
+                    score1: '85',
+                    awards: '',
+                },
+                {
+                    name: '语文组',
+                    score: '92',
+                    score1: '85',
+                    awards: '',
+                },
+                {
+                    name: '数学组',
+                    score: '67',
+                    score1: '85',
+                    awards: '',
+                },
+                {
+                    name: '语文组',
+                    score: '92',
+                    score1: '85',
+                    awards: '',
+                },
+                {
+                    name: '数学组',
+                    score: '67',
+                    score1: '85',
+                    awards: '',
+                },
+                {
+                    name: '语文组',
+                    score: '92',
+                    score1: '85',
+                    awards: '',
+                },
+                {
+                    name: '数学组',
+                    score: '67',
+                    score1: '85',
+                    awards: '',
                 },
                 {
                     name: '语文组',
                     score: '92',
                     score1: '85',
+                    awards: '',
+                },
+                {
+                    name: '数学组',
+                    score: '67',
+                    score1: '85',
+                    awards: '',
+                },
+                {
+                    name: '语文组',
+                    score: '92',
+                    score1: '85',
+                    awards: '',
                 },
             ],
             ruleDrawer: false,
@@ -405,77 +589,6 @@ export default {
                     width: 100,
                 },
             ],
-            ruleTree: [
-                {
-                    title: 'parent 1',
-                    expand: true,
-                    render: (h, { root, node, data }) => {
-                        return h('span', {
-                            style: {
-                                display: 'inline-block',
-                                width: '100%'
-                            }
-                        }, [
-                            /* h('span', [
-                                h(resolveComponent('Icon'), {
-                                    type: 'ios-folder-outline',
-                                    style: {
-                                        marginRight: '8px'
-                                    }
-                                }),
-                                h('span', data.title)
-                            ]), */
-                            h('span', {
-                                style: {
-                                    display: 'inline-block',
-                                    float: 'right',
-                                    marginRight: '32px'
-                                }
-                            }, /* [
-                                h(resolveComponent('Button'), {
-                                    ...this.buttonProps,
-                                    icon: 'ios-add',
-                                    type: 'primary',
-                                    style: {
-                                        width: '64px'
-                                    },
-                                    onClick: () => { this.append(data) }
-                                })
-                            ] */)
-                        ]);
-                    },
-                    children: [
-                        {
-                            title: 'child 1-1',
-                            expand: true,
-                            children: [
-                                {
-                                    title: 'leaf 1-1-1',
-                                    expand: true
-                                },
-                                {
-                                    title: 'leaf 1-1-2',
-                                    expand: true
-                                }
-                            ]
-                        },
-                        {
-                            title: 'child 1-2',
-                            expand: true,
-                            children: [
-                                {
-                                    title: 'leaf 1-2-1',
-                                    expand: true
-                                },
-                                {
-                                    title: 'leaf 1-2-1',
-                                    expand: true
-                                }
-                            ]
-                        }
-                    ]
-                }
-            ],
             ruleInfo: {
                 id: '100',
                 name: '创新课堂评价标准',
@@ -557,7 +670,29 @@ export default {
             buttonProps: {
                 type: 'default',
                 size: 'small',
-            }
+            },
+            awardsing: false,
+            awardsList: [
+                {
+                    value: '特等奖',
+                    label: '特等奖'
+                },
+                {
+                    value: '一等奖',
+                    label: '一等奖'
+                },
+                {
+                    value: '二等奖',
+                    label: '二等奖'
+                },
+                {
+                    value: '三等奖',
+                    label: '三等奖'
+                },
+            ],
+            processShow: false,
+            awardsShow: false,
+            awardsSel: '',
         }
     },
     mounted () {
@@ -701,7 +836,16 @@ export default {
             const parent = root.find(el => el.nodeKey === parentKey).node;
             const index = parent.children.indexOf(data);
             parent.children.splice(index, 1);
-        }
+        },
+        setAwards() {
+            this.awardsing = true
+            this.scoreColumns.unshift({
+                type: 'selection',
+                width: 60,
+                align: 'center'
+            })
+            this.$forceUpdate()
+        },
     }
 }
 </script>
@@ -773,16 +917,35 @@ export default {
         display: flex;
         justify-content: space-around;
         width: 100%;
-        margin-bottom: 30px;
+        margin-bottom: 50px;
+        margin-top: 30px;
         &>div {
-            text-align: center;
+            // text-align: center;
             margin: 0 30px;
-            &>p:first-of-type{
+
+            &>p:first-of-type {
+                border-left: 5px solid #70B1F0;
+                padding-left: 5px;
+                height: 15px;
+                line-height: 15px;
+                margin-bottom: 5px;
+            }
+            &>p:nth-child(2){
                 font-size: 30px;
                 font-weight: bold;
+                color: #6a6a6a;
             }
         }
     }
+
+    .data-box {
+        border-top: 1px dashed #d0d0d0;
+        padding-top: 20px;
+
+        .ivu-input-wrapper {
+            margin-bottom: 10px;
+        }
+    }
 }
 </style>
 <style lang="less">

+ 119 - 0
TEAMModelOS/ClientApp/src/view/signupActivity/setActivity.vue

@@ -0,0 +1,119 @@
+<template>
+    <div>
+        <Tabs v-model="activeName">
+            <TabPane label="分站管理" name="subs">
+                <Form :model="formItem" :label-width="120" style="width: 98%;">
+                    <FormItem label="授权单位/组织">
+                        <Input v-model="formItem.unit" placeholder="请输入..." disabled></Input>
+                    </FormItem>
+                    <FormItem label="分站代码">
+                        <Input v-model="formItem.code" placeholder="请输入..." disabled></Input>
+                    </FormItem>
+                    <FormItem label="分站路由">
+                        <Input v-model="formItem.route" placeholder="请输入..." disabled></Input>
+                    </FormItem>
+                </Form>
+            </TabPane>
+            <TabPane label="首页管理" name="homepage">
+                <Table :columns="columns" :data="data" class="banner-set">
+                    <template #type="{row}">
+                        <span>{{ row.type ? '区级' : '公开' }}</span>
+                    </template>
+                    <template #state="{row}">
+                        <Alert :type="row.state === 1 ? 'success' : 'warning'" v-if="row.state">
+                            {{ row.state === 1 ? '进行中' : '已结束' }}
+                        </Alert>
+                        <Alert v-else>未开始</Alert>
+                    </template>
+                    <template #image="{row}">
+                        <img :src="row.image" alt="">
+                    </template>
+                    <template #action="{row}">
+                        <Button type="error" size="small">删除</Button>
+                    </template>
+                </Table>
+            </TabPane>
+        </Tabs>
+    </div>
+</template>
+
+
+<script>
+export default {
+    data () {
+        return {
+            activeName: 'subs',
+            formItem: {
+                unit: '醍摩豆(成都)信息技术有限公司',
+                code: 'TMD',
+                route: '/TeamModel',
+            },
+            columns: [
+                {
+                    title: '活动名称',
+                    key: 'name',
+                    align: 'center',
+                },
+                {
+                    title: '申办人',
+                    key: 'people',
+                    align: 'center',
+                    width: '100',
+                },
+                {
+                    title: '醍摩豆ID',
+                    key: 'tmdid',
+                    align: 'center',
+                    width: '200',
+                },
+                {
+                    title: '活动类型',
+                    slot: 'type',
+                    align: 'center',
+                    width: '100',
+                },
+                {
+                    title: '活动状态',
+                    slot: 'state',
+                    width: '150',
+                    align: 'center',
+                },
+                {
+                    title: '图片',
+                    slot: 'image',
+                    width: '200',
+                    align: 'center',
+                },
+                {
+                    title: '操作',
+                    slot: 'action',
+                    width: '100',
+                    align: 'center',
+                }
+            ],
+            data: [
+                {
+                    name: '2019第八届科技领导卓越奖',
+                    people: '罗老师',
+                    tmdid: '1595321354',
+                    type: 0,
+                    state: 0,
+                    image: require('./demo.jpeg'),
+                },
+            ]
+        }
+    }
+}
+</script>
+
+<style lang="less" scoped>
+.banner-set {
+    img{
+        width: 100%;
+    }
+    .ivu-alert {
+        text-align: center;
+        padding: 5px 0;
+    }
+}
+</style>

+ 14 - 1
TEAMModelOS/ClientApp/src/view/student-web/AppiView.less

@@ -105,4 +105,17 @@
     .myNav .ivu-menu-horizontal .ivu-menu-item {
         padding: 0 11px !important;
     }
-}
+}
+
+@media screen and (max-width: 520px) {
+    .myNav .selectClass {
+        .ivu-select {
+            &:first-child {
+                width: 105px !important;
+            }
+            &:nth-child(2) {
+                width: 150px !important;
+            }
+        }
+    }
+}

+ 4 - 14
TEAMModelOS/Controllers/Both/CourseBaseController.cs

@@ -16,21 +16,11 @@ using Azure;
 using Microsoft.Extensions.Configuration;
 using TEAMModelOS.Filter; 
 using HTEXLib.COMM.Helpers; 
-using System.Globalization;
 using TEAMModelOS.SDK;
-using Microsoft.AspNetCore.Authorization;
 using StackExchange.Redis;
-using Azure.Storage.Blobs.Models;
-using Microsoft.Azure.Amqp.Sasl;
-using DocumentFormat.OpenXml.Drawing.Charts;
-using FastJSON;
-using OpenXmlPowerTools;
-using DocumentFormat.OpenXml.Drawing.Spreadsheet;
+
 using System.Text.RegularExpressions;
-using System.Security.Claims;
-using DocumentFormat.OpenXml.Bibliography;
-using DocumentFormat.OpenXml.Spreadsheet;
-using static SKIT.FlurlHttpClient.Wechat.TenpayV3.Models.CreateApplyForSubjectApplymentRequest.Types;
+using Microsoft.AspNetCore.Authorization;
 
 namespace TEAMModelOS.Controllers.Both
 {
@@ -2045,9 +2035,9 @@ namespace TEAMModelOS.Controllers.Both
         [ProducesDefaultResponseType]
         [AuthToken(Roles = "teacher,admin")]
         [HttpPost("teacher")]
-#if !DEBUG
+ 
         [Authorize(Roles = "IES")]
-#endif
+ 
         public async Task<IActionResult> Teacher(JsonElement request)
         {
 

+ 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
                 }
 
 

+ 628 - 51
TEAMModelOS/Controllers/Client/AClassONEController.cs

@@ -1,9 +1,12 @@
 using Azure.Cosmos;
 using Azure.Storage.Blobs.Models;
+using Azure.Storage.Sas;
 using DocumentFormat.OpenXml.Drawing.Charts;
 using DocumentFormat.OpenXml.Office2010.Excel;
 using DocumentFormat.OpenXml.Office2021.DocumentTasks;
+using DocumentFormat.OpenXml.Presentation;
 using DocumentFormat.OpenXml.Spreadsheet;
+using DocumentFormat.OpenXml.Wordprocessing;
 using HTEXLib.COMM.Helpers;
 using Microsoft.AspNetCore.Authorization;
 using Microsoft.AspNetCore.Http;
@@ -25,6 +28,8 @@ using System.Text;
 using System.Text.Json;
 using System.Threading.Tasks;
 using TEAMModelOS.Controllers.Analysis;
+using TEAMModelOS.Controllers.Both;
+using TEAMModelOS.Filter;
 using TEAMModelOS.Models;
 using TEAMModelOS.SDK;
 using TEAMModelOS.SDK.DI;
@@ -32,8 +37,13 @@ using TEAMModelOS.SDK.Extension;
 using TEAMModelOS.SDK.Models;
 using TEAMModelOS.SDK.Models.Cosmos.Common;
 using TEAMModelOS.SDK.Models.Cosmos.Student;
+using TEAMModelOS.SDK.Models.Service;
 using TEAMModelOS.SDK.Services;
+using TEAMModelOS.Services;
 using static TEAMModelOS.SDK.Services.BlobService;
+using CourseDto = TEAMModelOS.Controllers.Both.CourseDto;
+using Period = TEAMModelOS.SDK.Models.Period;
+
 namespace TEAMModelOS.Controllers
 {
 
@@ -61,13 +71,16 @@ namespace TEAMModelOS.Controllers
         private readonly Option _option;
         private readonly SnowflakeId _snowflakeId;
         private readonly IConfiguration _configuration;
+        private readonly CoreAPIHttpService _coreAPIHttpService;
+        private readonly HttpTrigger _httpTrigger;
+        private readonly IPSearcher _searcher;
         public AClassONEController(
             AzureStorageFactory azureStorage,
             AzureRedisFactory azureRedis,
             AzureCosmosFactory azureCosmos,
             DingDing dingDing,
             SnowflakeId snowflakeId,
-            IOptionsSnapshot<Option> option, IHttpClientFactory httpClient, IConfiguration configuration)
+            IOptionsSnapshot<Option> option, IHttpClientFactory httpClient, IConfiguration configuration, CoreAPIHttpService coreAPIHttpService, IPSearcher searcher, HttpTrigger httpTrigger)
         {
             _azureStorage = azureStorage;
             _azureRedis = azureRedis;
@@ -77,6 +90,9 @@ namespace TEAMModelOS.Controllers
             _option = option?.Value;
             _httpClient = httpClient;
             _configuration = configuration;
+            _coreAPIHttpService=coreAPIHttpService;
+            _searcher=searcher;
+            _httpTrigger=httpTrigger;
         }
 
         /// <summary>
@@ -119,21 +135,527 @@ namespace TEAMModelOS.Controllers
                 return BadRequest(Content);
             }
         }
-
         /// <summary>
-        /// 根据家长手机号获取监护学生的信息
+        /// 获取教师信息
         /// </summary>
         /// <param name="json"></param>
         /// <returns></returns>
         [ProducesDefaultResponseType]
-        [HttpPost("get-students-by-mobile")]
+        [HttpPost("get-teacher-info")]
 #if !DEBUG
         [Authorize(Roles = "AClassONE")]
 #endif
-        public async Task<IActionResult> GetStudentsByPhone(JsonElement json) {
+        public async Task<IActionResult> GetTeacherInfo(JsonElement json)
+        {
+            string head_lang = "";
+
+            if (HttpContext.Request.Headers.TryGetValue("lang", out var _lang))
+            {
+                head_lang = $"{_lang}";
+            }
+            if (!json.TryGetProperty("code", out JsonElement _code)) return BadRequest("code is null");
+            var phoneInfo = await GetWeChatPhoneNumber(_code.ToString());
+            if (phoneInfo.code==200)
+            {
+                string _mobile = phoneInfo.phone.phoneNumber;
+                var coreUser = await _coreAPIHttpService.GetUserInfo(new Dictionary<string, string> { { "key", $"{_mobile}" } }, _option.Location, _configuration);
+                if (coreUser != null && coreUser.id != null)
+                {
+                    (string ip, string region) = await LoginService.LoginIp(HttpContext, _searcher);
+                    Teacher teacher = null;
+                    TeacherInfo teacherInfo = await TeacherService.TeacherInfo(_azureCosmos, teacher, $"{coreUser.name}", $"{coreUser.picture}", coreUser.id, _azureStorage, _option, _azureRedis, ip, _httpTrigger, $"{_lang}");
+                    return Ok(new
+                    {
+                        teacherInfo.auth_token,
+                        teacherInfo.blob_uri,
+                        teacherInfo.blob_sas,
+                        teacherInfo.schools,
+                        teacherInfo.defaultschool,
+                    });
+                }
+                else return Ok(new { state = 404, msg = "未找到关联账号" });
+            }
             return Ok();
         }
+        /// <summary>
+        /// 获取学校信息
+        /// </summary>
+        /// <param name="json"></param>
+        /// <returns></returns>
+        [ProducesDefaultResponseType]
+        [HttpPost("get-school-info")]
+#if !DEBUG
+        [Authorize(Roles = "AClassONE")]
+#endif
+        [AuthToken(Roles = "teacher")]
+        public async Task<IActionResult> GetSchoolInfo(JsonElement json) {
+            (string tmdid, _, _, string school) = HttpContext.GetAuthTokenInfo();
+            if (!json.TryGetProperty("school_code", out JsonElement _school_code)) return BadRequest();
+            string school_code = $"{_school_code}";
+            List<string> roles = new List<string>();
+            List<string> permissions = new List<string>();
+            var client = _azureCosmos.GetCosmosClient();
+            int size = 0;
+            Teacher teacher = await client.GetContainer(Constant.TEAMModelOS, "Teacher").ReadItemAsync<Teacher>(tmdid, new PartitionKey("Base"));
+            List<AreaDto> areas = new List<AreaDto>();
+            HashSet<string> areaIds = new HashSet<string>();
+            if (teacher.areas.IsNotEmpty())
+            {
+                teacher.areas.ForEach(x => {
+                    areaIds.Add(x.areaId);
+                });
+            }
+            if (teacher.schools.IsNotEmpty())
+            {
+                teacher.schools.ForEach(x => {
+                    if (!string.IsNullOrEmpty(x.areaId))
+                    {
+                        areaIds.Add(x.areaId);
+                    }
 
+                });
+            }
+            List<Area> areasDbs = new List<Area>();
+            List<AreaSetting> areaSettings = new List<AreaSetting>();
+            if (areaIds.Count > 0)
+            {
+                string queryText = $"select value(c) from c where c.id in ({string.Join(",", areaIds.Select(x => $"'{x}'"))})";
+                await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "Normal").GetItemQueryIterator<Area>(queryText: queryText, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("Base-Area") }))
+                {
+                    areasDbs.Add(item);
+                }
+                await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "Normal").GetItemQueryIterator<AreaSetting>(queryText: queryText, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("AreaSetting") }))
+                {
+                    areaSettings.Add(item);
+                }
+            }
+            if (teacher.areas.IsNotEmpty())
+            {
+                foreach (var areat in teacher.areas)
+                {
+
+                    Area area = areasDbs.Find(x => x.id.Equals(areat.areaId));
+                    AreaSetting setting = null;
+                    if (area != null)
+                    {
+                        setting = areaSettings.Find(x => x.id.Equals(areat.areaId));
+                    }
+
+                    int access = 0;
+                    if (setting != null && !string.IsNullOrWhiteSpace(setting.accessConfig))
+                    {
+                        access = 1;
+                    }
+                    //if (setting != null)
+                    //{
+                    //    setting.accessConfig = null;
+                    //}
+                    areas.Add(new AreaDto { shortCode=area?.shortCode, areaId = area?.id, name = area?.name, standard = area?.standard, standardName = area?.standardName, setting = setting, access = access });
+                }
+            }
+            if (school_code.Equals(teacher.defaultSchool) && teacher.schools.IsNotEmpty() && !teacher.schools.Select(x => x.schoolId).Contains(school_code))
+            {
+                school_code = teacher.schools[0].schoolId;
+                teacher.defaultSchool = school_code;
+                teacher = await client.GetContainer(Constant.TEAMModelOS, "Teacher").ReplaceItemAsync<Teacher>(teacher, tmdid, new PartitionKey("Base"));
+            }
+            var response = await client.GetContainer(Constant.TEAMModelOS, "School").ReadItemStreamAsync(tmdid, new PartitionKey($"Teacher-{school_code}"));
+            string defaultPeriodId = "";
+            if (response.Status == 200)
+            {
+                using var jsonData = await JsonDocument.ParseAsync(response.ContentStream);
+                if (jsonData.RootElement.TryGetProperty("size", out JsonElement _size) && _size.ValueKind == JsonValueKind.Number)
+                {
+                    size = _size.GetInt32();
+                }
+                if (jsonData.RootElement.TryGetProperty("periodId", out JsonElement _periodId))
+                {
+                    defaultPeriodId =$"{_periodId}";
+                }
+                if (jsonData.RootElement.TryGetProperty("roles", out JsonElement _roles) && _roles.ValueKind != JsonValueKind.Null)
+                {
+                    foreach (var obj in _roles.EnumerateArray())
+                    {
+                        roles.Add(obj.GetString());
+                    }
+                }
+                if (jsonData.RootElement.TryGetProperty("permissions", out JsonElement _permissions) && _permissions.ValueKind != JsonValueKind.Null)
+                {
+                    foreach (var obj in _permissions.EnumerateArray())
+                    {
+                        permissions.Add(obj.GetString());
+                    }
+                }
+                var scteacher = jsonData.RootElement.ToObject<SchoolTeacher>();
+                if (!$"{teacher.name}".Equals($"{scteacher?.name}") || !$"{teacher.picture}".Equals($"{scteacher?.picture}"))
+                {
+                    scteacher.name=teacher.name;
+                    scteacher.picture=teacher.picture;
+                    await client.GetContainer(Constant.TEAMModelOS, "School").ReplaceItemAsync<SchoolTeacher>(scteacher, scteacher.id, new PartitionKey(scteacher.code));
+                }
+            }
+            if (roles.Count == 0)
+            {
+                //助理,管家
+                //roles.Add("assist");
+                roles.Add("teacher");
+            }
+            School school_base = await client.GetContainer(Constant.TEAMModelOS, "School").ReadItemAsync<School>($"{school_code}", new PartitionKey("Base"));
+            string currAreaId = "";
+            dynamic currArea = new ExpandoObject();
+            if (!string.IsNullOrEmpty(school_base.areaId))
+            {
+                try
+                {
+                    Area area = areasDbs.Find(x => x.id.Equals(school_base.areaId));
+                    AreaSetting setting = null;
+                    if (area != null)
+                    {
+                        //setting =await client.GetContainer(Constant.TEAMModelOS, "Normal").ReadItemAsync<AreaSetting>(school_base.areaId, new PartitionKey("AreaSetting"));
+                        setting = areaSettings.Find(x => x.id.Equals(school_base.areaId));
+                    }
+
+                    int access = 0;
+                    AccessConfig accessConfig = null;
+                    if (setting != null && !string.IsNullOrWhiteSpace(setting.accessConfig))
+                    {
+                        access = 1;
+                        accessConfig = setting.accessConfig.ToObject<AccessConfig>();
+                    }
+                    //if (setting!=null&& !string.IsNullOrEmpty(setting.accessConfig)) {
+                    //    setting.accessConfig = null;
+                    //}
+                    currArea = new
+                    {
+                        shortCode = area?.shortCode,
+                        areaId = area?.id,
+                        name = area?.name,
+                        standard = area?.standard,
+                        standardName = area?.standardName,
+                        setting = setting,
+                        access = access,
+                        //submitType=accessConfig?.submitType,
+                        homeworkType = accessConfig != null && accessConfig.homeworkType.IsNotEmpty() ? accessConfig.homeworkType : new List<string> { "pdf" },
+                    };
+                    currAreaId = area?.id;
+                }
+                catch (CosmosException)
+                {
+                    //数据库捞不到数据
+
+                }
+            }
+            if (areas.Count > 0)
+            {
+                roles.Add("area");
+            }
+            areas.ForEach(x => { { if (x.setting != null) { x.setting.accessConfig = x.setting.accessConfig; } } });
+
+            var auth_token = JwtAuthExtension.CreateAuthToken(_option.HostName,teacher. id, teacher.name, teacher.picture, _option.JwtSecretKey, Website: "IES", scope: Constant.ScopeTeacher, schoolID: school_code.ToString(), areaId: currAreaId, standard: school_base.standard, roles: roles.ToArray(), permissions: permissions.ToArray(), expire: 1);
+            string school_code_blob = school_code.ToLower();
+            var container = _azureStorage.GetBlobContainerClient(school_code_blob);
+            await container.CreateIfNotExistsAsync(PublicAccessType.None); //嘗試創建School容器,如存在則不做任何事,保障容器一定存在
+            var (blob_uri, blob_sas) = (roles.Contains("admin") || permissions.Contains("schoolAc-upd")) ? _azureStorage.GetBlobContainerSAS(school_code_blob, BlobContainerSasPermissions.Write | BlobContainerSasPermissions.Read | BlobContainerSasPermissions.List | BlobContainerSasPermissions.Delete) : _azureStorage.GetBlobContainerSAS(school_code_blob, BlobContainerSasPermissions.Read | BlobContainerSasPermissions.List | BlobContainerSasPermissions.Write);
+
+            return Ok(new {
+                auth_token,
+                blob_uri,
+                blob_sas,
+                school_base,
+                areas,
+                currArea,
+            });
+        }
+        /// <summary>
+        /// 获取教师的教学信息
+        /// </summary>
+        /// <param name="json"></param>
+        /// <returns></returns>
+        [ProducesDefaultResponseType]
+        [HttpPost("get-teach-info")]
+#if !DEBUG
+        [Authorize(Roles = "AClassONE")]
+#endif
+        [AuthToken(Roles = "teacher")]
+        public async Task<IActionResult> GetTeachInfo(JsonElement json) {
+            (string tmdid, _, _, string school) = HttpContext.GetAuthTokenInfo();
+            List<KeyValuePair<string, CourseTask>> schoolTeacherTask = new List<KeyValuePair<string, CourseTask>>();
+            List<KeyValuePair<string, CourseTask>> schoolAssistantTask = new List<KeyValuePair<string, CourseTask>>();
+
+            List<KeyValuePair<string, CourseTask>> privateTeacherTask = new List<KeyValuePair<string, CourseTask>>();
+            List<KeyValuePair<string, CourseTask>> privateAssistantTask = new List<KeyValuePair<string, CourseTask>>();
+            List<Both.CourseDto> schoolCourses = new List<CourseDto>();
+            List<CourseDto> teahcerCourses = new List<CourseDto>();
+            List<string> groupIds = new List<string>();
+            HashSet<string> hashGroupIds = new HashSet<string>();
+            List<Class> classes = new List<Class>();
+            Period periodSchool = null;
+            if (!string.IsNullOrWhiteSpace(school)) {
+                //班主任
+                string sqlClassTeacher = $"select value c from c where c.teacher.id='{tmdid}'";
+                var resultClass =   await  _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.School).GetList<Class>(sqlClassTeacher, $"Class-{school}");
+                if (resultClass.list.IsNotEmpty()) 
+                {
+                    classes.AddRange(resultClass.list);
+                    hashGroupIds = new HashSet<string>(resultClass.list.Select(z=>z.id));
+                }
+                //执教
+                //协同
+                //管理员
+                string sql = $"SELECT distinct value c FROM c  join b in c.schedules where c.pk='CourseTask' and (ARRAY_CONTAINS(b.assistants,'{tmdid}')or  b.teacherId  ='{tmdid}' )";
+               
+                School schoolBase = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.School).ReadItemAsync<School>(school, new PartitionKey("Base"));
+                JsonElement year = default, semesterId = default, _periodId = default;
+                int _year = -1;
+                string _semesterId=string.Empty;
+                if (!json.TryGetProperty("periodId", out _periodId)) return BadRequest();
+                if (json.TryGetProperty("year", out year))
+                {
+                    _year= int.Parse($"{_year}");
+                }
+                if (json.TryGetProperty("semesterId", out semesterId))
+                {
+                    _semesterId=$"{semesterId}";
+                }
+             
+                var period = schoolBase.period.Find(x => x.id.Equals($"{_periodId}"));
+                periodSchool=period;
+                if (_year==-1 || string.IsNullOrWhiteSpace(_semesterId))
+                {
+
+                    var semesterInfo = SchoolService.GetSemester(period, DateTimeOffset.UtcNow.ToUnixTimeMilliseconds());
+                    if (_year==-1) {
+                        _year=semesterInfo.studyYear;                    
+                    }
+                    if (string.IsNullOrWhiteSpace(_semesterId)) {
+                        _semesterId=semesterInfo.currSemester.id;
+                    }
+                }
+               
+                //string date = SchoolService.GetOpensByStudyYearAndSemester(period.semesters, int.Parse($"{_year}"), $"{_semesterId}");
+                sql = $"{sql} and  c.year={_year} and c.semesterId='{_semesterId}'";
+                HashSet<string> courseIds = new HashSet<string>();
+                var resultSchool = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.School).GetList<CourseTask>(sql, $"CourseTask-{school}");
+                if (resultSchool.list.IsNotEmpty())
+                {
+                    resultSchool.list.ForEach(x => {
+                        var schedulesTeacher = x.schedules.Where(z => !string.IsNullOrWhiteSpace(z.teacherId)   && z.teacherId.Equals(tmdid));
+                        if (schedulesTeacher.Any())
+                        {
+                            courseIds.Add(x.courseId);
+                            CourseTask courseTask = x.ToJsonString().ToObject<CourseTask>();
+                            courseTask.schedules=schedulesTeacher.ToList();
+                            schoolTeacherTask.Add(new KeyValuePair<string, CourseTask>(x.courseId, courseTask));
+
+                            //groupIds.AddRange(schedulesTeacher.Where(z => !string.IsNullOrWhiteSpace(z.groupId)).Select(x => x.groupId));
+                            var gpids=  schedulesTeacher.Where(z => !string.IsNullOrWhiteSpace(z.groupId)).Select(x => x.groupId);
+                            if (gpids!=null  && gpids.Count()>0) 
+                            {
+                                foreach (var gp in gpids) {
+                                    hashGroupIds.Add(gp);
+                                }
+                            }
+                        }
+                        var schedulesAssistant = x.schedules.Where(z => z.assistants.Contains(tmdid));
+                        if (schedulesAssistant.Any())
+                        {
+                            courseIds.Add(x.courseId);
+                            CourseTask courseTask = x.ToJsonString().ToObject<CourseTask>();
+                            courseTask.schedules=schedulesAssistant.ToList();
+                            schoolAssistantTask.Add(new KeyValuePair<string, CourseTask>(x.courseId, courseTask));
+                            //groupIds.AddRange(schedulesAssistant.Where(z => !string.IsNullOrWhiteSpace(z.groupId)).Select(x => x.groupId));
+                            var gpids = schedulesAssistant.Where(z => !string.IsNullOrWhiteSpace(z.groupId)).Select(x => x.groupId);
+                            if (gpids!=null  && gpids.Count()>0)
+                            {
+                                foreach (var gp in gpids)
+                                {
+                                    hashGroupIds.Add(gp);
+                                }
+                            }
+                        }
+                    });
+                }
+                if (courseIds.Any())
+                {
+                    string sqlCourse = $"select value c from c where c.id in ({string.Join(",", courseIds.Select(b => $"'{b}'"))})";
+                    var result = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.School).GetList<CourseBase>(sqlCourse, $"CourseBase-{school}");
+                    if (result.list.IsNotEmpty())
+                    {
+                        foreach (var item in result.list)
+                        {
+                            List<CourseTaskDto> courseTaskDtos = new List<CourseTaskDto>();
+                            var teacher = schoolTeacherTask.Where(x => x.Key.Equals(item.id)).Select(z => new CourseTaskDto { courseTask=z.Value, type="teacher" });
+                            if (teacher.Any())
+                            {
+                                courseTaskDtos.AddRange(teacher.ToList());
+                            }
+                            var assistant = schoolAssistantTask.Where(x => x.Key.Equals(item.id)).Select(z => new CourseTaskDto { courseTask=z.Value, type="assistant" });
+                            if (assistant.Any())
+                            {
+                                courseTaskDtos.AddRange(assistant.ToList());
+                            }
+                            schoolCourses.Add(new Both.CourseDto { courseBase=item, courseTasks=courseTaskDtos });
+                        }
+                    }
+                }
+            }
+
+
+            //个人
+            {
+                HashSet<string> courseIds = new HashSet<string>();
+                string sqlCoursePrivate = $"select value c.id from c where  c.creatorId='{tmdid}'";
+                var resultCourseBasePrivate = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Teacher).GetList<string>(sqlCoursePrivate, $"CourseBase");
+                if (resultCourseBasePrivate.list.IsNotEmpty())
+                {
+                    courseIds=new HashSet<string>(resultCourseBasePrivate.list);
+                }
+                string sqlprivate = $"SELECT distinct value c FROM c  join b in c.schedules where c.pk='CourseTask' and (ARRAY_CONTAINS(b.assistants,'{tmdid}')or  b.teacherId  ='{tmdid}' )";
+                var resultTeacher = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Teacher).GetList<CourseTask>(sqlprivate, $"CourseTask");
+                if (resultTeacher.list.IsNotEmpty())
+                {
+
+                    resultTeacher.list.ForEach(x => {
+                        var schedulesTeacher = x.schedules.Where(z => !string.IsNullOrWhiteSpace(z.teacherId)  &&  z.teacherId.Equals(tmdid));
+                        if (schedulesTeacher.Any())
+                        {
+                            courseIds.Add(x.courseId);
+                            CourseTask courseTask = x.ToJsonString().ToObject<CourseTask>();
+                            courseTask.schedules=schedulesTeacher.ToList();
+                            privateTeacherTask.Add(new KeyValuePair<string, CourseTask>(x.courseId, courseTask));
+                            groupIds.AddRange(schedulesTeacher.Where(z => !string.IsNullOrWhiteSpace(z.groupId)).Select(x => x.groupId));
+                            var gpids = schedulesTeacher.Where(z => !string.IsNullOrWhiteSpace(z.groupId));
+                            if (gpids!=null  && gpids.Count()>0)
+                            {
+                                foreach (var gp in gpids)
+                                {
+                                    if (!string.IsNullOrWhiteSpace(gp.school))
+                                    {
+                                        if (!string.IsNullOrWhiteSpace(school) && gp.school.Equals(school))
+                                        {
+                                            hashGroupIds.Add(gp.groupId);
+                                        }
+                                    }
+                                    else {
+                                        hashGroupIds.Add(gp.groupId);
+                                    }
+                                   
+                                }
+                            }
+                        }
+                        var schedulesAssistant = x.schedules.Where(z => z.assistants.Contains(tmdid));
+                        if (schedulesAssistant.Any())
+                        {
+                            courseIds.Add(x.courseId);
+                            CourseTask courseTask = x.ToJsonString().ToObject<CourseTask>();
+                            courseTask.schedules=schedulesAssistant.ToList();
+                            privateAssistantTask.Add(new KeyValuePair<string, CourseTask>(x.courseId, courseTask));
+                            groupIds.AddRange(schedulesAssistant.Where(z => !string.IsNullOrWhiteSpace(z.groupId)).Select(x => x.groupId));
+                            var gpids = schedulesAssistant.Where(z => !string.IsNullOrWhiteSpace(z.groupId));
+                            if (gpids!=null  && gpids.Count()>0)
+                            {
+                                foreach (var gp in gpids)
+                                {
+                                    if (!string.IsNullOrWhiteSpace(gp.school))
+                                    {
+                                        if (!string.IsNullOrWhiteSpace(school) && gp.school.Equals(school)) {
+                                            hashGroupIds.Add(gp.groupId);
+                                        }
+                                    }
+                                    else
+                                    {
+                                        hashGroupIds.Add(gp.groupId);
+                                    }
+                                }
+                            }
+                        }
+                    });
+
+                }
+                if (courseIds.Any())
+                {
+                    string sqlCourse = $"select distinct value c from c where c.code='CourseBase' and  ( c.creatorId='{tmdid}' or  c.id in ({string.Join(",", courseIds.Select(b => $"'{b}'"))}))";
+                    var result = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Teacher).GetList<CourseBase>(sqlCourse, $"CourseBase");
+                    if (result.list.IsNotEmpty())
+                    {
+                        foreach (var item in result.list)
+
+                        {
+                            List<CourseTaskDto> courseTaskDtos = new List<CourseTaskDto>();
+                            var teacher = privateTeacherTask.Where(x => x.Key.Equals(item.id)).Select(z => new CourseTaskDto { courseTask=z.Value, type="teacher" });
+                            if (teacher.Any())
+                            {
+                                courseTaskDtos.AddRange(teacher.ToList());
+                            }
+                            var assistant = privateAssistantTask.Where(x => x.Key.Equals(item.id)).Select(z => new CourseTaskDto { courseTask=z.Value, type="assistant" });
+                            if (assistant.Any())
+                            {
+                                courseTaskDtos.AddRange(assistant.ToList());
+                            }
+                            teahcerCourses.Add(new CourseDto { courseBase=item, courseTasks=courseTaskDtos });
+                        }
+                    }
+                }
+            }
+            var groupInfo = await  GroupListService.GetMemberByListids(_coreAPIHttpService, _azureCosmos.GetCosmosClient(), _dingDing, hashGroupIds.ToList(), school);
+            var groupIdsRel = groupIds.GroupBy(z => z).Select(z => new { groupId = z.Key, count = z.ToList().Count() });
+            List<dynamic> groupLists= new List<dynamic>();
+            foreach (var item in teahcerCourses)
+            {
+                
+                foreach (var z in item.courseTasks) {
+                    var groups = z.courseTask.schedules.Select(z => z.groupId);
+                    if (groups!=null) {
+                        var groupList = groupInfo.groups.Where(z => groups.Contains(z.id));
+                        if (groupList!=null)
+                        {
+                            foreach (var gp in groupList) {
+                                string subjectId = null;
+                                string subjectName = null;
+                                string subjectBindId = null;
+                                groupLists.Add(new { role = z.type,scope= "private", groupList= gp, subjectId, subjectName , subjectBindId });
+                            }
+                        }
+                    }
+                }
+            }
+            foreach (var item in schoolCourses)
+            {
+                string subjectBindId = null;
+                if (!string.IsNullOrWhiteSpace(school) && periodSchool!=null  && item.courseBase.scope.Equals("school") && !string.IsNullOrWhiteSpace(item.courseBase?.subject?.id))
+                {
+                    subjectBindId=periodSchool.subjects.Find(z => z.id.Equals(item.courseBase.subject.id))?.bindId;
+                }
+                foreach (var z in item.courseTasks)
+                {
+                    var groups = z.courseTask.schedules.Select(z => z.groupId);
+                    if (groups!=null)
+                    {
+                        var groupList = groupInfo.groups.Where(z => groups.Contains(z.id));
+                        if (groupList!=null)
+                        {
+                            foreach (var gp in groupList)
+                            {
+
+                                groupLists.Add(new { role = z.type, scope = "school", groupList = gp, subjectId = item.courseBase?.subject?.id, subjectName = item.courseBase?.subject?.name, subjectBindId });
+                            }
+                        }
+                    }
+                }
+            }
+            foreach (var item in classes) {
+                var groupList = groupInfo.groups.Where(z => item.id.Equals(z.id));
+                if (groupList!=null)
+                {
+                    foreach (var gp in groupList)
+                    {
+
+                        groupLists.Add(new { role = "charge", scope = "school", groupList = gp });
+                    }
+                }
+            }
+            return Ok(new { teahcerCourses, schoolCourses, groupIdsRel, groupLists });
+        }
         /// <summary>
         /// 根据家长手机号获取监护学生的信息
         /// </summary>
@@ -367,51 +889,6 @@ namespace TEAMModelOS.Controllers
                     arts = result.list;
                     token = result.continuationToken;
                 }
-                //await foreach (var item in client.GetContainer("TEAMModelOS", "Common").GetItemQueryStreamIterator(queryText: stringBuilder.ToString(), continuationToken: token, requestOptions: new QueryRequestOptions() { MaxItemCount = topcout, PartitionKey = new PartitionKey($"Art-{code}") }))
-                //{
-
-                //    using var json = await JsonDocument.ParseAsync(item.ContentStream);
-                //    if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
-                //    {
-                //        foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
-                //        {
-                //            arts.Add(obj.ToObject<ArtEvaluation>());
-                //        }
-                //    }
-                //    if (iscontinuation)
-                //    {
-                //        continuationToken = item.GetContinuationToken();
-                //        break;
-                //    }
-                //}
-                // arts = arts.Where((x, i) => arts.FindIndex(z => z.id == x.id) == i).ToList();
-                //List<(string artId, double count)> attachments = new();
-               /* foreach (ArtEvaluation art in arts) {
-                    List<StudentArtResult> artResults = new();
-                    string stu = string.Format("{0}{1}{2}", code.GetString(), "-", stuId.GetString());
-                    string sql = $"select value(c) from c where c.artId = '{art.id}' and c.id = '{stu}' and c.pk = 'ArtResult'";
-                    await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "Student").GetItemQueryStreamIterator(
-                    queryText: sql))
-                    {
-                        using var json = await JsonDocument.ParseAsync(item.ContentStream);
-                        if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
-                        {
-                            foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
-                            {
-                                artResults.Add(obj.ToObject<StudentArtResult>());
-                            }
-                        }
-                    }
-                    if (artResults.Count > 0)
-                    {
-                        var attCount = artResults[0].results.Where(z => z.files != null).SelectMany(c => c.files).ToList().Count;
-                        attachments.Add((art.id, attCount));
-                    }
-                    else {
-                        attachments.Add((art.id, 0));
-                    }
-                   
-                }*/
                 List<string> artIds = new();
                 artIds = arts.Select(c => c.id).ToList();
                 List<ArtAttachment> artAttachments = new();
@@ -442,7 +919,7 @@ namespace TEAMModelOS.Controllers
                     c.period,
                     c.periodType,
                     c.zymusicstds,c.code,
-                    count = artAttachments.Where(z => z.artId.Equals(c.id)).SelectMany(k => k.files).ToList().Count
+                    count = artAttachments.Where(z => z.artId.Equals(c.id)).ToList().Count
 
                 });
                 return Ok(new { arts = newArts ,token});
@@ -451,7 +928,95 @@ namespace TEAMModelOS.Controllers
                 return BadRequest("500错误");
             }
         }
+        /// <summary>
+        /// 获取当前老师所在范围的活动列表
+        /// </summary>
+        /// <param name="json"></param>
+        /// <returns></returns>
+        [ProducesDefaultResponseType]
+        [HttpPost("find-teacher-activity")]
+#if !DEBUG
+        [Authorize(Roles = "AClassONE")]
+#endif
+        public async Task<IActionResult> getTeacherActivity(JsonElement request)
+        {
+            try
+            {
+                if (!request.TryGetProperty("studentId", out JsonElement teacId)) return BadRequest();
+                if (!request.TryGetProperty("classIds", out JsonElement classIds)) return BadRequest();
+                if (!request.TryGetProperty("code", out JsonElement code)) return BadRequest();//学校编码
+                List<string> cIds = classIds.ToObject<List<string>>();
+                var client = _azureCosmos.GetCosmosClient();
+                StringBuilder stringBuilder = new($"select value(c) from c where (c.status<>404 or IS_DEFINED(c.status) = false )");
+                string continuationToken = string.Empty;
+                string token = default;
+                stringBuilder.Append("order by c.createTime desc");
+                //是否需要进行分页查询,默认不分页
+                if (request.TryGetProperty("token", out JsonElement token_1))
+                {
+                    token = token_1.GetString();
+                };
+                //默认不指定返回大小
+                int? topcout = null;
+                if (request.TryGetProperty("count", out JsonElement jcount))
+                {
+                    if (!jcount.ValueKind.Equals(JsonValueKind.Undefined) && !jcount.ValueKind.Equals(JsonValueKind.Null) && jcount.TryGetInt32(out int data))
+                    {
+                        topcout = data;
+                    }
+                }
+
+                List<ArtEvaluation> arts = new();
+                var result = await client.GetContainer(Constant.TEAMModelOS, Constant.Common).GetList<ArtEvaluation>(stringBuilder.ToString(), $"Art-{code}", token, 5);
+                if (result.list.IsNotEmpty())
+                {
+                    arts = result.list;
+                    token = result.continuationToken;
+                }
+                arts = arts.Where(c => c.classes.Intersect(cIds).Any()).Where((x, i) => arts.FindIndex(z => z.id == x.id) == i).ToList();
+                List<string> artIds = new();
+                artIds = arts.Select(c => c.id).ToList();
+                List<ArtAttachment> artAttachments = new();
+                string sqlTask = $"select value(c)  from c where c.studentId = '{teacId}' and c.artId in ({string.Join(",", artIds.Select(s => $"'{s}'"))})";
+                await foreach (var item in _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Student).
+                GetItemQueryIterator<ArtAttachment>(queryText: sqlTask, requestOptions: new QueryRequestOptions { PartitionKey = new PartitionKey($"ArtAttachment-{code}") }))
+                {
+                    artAttachments.Add(item);
+                }
+                var newArts = arts.Select(c => new {
 
+                    c.id,
+                    c.name,
+                    c.school,
+                    c.createTime,
+                    c.type,
+                    c.classes,
+                    c.progress,
+                    c.scope,
+                    c.areaId,
+                    c.pId,
+                    c.topic,
+                    c.startTime,
+                    c.endTime,
+                    c.uploadSTime,
+                    c.uploadETime,
+                    c.publish,
+                    c.subjects,
+                    c.period,
+                    c.periodType,
+                    c.zymusicstds,
+                    c.code,
+                    count = artAttachments.Where(z => z.artId.Equals(c.id)).ToList().Count
+
+                });
+                return Ok(new { arts = newArts, token });
+            }
+            catch (Exception e)
+            {
+                await _dingDing.SendBotMsg($"OS,{_option.Location},art/find-children-activity()\n{e.Message}\n{e.StackTrace}\n", GroupNames.醍摩豆服務運維群組);
+                return BadRequest("500错误");
+            }
+        }
 
         [ProducesDefaultResponseType]
         [HttpPost("find-summary-activity")]
@@ -554,6 +1119,18 @@ namespace TEAMModelOS.Controllers
                 var client = _azureCosmos.GetCosmosClient();
                 if (!element.TryGetProperty("id", out JsonElement id)) return BadRequest();
                 if (!element.TryGetProperty("code", out JsonElement code)) return BadRequest();
+                ArtAttachment attachment = new();
+                var res = await client.GetContainer(Constant.TEAMModelOS, "Student").ReadItemStreamAsync(id.ToString(), new PartitionKey($"ArtAttachment-{code}"));
+                if (res.Status == 200)
+                {
+                    using var json = await JsonDocument.ParseAsync(res.ContentStream);
+                    attachment = json.ToObject<ArtAttachment>();
+
+                }
+                List<Attachment> atts = attachment.files;
+                foreach (Attachment att in atts) {
+                    await _azureStorage.GetBlobServiceClient().DeleteBlobs(_dingDing, code.GetString(), new List<string> { $"{att.blob}" });
+                }
                 await client.GetContainer("TEAMModelOS", "Student").DeleteItemStreamAsync(id.GetString(), new PartitionKey($"ArtAttachment-{code}"));
                 return Ok();
             }

+ 131 - 0
TEAMModelOS/Controllers/Common/ActivityController.cs

@@ -0,0 +1,131 @@
+using Azure.Cosmos;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.Options;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Text.Json;
+using System.Threading.Tasks;
+using TEAMModelOS.Models;
+using TEAMModelOS.SDK.Models;
+using TEAMModelOS.SDK.DI;
+using TEAMModelOS.SDK.Extension;
+using Azure;
+using Microsoft.Extensions.Configuration;
+using TEAMModelOS.Filter;
+using HTEXLib.COMM.Helpers;
+using TEAMModelOS.SDK;
+using StackExchange.Redis;
+
+using System.Text.RegularExpressions;
+using Microsoft.AspNetCore.Authorization;
+
+namespace TEAMModelOS.Controllers
+{
+ 
+
+    [ProducesResponseType(StatusCodes.Status200OK)]
+    [ProducesResponseType(StatusCodes.Status400BadRequest)]
+
+    [Route("activity")]
+    [ApiController]
+    public class ActivityController : ControllerBase
+    {
+        private AzureCosmosFactory _azureCosmos;
+        private readonly DingDing _dingDing;
+        private readonly CoreAPIHttpService _coreAPIHttpService;
+        private readonly Option _option;
+        private readonly AzureServiceBusFactory _serviceBus;
+        private readonly AzureStorageFactory _azureStorage;
+        private readonly AzureRedisFactory _azureRedis;
+        public IConfiguration _configuration { get; set; }
+        public ActivityController(AzureRedisFactory azureRedis, AzureCosmosFactory azureCosmos, DingDing dingDing, IOptionsSnapshot<Option> option, CoreAPIHttpService coreAPIHttpService, AzureServiceBusFactory serviceBus, AzureStorageFactory azureStorage, IConfiguration configuration)
+        {
+            _azureCosmos = azureCosmos;
+            _dingDing = dingDing;
+            _option = option?.Value;
+            _serviceBus = serviceBus;
+            _configuration = configuration;
+            _azureStorage = azureStorage;
+            _azureRedis = azureRedis; 
+            _coreAPIHttpService = coreAPIHttpService;
+        }
+        /// <summary>
+        /// 活动创建
+        /// </summary>
+        /// <param name="request"></param>
+        /// <returns></returns>
+        [ProducesDefaultResponseType]
+        [AuthToken(Roles = "teacher,admin,area")]
+        [HttpPost("manage")]
+        [Authorize(Roles = "IES")]
+
+        public async Task<IActionResult> Manage(JsonElement request)
+        {
+            try
+            {
+                (string tmdid, _, _, string school) = HttpContext.GetAuthTokenInfo();
+                if (!request.TryGetProperty("grant_type", out JsonElement grant_type)) return BadRequest();
+               
+                var client = _azureCosmos.GetCosmosClient();
+                switch (true)
+                {
+                    case bool when $"{grant_type}".Equals("create", StringComparison.OrdinalIgnoreCase):
+                        {
+                            if (!request.TryGetProperty("Activity", out JsonElement _activity)) return Ok(new { error = ResponseCode._400ParamsError, msg = "活动信息参数错误" });
+
+                            Activity activity = _activity.ToObject<Activity>();
+                            activity.id=!string.IsNullOrWhiteSpace(activity.id)?activity.id: Guid.NewGuid().ToString();
+                            activity.code="Activity";
+                            activity.pk="Activity";
+                            ValidResult validResult = activity.Valid();
+                            if (validResult.isVaild)
+                            {
+                                activity.creatorId=tmdid;
+                                activity.createTime= DateTimeOffset.Now.ToUnixTimeMilliseconds();
+                                activity.year=DateTimeOffset.Now.Year;
+                                foreach (var module in activity.modules) {
+                                    switch (true) 
+                                    {
+                                        //赛课
+                                        case bool when module.Equals("Contest"):
+                                            {
+                                                if (!request.TryGetProperty("Contest", out JsonElement _contest)) return Ok(new { error = ResponseCode._400ParamsError, msg = "赛课信息参数错误" });
+                                                Contest contest = _activity.ToObject<Contest>();
+                                                contest.id=activity.id;
+                                                contest.code="Contest";
+                                                contest.pk="Contest";
+
+                                                break;
+                                            }
+                                        //培训
+                                        case bool when module.Equals("Training"):
+                                            {
+                                                break;
+                                            }
+                                        //教研
+                                        case bool when module.Equals("Research"):
+                                            {
+                                                break;
+                                            }
+                                    }
+                                }
+                            }
+                            else
+                            {
+                                return Ok(validResult);
+                            }
+                            break;
+                        }
+                   
+                }
+            }catch (Exception ex)
+            {
+
+            }
+            return Ok();
+        }
+    }
+}

文件差异内容过多而无法显示
+ 30 - 2
TEAMModelOS/Controllers/System/WeChatPayController.cs


+ 1 - 0
TEAMModelOS/Controllers/Teacher/InitController.cs

@@ -791,6 +791,7 @@ namespace TEAMModelOS.Controllers
                         //}
                         currArea = new
                         {
+                            shortCode = area?.shortCode,
                             areaId = area?.id,
                             name = area?.name,
                             standard = area?.standard,