瀏覽代碼

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

zhouj1203@hotmail.com 1 年之前
父節點
當前提交
694b0e6f45
共有 50 個文件被更改,包括 3008 次插入311 次删除
  1. 1 1
      TEAMModelBI/ClientApp/public/index.html
  2. 99 22
      TEAMModelBI/ClientApp/src/view/userInquire/details.vue
  3. 310 8
      TEAMModelBI/ClientApp/src/view/userInquire/ies.vue
  4. 1334 0
      TEAMModelBI/ClientApp/src/view/userInquire/iot.vue
  5. 6 0
      TEAMModelBI/ClientApp/src/view/userInquire/score.vue
  6. 317 11
      TEAMModelBI/ClientApp/src/view/userInquire/socrates.vue
  7. 85 23
      TEAMModelBI/ClientApp/src/view/userInquire/ticket.vue
  8. 2 2
      TEAMModelOS.FunctionV4/CosmosDB/TriggerArt.cs
  9. 1 1
      TEAMModelOS.FunctionV4/CosmosDB/TriggerCorrect.cs
  10. 2 2
      TEAMModelOS.FunctionV4/CosmosDB/TriggerExam.cs
  11. 2 2
      TEAMModelOS.FunctionV4/CosmosDB/TriggerExamLite.cs
  12. 2 2
      TEAMModelOS.FunctionV4/CosmosDB/TriggerHomework.cs
  13. 2 2
      TEAMModelOS.FunctionV4/CosmosDB/TriggerStudy.cs
  14. 2 2
      TEAMModelOS.FunctionV4/CosmosDB/TriggerSurvey.cs
  15. 2 2
      TEAMModelOS.FunctionV4/CosmosDB/TriggerVote.cs
  16. 8 8
      TEAMModelOS.FunctionV4/ServiceBus/ActiveTaskTopic.cs
  17. 46 15
      TEAMModelOS.SDK/Models/Cosmos/Common/Activity.cs
  18. 100 0
      TEAMModelOS.SDK/Models/Service/Common/ActivityService.cs
  19. 4 2
      TEAMModelOS.SDK/Models/Service/ActivityService.cs
  20. 14 8
      TEAMModelOS/ClientApp/public/lang/en-US.js
  21. 6 0
      TEAMModelOS/ClientApp/public/lang/zh-CN.js
  22. 7 1
      TEAMModelOS/ClientApp/public/lang/zh-TW.js
  23. 1 1
      TEAMModelOS/ClientApp/src/common/BaseAreaList.vue
  24. 11 11
      TEAMModelOS/ClientApp/src/common/BaseLayout.vue
  25. 1 1
      TEAMModelOS/ClientApp/src/common/BaseNotification.vue
  26. 1 1
      TEAMModelOS/ClientApp/src/components/student-web/ClassRecord/RecordView.vue
  27. 14 7
      TEAMModelOS/ClientApp/src/utils/public.js
  28. 1 0
      TEAMModelOS/ClientApp/src/view/areaArtExam/AcQuos.vue
  29. 16 14
      TEAMModelOS/ClientApp/src/view/areaMgmt/AreaLayout.vue
  30. 1 0
      TEAMModelOS/ClientApp/src/view/artexam/AcQuos.vue
  31. 47 6
      TEAMModelOS/ClientApp/src/view/artexam/Create.vue
  32. 3 3
      TEAMModelOS/ClientApp/src/view/artexam/ExamSetting.vue
  33. 5 4
      TEAMModelOS/ClientApp/src/view/artexam/ExamSubject.vue
  34. 10 2
      TEAMModelOS/ClientApp/src/view/artexam/QuoTree.vue
  35. 4 4
      TEAMModelOS/ClientApp/src/view/artexam/WorkSetting.vue
  36. 5 4
      TEAMModelOS/ClientApp/src/view/artexam/WorkSubject.vue
  37. 7 0
      TEAMModelOS/ClientApp/src/view/classrecord/ClassRecord.vue
  38. 1 1
      TEAMModelOS/ClientApp/src/view/classrecord/eventchart/ExamGrade.vue
  39. 177 109
      TEAMModelOS/ClientApp/src/view/elegant/Elegant.vue
  40. 1 1
      TEAMModelOS/ClientApp/src/view/learnactivity/echarts/ScoreMatrix.vue
  41. 37 1
      TEAMModelOS/ClientApp/src/view/mycourse/MyCourse.vue
  42. 8 2
      TEAMModelOS/ClientApp/src/view/mycourse/record/Record.less
  43. 11 4
      TEAMModelOS/ClientApp/src/view/mycourse/record/Record.vue
  44. 2 2
      TEAMModelOS/ClientApp/src/view/mycourse/score/AddProject.less
  45. 2 2
      TEAMModelOS/ClientApp/src/view/mycourse/score/AddProject.vue
  46. 1 1
      TEAMModelOS/ClientApp/src/view/mycourse/score/DialogBox.less
  47. 6 6
      TEAMModelOS/ClientApp/src/view/syllabus/Syllabus.vue
  48. 10 1
      TEAMModelOS/ClientApp/src/view/teachermgmt/components/mgt/TeacherMgt.vue
  49. 272 8
      TEAMModelOS/Controllers/Common/ActivityController.cs
  50. 1 1
      TEAMModelOS/Controllers/Common/CommonController.cs

+ 1 - 1
TEAMModelBI/ClientApp/public/index.html

@@ -12,7 +12,7 @@
     </title>
 </head>
 <script src="https://g.alicdn.com/dingding/dinglogin/0.0.5/ddLogin.js"></script>
-<script src="https://at.alicdn.com/t/c/font_2934132_z6jnssvqk88.js"></script>
+<script src="https://at.alicdn.com/t/c/font_2934132_fbh0djqz67s.js"></script>
 <script src="../src/access/iconfont.js"></script>
 
 <body>

+ 99 - 22
TEAMModelBI/ClientApp/src/view/userInquire/details.vue

@@ -49,20 +49,19 @@
         <div class="product-details" v-else-if="!userdetailState">
             <div class="hiteachboxdiv">
                 <div  class="common-header-title">
-                    <div>HiTeach</div>
+                    <div>IOT</div>
                     <!-- <div class="expire">到期日:XXXXX</div> -->
                 </div>
                 <el-divider />
-                <Hiteach></Hiteach>
+                <replaceHiTeach></replaceHiTeach>
             </div>
-            <div class="hitadiv">
+            <!-- <div class="hitadiv">
                 <div  class="common-header-title">
                     <div>HiTA</div>
-                    <!-- <div class="expire">到期日:XXXXX</div> -->
                 </div>
                 <el-divider />
                 <HiTA></HiTA>
-            </div>
+            </div> -->
             <div class="user-size">
                 <div  class="common-header-title">
                     <div>空间与权益</div>
@@ -85,11 +84,11 @@
                 <div class="equitystate" v-else>
                     <div class="rightsbox">
                         <div class="rightsbox-content">
-                            <el-carousel trigger="click" height="33vh" :autoplay="false">
+                            <el-carousel trigger="click" height="32vh" :autoplay="false">
                                 <el-carousel-item v-for="item in powerList" :key="item">
                                     <div class="rightsbox-item">
                                         <div class="rightsbox-item-title">
-                                            <p>ID授权 {{item.name}}</p>
+                                            <p>[ID授权] {{item.name}}</p>
                                         </div>
                                         <div class="rightsbox-item-time">
                                             <p>
@@ -116,7 +115,27 @@
                             </el-carousel>
                         </div>
                         <div class="subjoin">
-                            <p class="subjoin-title">附加功能:</p>
+                           <div class="subjoin-item" >
+                                <p class="subjoin-title">{{subjoinData.hiteachTitle}}</p>
+                                <div class="subjoin-content" v-for="item in subjoinData.hiteach" :key="item.key">
+                                    <div>
+                                        <span>{{item.title}}</span>
+                                        <span class="ccnums" v-show="item.key==='webirs'">{{item.value}}</span>
+                                        <span class="endtimes" v-if="item.exps >= notTime">(到期日:{{item.end}})</span>
+                                        <span class="endtimes-error" v-else>(到期日:{{item.end}})</span>
+                                    </div>
+                                </div>
+                           </div>
+                           <div class="subjoin-item" >
+                                <p class="subjoin-title">{{subjoinData.hiteachCCTitle}}</p>
+                                <div class="subjoin-content" v-for="itemc in subjoinData.hiteachCC" :key="itemc.key">
+                                    <div>
+                                        <span>{{itemc.title}}</span>
+                                        <span class="ccnums">{{itemc.num}}</span>
+                                        <span class="endtimes">(到期日:{{itemc.end}})</span>
+                                    </div>
+                                </div>
+                           </div>
                         </div>
                     </div>
                 </div>
@@ -142,19 +161,19 @@
                 <el-divider />
                 <Socrates :sokratesdatas="sokratesData"></Socrates>
             </div>
-            <div class="scorebox">
+            <!-- <div class="scorebox">
                 <div  class="common-header-title">
                     <div>积分统计</div>
                 </div>
                 <el-divider />
                 <Score :pointsdatas="pointsData"></Score>
-            </div>
+            </div> -->
             <div class="ticketdiv">
                 <div  class="common-header-title">
-                    <div>票卷明细</div>
+                    <div>积分与票卷</div>
                 </div>
                 <el-divider />
-                <Ticket></Ticket>
+                <Ticket :pointsandcoupons="pointsData"></Ticket>
             </div>
         </div>
         <!--产品页面end-->
@@ -172,6 +191,7 @@ import Socrates from './socrates.vue'
 import Score from './score.vue'
 import Ticket from './ticket.vue'
 import Userdetail from './userdetail.vue'
+import replaceHiTeach from './iot.vue'
 let props = defineProps({
     searchdata: Object,
 })
@@ -456,7 +476,7 @@ let detailsData = ref()
 detailsData.value = props.searchdata?.length > 0 ? props.searchdata : ''
 let ies5datas=detailsData.value[0].ies5
 let sokratesData=detailsData.value[0].sokrates
-let pointsData=detailsData.value[0].points
+let pointsData=detailsData.value[0]
 console.log(props.searchdata, '传输的值')
 console.log(detailsData.value, '最终数据')
 let powerExtension=ref([
@@ -473,6 +493,22 @@ let powerExtension=ref([
     {title:'議課人數',key:'soknumber',value:0,state:false},
     {title:'IRS硬體遙控器',key:'irs',state:true},
 ])
+let subjoinData=ref({
+    hiteachTitle:'HiTeach 附加功能',
+    hiteach:[
+        {title:'AI文句分析',key:'aitext',start:0,end:0,value:0,gets:0,exps:0},
+        {title:'AI苏格拉底小数据',key:'soklite',start:0,end:0,value:0,gets:0,exps:0},
+        {title:'智慧评分',key:'smartrating',start:0,end:0,value:0,gets:0,exps:0},
+        {title:'clouDAS诊断分析',key:'cloudas',start:0,end:0,value:0,gets:0,exps:0},
+        {title:'Web IRS连线数',key:'webirs',start:0,end:0,value:0,gets:0,exps:0}
+    ],
+    hiteachCCTitle:'HiTeach CC 权限',
+    hiteachCC:[
+        {title:'作品收集数',key:'works',start:0,end:0,num:0,gets:0,exps:0},
+        {title:'连线人数',key:'clients',start:0,end:0,num:0,gets:0,exps:0},
+    ]
+})
+let notTime=ref(Math.floor(Date.now() / 1000))
 let powerList=ref([])
 const handleChildEvent = (payload) => {
     userdetailState.value=false
@@ -506,7 +542,7 @@ function initdata() {
     let usePercentum=parseInt(((Number(bytesToGB(usedSize))+Number(bytesToGB(teachSize)))/Number(bytesToGB(totalSize)))*100);let useGsize=proxy.$common.convertSize(usedSize)
     
     //权益内容
-    let {prod}=transmitData
+    let {prod,benefits}=transmitData
     prod.forEach((item)=>{
         //处理名字
         item.name=item.prodCode ==='Z6ELB6EZ' ? 'HiTeach 5':item.prodCode ==='IPALB6EY' ? 'IES 5 個人空間':''
@@ -539,6 +575,24 @@ function initdata() {
         item.endDateText=proxy.$common.timestampToTime(item.endDate)
         powerList.value.push(item)  
     })
+    //处理权益内 附加功能(HITeach附加 CC权限)
+    let hiteachData=benefits.hiteach; let ccData=benefits.hiteachcc
+    if(hiteachData.length >0){
+        hiteachData.forEach((item)=>{
+            let funcKey=item.func;let startTime=proxy.$common.timestampToTime(item.get);let endTime=proxy.$common.timestampToTime(item.exp)
+            subjoinData.value.hiteach.forEach((items)=>{
+                funcKey === items.key ? (items.start=startTime,items.end=endTime,items.value=item.para,items.gets=item.get,items.exps=item.exp):''
+            })
+        })
+    }
+    if(ccData.length >0){
+        ccData.forEach((itemA)=>{
+            let funcKey=itemA.func;let startTime=proxy.$common.timestampToTime(itemA.get);let endTime=proxy.$common.timestampToTime(itemA.exp)
+            subjoinData.value.hiteachCC.forEach((itemc)=>{
+                funcKey === itemc.key ? (itemc.start=startTime,itemc.end=endTime,itemc.num=itemA.para,itemc.gets=itemA.get,itemc.exps=itemA.exp):''
+            })
+        })
+    }
     console.log(powerList,'处理数据的结果')
     //空间图表
     gaugedata.value.series[0].data[0].value=usePercentum
@@ -701,7 +755,7 @@ onMounted(() => {
     justify-content: space-between;
 }
 .hiteachboxdiv{
-    width:45%;
+    width:75%;
     border-radius: 5px;
     box-shadow: rgba(0, 0, 0, 0.3) 0px 3px 8px;
     padding: 1%;
@@ -830,14 +884,14 @@ onMounted(() => {
     align-items: center;
 }
 .iesdiv{
-    width:25%;
+    width:37%;
     border-radius: 5px;
     box-shadow: rgba(0, 0, 0, 0.3) 0px 3px 8px;
     padding: 1%;
     margin-top:1%;
 }
 .socratesdiv{
-    width:25%;
+    width:37%;
     border-radius: 5px;
     box-shadow: rgba(0, 0, 0, 0.3) 0px 3px 8px;
     padding: 1%;
@@ -854,7 +908,6 @@ onMounted(() => {
     margin-bottom: 10px;
 }
 .rightsbox-content{
-    padding: 1%;
     display: flex;
     flex-direction: column;
     /* height: 18vh; */
@@ -865,7 +918,6 @@ onMounted(() => {
     text-align: left;
     border-bottom: 1px solid #ccc;
     font-size:14px;
-    padding: 1%;
     line-height: 25px;
     height:100%;
 }
@@ -901,7 +953,7 @@ onMounted(() => {
     font-weight: 700;
 }
 .expansion-sizenum{
-    size:16px
+    font-size:16px
 }
 .rightsbox-item-time-num,.rightsbox-item-timenums,.start-title,.expansion-sizenum{
     color:#409EFF;
@@ -910,13 +962,38 @@ onMounted(() => {
     color:#909399;
 }
 .subjoin{
+    height:20vh;
     line-height: 20px;
     color:#73767a;
+    overflow: hidden;
+    overflow-y: auto;
 }
 .subjoin-title{
     text-align: left;
-    size:14px;
-
+    font-size:16px;
+    margin-bottom: 5px;
+}
+.subjoin-content{
+    text-align: left;
+}
+.subjoin-content div{
+    padding-left: 10px;
+    font-size:14px;
+    color:#303133;
+    display: flex;
+    justify-content: space-between;
+    flex-wrap: nowrap;
+}
+.ccnums{
+    margin: 0px 7px;
+    color:#409EFF;
+}
+.endtimes{
+    margin-left:15px;
+    color:#409EFF;
+}
+.endtimes-error{
+    color:#F56C6C
 }
 .scorebox{
     width: 23%;

+ 310 - 8
TEAMModelBI/ClientApp/src/view/userInquire/ies.vue

@@ -11,6 +11,58 @@
                 <div class="iesheader-item-value">{{item.value}}</div>
             </div>
         </div>
+        <el-divider />
+            <div class="joinSchool">
+                <div class="joinSchool-item">
+                    <div class="joinSchool-img">
+                        <svg class="joinSchool-icon" aria-hidden="true">
+                            <use xlink:href="#icon-xuexiao8"></use>
+                        </svg>
+                    </div>
+                    <div class="joinSchool-content">
+                        <div class="content-title">
+                            <div>
+                                <svg class="default-icon" aria-hidden="true">
+                                    <use xlink:href="#icon-gouxuan1"></use>
+                                </svg>
+                            </div>
+                            <div class="joinSchool-title-name">学校名称:<span class="name-label">{{schoolDefault.name}}</span></div>
+                        </div>
+                        <div class="content-box">
+                            <div class="content-box-item">
+                                <div class="itembox-div" >
+                                    <span class="itembox-div-title">学校简码:</span>
+                                    <span class="itembox-div-value">{{schoolDefault.schoolId}}</span>
+                                </div>
+                                <div class="itembox-div" >
+                                    <span class="itembox-div-title">加入学校数:</span>
+                                    <span class="itembox-div-value">{{schoolDefault.joinNums}}</span>
+                                </div>
+                                <div class="itembox-div" >
+                                    <span class="itembox-div-title">本校课程数:</span>
+                                    <span class="itembox-div-value">{{schoolDefault.courseCnt}}</span>
+                                </div>
+                                <div class="itembox-div-special" >
+                                    <span class="itembox-div-title">本校位置:</span>
+                                    <span class="itembox-div-value">{{schoolDefault.region}}{{schoolDefault.city}}{{schoolDefault.dist}}锦江区</span>
+                                </div>
+                                <div class="itembox-div-special">
+                                    <div class="special-title">空间情况:</div>
+                                    <div class="special-progress"><el-progress :text-inside="true" :stroke-width="18" :percentage="schoolDefault.percentage" /></div>
+                                </div>
+                            </div>
+                        </div>
+                    </div>
+                    <div class="joinSchool-listbtn">
+                        <el-button type="primary" size="small" @click="allschoolState=true">
+                        查看所有学校<el-icon class="el-icon--right"><ArrowRight /></el-icon>
+                        </el-button>
+                    </div>
+                    <div class="default-tag">
+                        <span>默认学校</span>
+                    </div>
+                </div>
+            </div>
         <el-divider />
             <div class="ies-flex">
                 <!-- <div class="schools-item" v-for="item in schooldata" :key="item.code">
@@ -24,27 +76,78 @@
             <!-- </div> -->
             <bars :barData="echartsBar"></bars>
             </div>
+            <div class="dialog-school">
+                <el-dialog v-model="allschoolState" title="加入学校列表" width="50%" :before-close="handleClose">
+                    <div class="dialog-school-box">
+                        <div class="school-box-title">总计加入<span class="school-box-title-num">{{schoolDefault.joinNums}}</span>所学校</div>
+                        <div class="dialogschool-item" v-for="item in allschoolList" :key="item.schoolId">
+                            <div class="dialogschool-item-top">
+                                <svg class="joinSchool-icon" aria-hidden="true">
+                                    <use xlink:href="#icon-xuexiao8"></use>
+                                </svg>
+                            </div>
+                            <div class="dialogschool-item-content">
+                                <p class="item-content-tile">
+                                    {{item.name}}
+                                </p>
+                                <p><span class="itembox-div-title">学校简码:</span><span class="itembox-div-value">{{item.schoolId}}</span></p>
+                                <p><span class="itembox-div-title">学校课程数:</span><span class="itembox-div-value">{{item.courseCnt}}</span></p>
+                                <p><span class="itembox-div-title">学校位置:</span><span class="itembox-div-value">{{item.region}}{{item.city}}{{item.dist}}</span></p>
+                                <div>
+                                    <div>空间情况:</div>
+                                    <div class="item-content-sizebox">
+                                        <div><p class="itembox-div-title">已使用</p><p class="itembox-div-title fontsize">{{item.size.usedText}}   GB</p></div>
+                                        <div><p class="itembox-div-title">可使用</p><p class="itembox-div-title fontsize">{{item.size.avaliableText}}   GB</p></div>
+                                        <div><p class="itembox-div-title">总空间</p><p class="itembox-div-title fontsize">{{item.size.totalText}}   GB</p></div>
+                                    </div>
+                                    <div class="item-content-progress"><el-progress :text-inside="true" :stroke-width="16" :percentage="item.size.percentage" /></div></div>
+                            </div>
+                            <div class="dialog-default-tag" v-show="item.defaultSchool">
+                                <span>默认学校</span>
+                            </div>
+                        </div>
+                    </div>
+                    <template #footer>
+                    <span class="dialog-footer">
+                        <el-button @click="allschoolState = false">关闭</el-button>
+                    </span>
+                    </template>
+                </el-dialog> 
+            </div>   
     </div>
  </template>
  <script setup>
  import { ref, getCurrentInstance, watch, h, nextTick, onMounted } from 'vue'
+ import { ArrowRight } from '@element-plus/icons'
  import * as echarts from 'echarts'
  import bars from '@/components/echarts/commonBar.vue'
  let props = defineProps({
     iesdata: Object,
 })
  let relevancedata = ref([
-      { title: '我的教材', icon: '#icon-anli', value: 38, key: 'material' },
-      { title: '我的题库', icon: '#icon-tiku', value: 58, key: 'question' },
-      { title: '我的试卷', icon: '#icon-shijuan', value: 88, key: 'examination' },
-      { title: '作业评量', icon: '#icon-zuoyepigai', value: 18, key: 'work' },
+      { title: '资源数', icon: '#icon-anli', value: 0, key: 'material' },
+      { title: '试题数', icon: '#icon-tiku', value: 0, key: 'question' },
+      { title: '试卷数', icon: '#icon-shijuan', value: 0, key: 'examination' },
+    //   { title: '作业评量', icon: '#icon-zuoyepigai', value: 18, key: 'work' },
  ])
+ let schoolDefault=ref({
+    name:'',
+    joinNums:0,
+    schoolId:'',
+    courseCnt:0,
+    region:"",
+    city: "",
+    dist: '',
+    percentage:0,
+ })
+ let allschoolState=ref(false)
  let schooldata = ref([
       { name: '研发学校', code: 'habook', location: '中国', class: 15, totalsize: 300, occupy: 75 },
       { name: '醍摩豆学校', code: 'hbcn', location: '中国', class: 15, totalsize: 300, occupy: 75 },
       { name: '商务智能学校(BI)', code: 'cswznb', location: '中国', class: 15, totalsize: 300, occupy: 75 },
       {name:'南京特殊教育师范学院',code:'ntsjsy',location:'中国',class:15,totalsize:300,occupy:75}
  ])
+ let allschoolList=ref([])
  let echartsBar=ref({
     tooltip: {
         trigger: 'axis',
@@ -151,10 +254,37 @@
  })
  function initdatas(){
     console.log(props.iesdata,'IESde zhi')
-    relevancedata.value[0].value='XXX'
-    relevancedata.value[1].value='XXX'
-    relevancedata.value[2].value=props.iesdata.paperCount
-    relevancedata.value[3].value='XXX'
+    let {resCount,itemCount,paperCount}=props.iesdata
+    relevancedata.value[0].value=resCount
+    relevancedata.value[1].value=itemCount
+    relevancedata.value[2].value=paperCount
+    let {schools}=props.iesdata
+    //外部显示默认
+    schools.forEach(item => {
+        if(item.defaultSchool){
+            schoolDefault.value.name=item.name
+            schoolDefault.value.joinNums=schools.length
+            schoolDefault.value.schoolId=item.schoolId
+            schoolDefault.value.courseCnt=item.courseCnt
+            schoolDefault.value.region=item.region
+            schoolDefault.value.city=item.city
+            schoolDefault.value.dist=item.dist
+            let totalGB=item.size.total / 1024 / 1024 / 1024;let useGB=item.size.used/ 1024 / 1024 / 1024
+            schoolDefault.value.percentage=Math.round(((useGB/totalGB).toFixed(2))*100)
+        }
+    });
+    //整体列表
+    schools.forEach((items)=>{
+        items.size.usedText=(items.size.used/ 1024 / 1024 / 1024).toFixed(2);
+        console.log(items.size.usedText)
+        items.size.avaliableText=(items.size.avaliable/ 1024 / 1024 / 1024).toFixed(2);
+        console.log(items.size.avaliableText)
+        items.size.totalText=(items.size.total/ 1024 / 1024 / 1024).toFixed(2);
+        console.log(items.size.totalText)
+        items.size.percentage=Math.round(((items.size.used/ items.size.total).toFixed(2))*100)
+        allschoolList.value.push(items)
+    })
+    console.log(allschoolList.value,'list')
  }
  onMounted(() => {
     initdatas()
@@ -247,4 +377,176 @@
     text-align: center;
     padding: 2px 7px;
  }
+ .joinSchool{
+    padding: 1% 1%;
+ }
+ .joinSchool-item{
+    display: flex;
+    flex-wrap: nowrap;
+    padding: 2% 1%;
+    border-radius: 5px;
+    position: relative;
+    overflow: hidden;
+    border:1px solid #ccc;
+ }
+ .joinSchool-item:hover{
+    box-shadow: rgba(17, 17, 26, 0.1) 0px 1px 0px, rgba(17, 17, 26, 0.1) 0px 8px 24px, rgba(17, 17, 26, 0.1) 0px 16px 40px;
+    cursor: pointer;
+ }
+ .joinSchool-img{
+    width:15%;
+ }
+ .joinSchool-content{
+    width:65%;
+    text-align: left;
+    line-height: 20px;
+    padding-left:2%;
+ }
+ .content-title{
+    display: flex;
+ }
+ .joinSchool-icon{
+    width: 5em;
+    height: 5em;
+    vertical-align: -0.5em;
+    fill: currentColor;
+ }
+ .default-icon{
+    width: 20px;
+    height: 20px;
+    vertical-align: 0px;
+    fill: currentColor;
+    margin-right:5px
+ }
+ .joinSchool-title-name{
+    font-size:18px;
+    font-weight: 700;
+ }
+ .content-box-item{
+    display: flex;
+    flex-wrap: wrap;
+ }
+ .itembox-div{
+    width:50%;
+    font-size:14px;
+ }
+ .itembox-div-special{
+    width:100%;
+    font-size:14px;
+    display: flex;
+ }
+ .special-title{
+    width:25%;
+ }
+ .special-progress{
+    width:70%;
+ }
+ .itembox-div-title,.special-title{
+    color:#73767a;
+ }
+ .name-label,.itembox-div-value{
+    color:#409EFF;
+ }
+ .joinSchool-listbtn{
+    width:18%;
+    display: flex;
+    align-items: center;
+ }
+ .dialog-school{
+    line-height: 40px;
+    text-align: left;
+ }
+ .dialog-school-box{
+    padding: 1%;
+    display: flex;
+    flex-wrap: wrap;
+ }
+ .dialogschool-item{
+    width:25%;
+    padding: 2% 1%;
+    justify-content: center;
+    align-items: center;
+    border:1px solid #ccc;
+    border-radius: 5px;
+    margin:0px 10px;
+    position:relative;
+    overflow: hidden;
+ }
+ .dialogschool-item-top,.item-content-tile{
+    text-align: center;
+ }
+ .dialogschool-item-content{
+    font-size:14px;
+ }
+ .item-content-tile{
+    font-size:18px;
+    font-weight: bold;
+ }
+ .dialogschool-item-content p{
+    margin-bottom: 5px;
+ }
+ .item-content-sizebox{
+    width:100%;
+    display: flex;
+    flex-wrap: nowrap;
+    font-size:14px;
+ }
+ .item-content-sizebox div{
+    width:32%;
+ }
+ .item-content-sizebox div p{
+    margin-bottom: 0px;
+    line-height: 30px;
+ }
+ .school-box-title{
+    width:100%;
+    text-align: center;
+    font-size:18px;
+ }
+ .school-box-title-num{
+    margin:5px;
+    font-size:20px;
+    color:#409EFF;
+ }
+ .default-tag{
+    background: #19be6b;
+    font-size: 10px;
+    user-select: none;
+    color: #fff;
+    position: absolute;
+    right: -35px;
+    top: 8px;
+    width: 110px;
+    padding: 2px 0;
+    line-height: 16px;
+    text-align: center;
+    transform: rotate(45deg);
+ }
+ .dialog-default-tag{
+    background: #19be6b;
+    font-size: 12px;
+    user-select: none;
+    color: #fff;
+    position: absolute;
+    right: -35px;
+    top: 8px;
+    width: 110px;
+    padding: 2px 0;
+    line-height: 16px;
+    text-align: center;
+    transform: rotate(45deg);
+ }
+ .fontsize{
+    font-size:12px;
+ }
  </style>
+<style>
+.dialog-school .el-dialog__body{
+    padding: 5px 15px;
+    overflow: hidden;
+    overflow-x: auto;
+}
+.dialog-school .el-dialog__header{
+    padding: 10px 15px;
+}
+</style>

File diff suppressed because it is too large
+ 1334 - 0
TEAMModelBI/ClientApp/src/view/userInquire/iot.vue


+ 6 - 0
TEAMModelBI/ClientApp/src/view/userInquire/score.vue

@@ -121,6 +121,12 @@ let scoredetail = ref({
         {type:'add',typename:'系统添加',value:'+2000',time:'2023/05/23 15:23'},
     ]
 })
+let tdatas = ref([
+    { title: '总时长(分)', value: 'XXXX', key: 'times' },
+    { title: '学生人次', value: 'XXXX', key: 'stundennum' },
+    { title: '互动次数', value: 'XXXX', key: 'interact' },
+    { title: '学生学习总时长', value: 'XXXX', key: 'studentime' },
+])
 function initdatas(){
     console.log(props,'SGLD')
     scoredata.value[0].value=Number(props.pointsdatas.points)-Number(props.pointsdatas.balance)   

+ 317 - 11
TEAMModelBI/ClientApp/src/view/userInquire/socrates.vue

@@ -1,5 +1,19 @@
 <template>
     <div class="socrates-content">
+        <div class="presetbox">
+            <p>预设频道:
+                <span class="presetbox-schoolname" v-if="presupposed.state">{{presupposed.data.default_channel.school_name}}</span>
+                <span class="presetbox-schoolname" v-else> 暂无预设学校</span>
+            </p>
+            <div class="presetbox-content" v-show="presupposed.state">
+                <div>
+                    <svg class="default-icon" aria-hidden="true">
+                        <use xlink:href="#icon-gouxuan1"></use>
+                    </svg>
+                </div>
+                <div>AI苏格拉底小数据  (到期日:{{presupposed.data.benefits.t_data_deadline}})</div>
+            </div>
+        </div>
         <div class="socrates-header">
             <div class="socrates-item" v-for="item in primary" :key="item.title">
                 <div class="socrates-item-title">{{item.title}}</div>
@@ -13,15 +27,48 @@
             </div>
         </div>
         <el-divider />
-        <div class="socrates-echatrs">
+        <!-- <div class="socrates-echatrs">
             <bars :barData="ecahrtsdata"></bars>
-        </div>
+        </div> -->
+        <div class="tnum-content">
+                <div class="selectbox">
+                    <div><el-button v-for="item in buttonSelect" :key="item.value" :type="item.click ? 'primary':''" size="small" class="clickbox" @click="selectTime(item.value)">{{item.name}}</el-button></div>
+                    <div class="nowaday-time">时间:<span>{{hiteachDatas.appear.start_date}}~{{hiteachDatas.appear.end_date}}</span></div>
+                </div>
+                <div class="contentbox">
+                    <!-- <div class="nowaday-time">时间:2023/07/05-2023/07/11</div> -->
+                    <div class="nowaday">
+                        <div class="nowaday-now">
+                            <div class="nows-box">T数据:<span class="now-value">{{hiteachDatas.appear.t_data}}</span></div>
+                            <div class="nows-box">T绿灯:<span class="now-value">{{hiteachDatas.appear.t_green}}</span></div>
+                            <!-- <div class="nows-box">双绿灯:<span class="now-value">XXX</span></div> -->
+                        </div>
+                        <div class="nowaday-proportion">
+                            <Pies :piesData="zb" ref="echartsb"></Pies>
+                        </div>
+                    </div>
+                    <el-divider />
+                    <div class="restbox">
+                        <div class="restbox-item" >
+                            <div class="restbox-left" v-for="item in tdatas" :key="item.key">
+                                <div class="left-content">{{item.title}}:
+                                    <span>{{item.values}}</span><span v-show="item.key ==='times' ||item.key ==='studentime'" class="time-unit">Min</span>
+                                </div>
+                            </div>
+                            <!-- <div class="restbox-right">
+                                <div class="left-content">学生人次:<span>16</span></div>
+                            </div> -->
+                        </div>
+                    </div>
+                </div>
+            </div>
     </div>
  </template>
  <script setup>
  import { ref, getCurrentInstance, watch, h, nextTick, onMounted } from 'vue'
  import * as echarts from 'echarts'
  import bars from '@/components/echarts/commonBar.vue'
+ import Pies from './echarts/pie.vue'
  let props = defineProps({
     sokratesdatas: Object,
 })
@@ -39,6 +86,29 @@ let auxiliary=ref([
            { title: '观课数', value: 0, },
            {title:'课件数',value:0,},
 ])
+let presupposed=ref({
+    data:{
+        benefits:{},
+        default_channel:{},
+        general_observation:{},
+    },
+    state:false
+})
+let hiteachDatas=ref({
+    appear:{
+        attendance:0,
+        duration:0,
+        end_date:0,
+        interaction:0,
+        learning_duration:0,
+        start_date:0,
+        t_data:0,
+        t_green:0,
+    },
+    weeks:{},
+    months:{},
+    years:{},
+})
  let ecahrtsdata = ref({
         tooltip: {
           trigger: 'axis',
@@ -186,21 +256,158 @@ let auxiliary=ref([
           data: [40, 60, 70, 70, 100, 40, 40, 60, 70]
         }]
  })
- function initdatas(){
+ let tdatas = ref([
+    { title: '总时长(分)', values: 'XXXX', key: 'times' },
+    { title: '学生人次', values: 'XXXX', key: 'stundennum' },
+    { title: '互动次数', values: 'XXXX', key: 'interact' },
+    { title: '学生学习总时长', values: 'XXXX', key: 'studentime' },
+])
+let buttonSelect = ref([
+    { name: '周', value: 'week', click: true },
+    { name: '月', value: 'month', click: false },
+    {name:'年',value:'year',click:false}
+])
+let proportiondata = ref({
+    legend: {
+        top: '1%',
+        left: '1%',
+        right:'1%',
+    },
+    title: [
+        {
+        text: '占同月T绿灯比例',
+        x: '45%',
+        y: '85%',
+        textAlign: 'center',
+            textStyle: {
+                fontSize: '14',
+                fontWeight: '100',
+                color: '#333',
+                textAlign: 'center',
+            },
+        }, 
+        {
+        text: '16%',
+        left: '50%',
+        top: '30%',
+        textAlign: 'center',
+            textStyle: {
+                fontSize: '30',
+                fontWeight: '100',
+                color: '#333',
+                textAlign: 'center',
+            },
+        }, 
+    ],
+    series: [
+        {
+            type: 'pie',
+            startAngle: 0,
+            radius: ['80%', '60%'],
+            center: ['50%', '40%'],
+            data: [{
+                    hoverOffset: 1,
+                    value: 75,
+                    name: '',
+                    itemStyle: {
+                        normal: {
+                            color: new echarts.graphic.LinearGradient(0, 1, 1, 0, [{
+                                offset: 0,
+                                color: '#ef29b1'
+                            }, {
+                                offset: 1,
+                                color: '#fd7225'
+                            }]),
+                            shadowColor: '#1c1b3a',
+                            shadowBlur: 1,
+                            shadowOffsetX: '0',
+                            shadowOffsetY: '3',
+                        }
+                    },
+                    label: {
+                        show: true
+                    },
+                    labelLine: {
+                        normal: {
+                            smooth: true,
+                            lineStyle: {
+                                width: 0
+                            }
+                        }
+                    },
+                    hoverAnimation: false,
+                },
+                {
+                    label: {
+                        show: true
+                    },
+                    labelLine: {
+                        normal: {
+                            smooth: true,
+                            lineStyle: {
+                                width: 0
+                            }
+                        }
+                    },
+                    value: 100 - 75,
+                    hoverAnimation: false,
+                    itemStyle: {
+                        color: 'rgba(251, 200, 79, 0)',
+                    },
+                }
+            ]
+        },
+    ]
+})
+let zb = ref()
+onMounted(() => {
+    initdatas()
+    zb.value = proportiondata.value
+})
+function initdatas(){
     console.log(props,'苏格拉底')
-    primary.value[0].value='XXX'
-    primary.value[1].value='XXX'
-    primary.value[2].value='XXX'
-    primary.value[3].value='XXX'
+    let {hiteach_data,user_channels}=props.sokratesdatas
+    //处理预设学校
+    user_channels.benefits.t_data &&  user_channels.default_channel.school_name && user_channels.default_channel.school_code ?
+    (presupposed.value.data.benefits=user_channels.benefits,
+    presupposed.value.data.default_channel=user_channels.default_channel,
+    presupposed.value.data.general_observation=user_channels.general_observation,
+    presupposed.value.state=true)
+    :presupposed.value.state=false
+    //处理total数据
+    primary.value[0].value=hiteach_data.total.t_data
+    primary.value[1].value=hiteach_data.total.t_green
+    primary.value[2].value=hiteach_data.total.double_green_light
+    // primary.value[3].value=hiteach_data.total.
 
     auxiliary.value[0].value='XXX'
     auxiliary.value[1].value='XXX'
     auxiliary.value[2].value='XXX'
     auxiliary.value[3].value='XXX'
- }
- onMounted(() => {
-    initdatas()
-})
+    //处理下方图表
+    hiteachDatas.value.weeks=hiteach_data.this_week
+    hiteachDatas.value.months=hiteach_data.this_month
+    hiteachDatas.value.years=hiteach_data.this_year
+    hiteachDatas.value.appear=hiteach_data.this_week
+    tdatas.value[0].values=hiteachDatas.value.appear.duration
+    tdatas.value[1].values=hiteachDatas.value.appear.attendance
+    tdatas.value[2].values=hiteachDatas.value.appear.interaction
+    tdatas.value[3].values=hiteachDatas.value.appear.learning_duration
+}
+function selectTime(values) {
+    console.log(values)
+    let hiteach_data=props.sokratesdatas.hiteach_data
+    values === 'week' ?  hiteachDatas.value.appear=hiteach_data.this_week:values === 'month' ? hiteachDatas.value.appear=hiteach_data.this_month:
+    values === 'year' ? hiteachDatas.value.appear=hiteach_data.this_year:''
+    tdatas.value[0].values=hiteachDatas.value.appear.duration
+    tdatas.value[1].values=hiteachDatas.value.appear.attendance
+    tdatas.value[2].values=hiteachDatas.value.appear.interaction
+    tdatas.value[3].values=hiteachDatas.value.appear.learning_duration
+    buttonSelect.value.forEach(element => {
+        element.value === values ? element.click=true: element.click=false
+    });
+
+}
  </script>
  <style scoped>
  .socrates-header,.socrates-auxiliary{
@@ -228,4 +435,103 @@ let auxiliary=ref([
     width:100%;
     height:20vh;
  }
+ .presetbox{
+    line-height: 20px;
+    text-align: left;
+    padding-left:1%;
+ }
+ .presetbox p{
+    margin-bottom: 5px;
+ }
+ .presetbox-content{
+    display: flex;
+    font-size:14px;
+ }
+ .presetbox-schoolname{
+    color:#409EFF;
+ }
+ .default-icon{
+    width: 18px;
+    height: 18px;
+    vertical-align: 0px;
+    fill: currentColor;
+    margin-right:5px
+ }
+
+
+ .selectbox{
+    display: flex;
+    flex-direction: row-reverse;
+    align-items: center;
+    justify-content: space-between;
+}
+.clickbox{
+    margin:0px 2px;
+}
+.nowaday-time{
+    font-size:14px;
+    color:#bbbecd;
+    text-align: left;
+}
+.nowaday{
+    display: flex;
+    text-align: left;
+    line-height: 20px;
+    width:100%;
+}
+.nowaday-now{
+    width:40%;
+    height:150px;
+    font-size:18px;
+    font-weight: bold;
+    line-height: 40px;
+    display: flex;
+    flex-direction: column;
+    justify-content: space-around;
+}
+.now-value{
+    font-size:22px;
+    font-weight: bold;
+}
+.nowaday-proportion{
+    width:60%;
+    height:150px;
+}
+.nowaday-proportion div div{
+    width:100%;
+    height:150px;
+}
+.nows-box{
+    line-height: 40px;
+    padding-left:5px;
+    margin-bottom: 5px;
+}
+.restbox-item{
+    padding: 1%;;
+}
+.restbox-item{
+    display: flex;
+    justify-content: space-between;
+    flex-wrap: wrap;
+    text-align: left;
+}
+.restbox-left{
+    width:50%;
+    line-height: 40px;
+}
+.left-content{
+    font-size:14px;
+    color:#bbbecd;
+}
+.left-content span{
+    font-size:16px;
+    color: #8a90a8;
+    font-weight: bold;
+}
+.hiteachbox-content{
+    display: flex;
+}
+.time-unit{
+    margin:0px 5px;
+}
 </style>

+ 85 - 23
TEAMModelBI/ClientApp/src/view/userInquire/ticket.vue

@@ -5,11 +5,14 @@
                 <div class="ticket-header-item-title">{{item.title}}</div>
                 <div class="ticket-header-item-value">{{item.value}}</div>
             </div>
+            <div class="integral-box">
+                积分统计
+            </div>
         </div>
         <el-divider />
         <div class="ticket-detail">
-            <el-tabs :tab-position="tabPosition" style="height: 305px" class="demo-tabs">
-                <el-tab-pane label="全部">
+            <el-tabs :tab-position="tabPosition" style="height: 40vh" class="demo-tabs">
+                <el-tab-pane label="已使用">
                     <div class="ticket-total">
                         <div :class="[item.type ==='use' ? 'usebox':'','ticket-total-item']" v-for="item in ticketlist" :key="item.code">
                             <div class="ticket-total-item-title">
@@ -18,19 +21,16 @@
                             </div>
                             <div class="ticket-total-item-content">
                                 <p class="ticket-total-title">授权</p>
-                                <div class="ticket-total-item-scope">
+                                <!-- <div class="ticket-total-item-scope">
                                     {{item.scope}}
                                 </div>
                                 <div class="ticket-total-item-type">
                                     {{item.source}}
+                                </div> -->
+                                <div class="ticket-total-item-time">
+                                    于{{item.exchangeTime}} 使用
                                 </div>
-                                <div class="ticket-total-item-time" v-show="item.type ==='gain'">
-                                    {{item.time}} 到期
-                                </div>
-                                <div class="ticket-total-item-usetime" v-show="item.type ==='use'">
-                                    {{item.time}} 使用
-                                </div>
-                                <div class="ticket-use" v-show="item.type ==='use'">
+                                <div class="ticket-use">
                                     <svg class="useicon" aria-hidden="true">
                                         <use xlink:href="#icon-yishiyong"></use>
                                     </svg>
@@ -39,7 +39,7 @@
                         </div>
                     </div>
                 </el-tab-pane>
-                <el-tab-pane label="使用">
+                <!-- <el-tab-pane label="使用">
                     <div class="ticket-total">
                         <div :class="[item.type ==='use' ? 'usebox':'','ticket-total-item']" v-for="item in usedata" :key="item.code">
                             <div class="ticket-total-item-title">
@@ -86,26 +86,33 @@
                             </div>
                         </div>
                     </div>
-                </el-tab-pane>
+                </el-tab-pane> -->
             </el-tabs>
+            <div class="ticket-box">
+                票卷明细
+            </div>
         </div>
     </div>
 </template>
 <script setup>
 import { ref, getCurrentInstance, watch, h, nextTick, onMounted,computed } from 'vue'
+let props = defineProps({
+    pointsandcoupons: Object,
+})
+let { proxy } = getCurrentInstance()
 let tabPosition=ref('left')
 let ticketdata = ref([
-    { title: '已使用', value: 'XXX', key: 'use' },
-    { title: '未使用', value: 'XXX', key: 'notuse' },
-    { title: '总计', value: 'XXX', key: 'total' },
+    { title: '已使用', value:0 , key: 'use' },
+    { title: '未使用', value:0, key: 'notuse' },
+    { title: '总计', value:0, key: 'total' },
 ])
 let ticketlist = ref([
-    { code: '154dsf-454sdfds-545fg4d-552', scope: 'WebIRS 50人授權', source: '系统授予', time: '2023/11/22',type:'gain'},
-    { code: '154dsf-454sdfds-545fg4d-552', scope: 'WebIRS 50人授權', source: '系统授予', time: '2023/11/22',type:'gain' },
-    { code: '154dsf-454sdfds-545fg4d-552', scope: 'WebIRS 50人授權', source: '系统授予', time: '2023/11/22',type:'use'},
-    { code: '154dsf-454sdfds-545fg4d-552', scope: 'WebIRS 50人授權', source: '系统授予', time: '2023/11/22',type:'gain' },
-    { code: '154dsf-454sdfds-545fg4d-552', scope: 'WebIRS 50人授權', source: '系统授予', time: '2023/11/22',type:'gain' },
-    {code:'154dsf-454sdfds-545fg4d-552',scope:'WebIRS 50人授權',source:'系统授予',time:'2023/11/22',type:'gain'},
+    // { code: '154dsf-454sdfds-545fg4d-552', scope: 'WebIRS 50人授權', source: '系统授予', time: '2023/11/22',type:'gain'},
+    // { code: '154dsf-454sdfds-545fg4d-552', scope: 'WebIRS 50人授權', source: '系统授予', time: '2023/11/22',type:'gain' },
+    // { code: '154dsf-454sdfds-545fg4d-552', scope: 'WebIRS 50人授權', source: '系统授予', time: '2023/11/22',type:'use'},
+    // { code: '154dsf-454sdfds-545fg4d-552', scope: 'WebIRS 50人授權', source: '系统授予', time: '2023/11/22',type:'gain' },
+    // { code: '154dsf-454sdfds-545fg4d-552', scope: 'WebIRS 50人授權', source: '系统授予', time: '2023/11/22',type:'gain' },
+    // {code:'154dsf-454sdfds-545fg4d-552',scope:'WebIRS 50人授權',source:'系统授予',time:'2023/11/22',type:'gain'},
 ])
 let usedata = computed(() => { 
     return ticketlist.value.filter((item) => { return item.type ==='use'})
@@ -113,6 +120,22 @@ let usedata = computed(() => {
 let gaindata = computed(() => {
     return ticketlist.value.filter((item) => { return item.type ==='gain'})
  })
+ function inidatas(){
+    console.log(props,'积分和票卷')
+    let {coupons,points}=props.pointsandcoupons
+    //积分
+    ticketdata.value[0].value=points.points-points.balance
+    ticketdata.value[1].value=points.balance
+    ticketdata.value[2].value=points.points
+    //票卷
+    coupons.forEach(item => {
+        item.exchangeTime=proxy.$common.timestampToTime(item.exchange,'all')
+        ticketlist.value.push(item)
+    });
+ }
+ onMounted(() => {
+    inidatas()
+})
 </script>
 <style scoped>
 .ticket-header{
@@ -120,6 +143,8 @@ let gaindata = computed(() => {
     justify-content: space-between;
     align-items: center;
     line-height: 20px;
+    position: relative;
+    padding-top: 25px;
 }
 .ticket-header-item{
     display: flex;
@@ -203,8 +228,8 @@ let gaindata = computed(() => {
     left:40%;
 }
 .useicon{
-  width: 4.5em;
-  height: 4.5em;
+  width: 4em;
+  height: 4em;
   vertical-align: 0em;
   fill: currentColor;
   overflow: hidden;
@@ -217,6 +242,43 @@ let gaindata = computed(() => {
     color:#fff;
     margin-bottom: 4px;
 }
+.integral-box{
+    width: 60px;
+    line-height: 12px;
+    font-size: 12px;
+    padding: 2px;
+    border: 1px solid #ccc;
+    border-top: none;
+    color: #bbbecd;
+    cursor: pointer;
+    background: linear-gradient(to top right, #7d82fe, #6fa5fe);
+    color: #fff;
+    margin-top: 1px;
+    position:absolute;
+    top:-5px;
+    transform: skew(-20deg);
+}
+.ticket-box{
+    width: 60px;
+    line-height: 12px;
+    font-size: 12px;
+    padding: 2px;
+    border: 1px solid #ccc;
+    border-top: none;
+    color: #bbbecd;
+    cursor: pointer;
+    background: linear-gradient(to top right, #7d82fe, #6fa5fe);
+    color: #fff;
+    margin-top: 1px;
+    position:absolute;
+    top:-5px;
+    transform: skew(-20deg);
+}
+.ticket-detail{
+    position: relative;
+    padding-top:25px;
+}
+
 /* .usebox{
     color:rgba(255,255,255,.9) !important;
 } */

+ 2 - 2
TEAMModelOS.FunctionV4/CosmosDB/TriggerArt.cs

@@ -42,7 +42,7 @@ namespace TEAMModelOS.FunctionV4.CosmosDB
                     ActivityList data = input.ToObject<ActivityList>();
                     //删除blob 相关资料
                     await _azureStorage.GetBlobServiceClient().DeleteBlobs(_dingDing, tdata.school, new List<string> { $"art/{tdata.id}" });
-                    await ActivityService.DeleteActivity(_coreAPIHttpService, client, _dingDing, data);
+                    await IESActivityService.DeleteActivity(_coreAPIHttpService, client, _dingDing, data);
                     var table_cancel = _azureStorage.GetCloudTableClient().GetTableReference("ChangeRecord");
                     List<ChangeRecord> records = await table_cancel.FindListByDict<ChangeRecord>(new Dictionary<string, object>() { { "RowKey", tdata.id } });
                     foreach (var record in records)
@@ -211,7 +211,7 @@ namespace TEAMModelOS.FunctionV4.CosmosDB
                                         });
                                     });
                                 }
-                                await ActivityService.SaveStuActivity(client, _dingDing, stuActivities, tmdActivities, tchActivities);
+                                await IESActivityService.SaveStuActivity(client, _dingDing, stuActivities, tmdActivities, tchActivities);
                                 //await StatisticsService.SendServiceBus(list, _configuration, _serviceBus, client)
 
                             }

+ 1 - 1
TEAMModelOS.FunctionV4/CosmosDB/TriggerCorrect.cs

@@ -27,7 +27,7 @@ namespace TEAMModelOS.FunctionV4
             {
                 await client.GetContainer(Constant.TEAMModelOS, "Common").DeleteItemStreamAsync(tdata.id, new PartitionKey(tdata.code));
                 ActivityList data = input.ToObject<ActivityList>();
-                await ActivityService.DeleteActivity(_coreAPIHttpService, client, _dingDing, data);
+                await IESActivityService.DeleteActivity(_coreAPIHttpService, client, _dingDing, data);
                 var table_cancel = _azureStorage.GetCloudTableClient().GetTableReference("ChangeRecord");
                 List<ChangeRecord> records = await table_cancel.FindListByDict<ChangeRecord>(new Dictionary<string, object>() { { "RowKey", tdata.id } });
                 foreach (var record in records)

+ 2 - 2
TEAMModelOS.FunctionV4/CosmosDB/TriggerExam.cs

@@ -51,7 +51,7 @@ namespace TEAMModelOS.FunctionV4
                 {
                     await client.GetContainer(Constant.TEAMModelOS, "Common").DeleteItemStreamAsync(data.id, new PartitionKey(data.code));
                     ActivityList activity = input.ToObject<ActivityList>();
-                    await ActivityService.DeleteActivity(_coreAPIHttpService, client, _dingDing, activity);
+                    await IESActivityService.DeleteActivity(_coreAPIHttpService, client, _dingDing, activity);
                     var table_cancel = _azureStorage.GetCloudTableClient().GetTableReference("ChangeRecord");
                     List<ChangeRecord> records = await table_cancel.FindListByDict<ChangeRecord>(new Dictionary<string, object>() { { "RowKey", data.id } });
                     foreach (var record in records)
@@ -1018,7 +1018,7 @@ namespace TEAMModelOS.FunctionV4
                     });
                 });
             }
-            await ActivityService.SaveStuActivity(client, _dingDing, stuActivities, tmdActivities, null);
+            await IESActivityService.SaveStuActivity(client, _dingDing, stuActivities, tmdActivities, null);
             return classLists;
         }
 

+ 2 - 2
TEAMModelOS.FunctionV4/CosmosDB/TriggerExamLite.cs

@@ -29,7 +29,7 @@ namespace TEAMModelOS.FunctionV4
                 {
                     await client.GetContainer(Constant.TEAMModelOS, "Common").DeleteItemStreamAsync(tdata.id, new PartitionKey(tdata.code));
                     ActivityList data = input.ToObject<ActivityList>();
-                    await ActivityService.DeleteActivity(_coreAPIHttpService, client, _dingDing, data);
+                    await IESActivityService.DeleteActivity(_coreAPIHttpService, client, _dingDing, data);
                     var table_cancel = _azureStorage.GetCloudTableClient().GetTableReference("ChangeRecord");
                     List<ChangeRecord> records = await table_cancel.FindListByDict<ChangeRecord>(new Dictionary<string, object>() { { "RowKey", tdata.id } });
                     foreach (var record in records)
@@ -150,7 +150,7 @@ namespace TEAMModelOS.FunctionV4
                                 });
                               
                             }
-                            await ActivityService.SaveStuActivity(client, _dingDing, null, null, tchActivities);
+                            await IESActivityService.SaveStuActivity(client, _dingDing, null, null, tchActivities);
                             await StatisticsService.SendServiceBus(list, _configuration, _serviceBus,   client);
                             var messageWorkEnd = new ServiceBusMessage(new { id = tdata.id, progress = "finish", code = tdata.code }.ToJsonString());
                             messageWorkEnd.ApplicationProperties.Add("name", "ExamLite");

+ 2 - 2
TEAMModelOS.FunctionV4/CosmosDB/TriggerHomework.cs

@@ -30,7 +30,7 @@ namespace TEAMModelOS.FunctionV4
                 {
                     await client.GetContainer(Constant.TEAMModelOS, "Common").DeleteItemStreamAsync(tdata.id, new PartitionKey(tdata.code));
                     ActivityList data = input.ToObject<ActivityList>();
-                    await ActivityService.DeleteActivity(_coreAPIHttpService, client, _dingDing, data);
+                    await IESActivityService.DeleteActivity(_coreAPIHttpService, client, _dingDing, data);
                     var table_cancel = _azureStorage.GetCloudTableClient().GetTableReference("ChangeRecord");
                     List<ChangeRecord> records = await table_cancel.FindListByDict<ChangeRecord>(new Dictionary<string, object>() { { "RowKey", tdata.id } });
                     foreach (var record in records)
@@ -377,7 +377,7 @@ namespace TEAMModelOS.FunctionV4
                 });
 
             }
-            await ActivityService.SaveStuActivity(client, _dingDing, stuActivities, tmdActivities, tchActivities);
+            await IESActivityService.SaveStuActivity(client, _dingDing, stuActivities, tmdActivities, tchActivities);
             await StatisticsService.SendServiceBus(list, _configuration, _serviceBus, client);
 
         }

+ 2 - 2
TEAMModelOS.FunctionV4/CosmosDB/TriggerStudy.cs

@@ -35,7 +35,7 @@ namespace TEAMModelOS.FunctionV4
                 {
                     await client.GetContainer(Constant.TEAMModelOS, "Common").DeleteItemStreamAsync(tdata.id, new PartitionKey(tdata.code));
                     ActivityList data = input.ToObject<ActivityList>();
-                    await ActivityService.DeleteActivity(_coreAPIHttpService, client, _dingDing, data);
+                    await IESActivityService.DeleteActivity(_coreAPIHttpService, client, _dingDing, data);
                     var table_cancel = _azureStorage.GetCloudTableClient().GetTableReference("ChangeRecord");
                     List<ChangeRecord> records = await table_cancel.FindListByDict<ChangeRecord>(new Dictionary<string, object>() { { "RowKey", tdata.id } });
                     foreach (var record in records)
@@ -178,7 +178,7 @@ namespace TEAMModelOS.FunctionV4
                                         });
                                     }                         
                                 }
-                                await ActivityService.SaveStuActivity(client, _dingDing, null, null, tchActivities);
+                                await IESActivityService.SaveStuActivity(client, _dingDing, null, null, tchActivities);
                                 await StatisticsService.SendServiceBus(list, _configuration, _serviceBus,client);
                                 var messageWorkEnd = new ServiceBusMessage(new { id = tdata.id, progress = "finish", code = tdata.code }.ToJsonString());
                                 messageWorkEnd.ApplicationProperties.Add("name", "Study");

+ 2 - 2
TEAMModelOS.FunctionV4/CosmosDB/TriggerSurvey.cs

@@ -36,7 +36,7 @@ namespace TEAMModelOS.FunctionV4
                 {
                     await client.GetContainer(Constant.TEAMModelOS, "Common").DeleteItemStreamAsync(tdata.id, new PartitionKey(tdata.code));
                     ActivityList data = input.ToObject<ActivityList>();
-                    await ActivityService.DeleteActivity(_coreAPIHttpService, client, _dingDing, data);
+                    await IESActivityService.DeleteActivity(_coreAPIHttpService, client, _dingDing, data);
                     _azureRedis.GetRedisClient(8).KeyDelete($"Survey:Record:{tdata.id}");
                     _azureRedis.GetRedisClient(8).KeyDelete($"Survey:Submit:{tdata.id}");
                     var table_cancel = _azureStorage.GetCloudTableClient().GetTableReference("ChangeRecord");
@@ -267,7 +267,7 @@ namespace TEAMModelOS.FunctionV4
                                 });
 
                             }
-                            await ActivityService.SaveStuActivity(client, _dingDing, stuActivities, tmdActivities, tchActivities);
+                            await IESActivityService.SaveStuActivity(client, _dingDing, stuActivities, tmdActivities, tchActivities);
                             await StatisticsService.SendServiceBus(list, _configuration, _serviceBus, client);
                             //向学生或醍摩豆账号发起通知
                             #region

+ 2 - 2
TEAMModelOS.FunctionV4/CosmosDB/TriggerVote.cs

@@ -35,7 +35,7 @@ namespace TEAMModelOS.FunctionV4
                 {
                     await client.GetContainer(Constant.TEAMModelOS, "Common").DeleteItemStreamAsync(tdata.id, new PartitionKey(tdata.code));
                     ActivityList data = input.ToObject<ActivityList>();
-                    await ActivityService. DeleteActivity(_coreAPIHttpService, client, _dingDing, data);
+                    await IESActivityService. DeleteActivity(_coreAPIHttpService, client, _dingDing, data);
                     _azureRedis.GetRedisClient(8).KeyDelete($"Vote:Record:{tdata.id}");
                     _azureRedis.GetRedisClient(8).KeyDelete($"Vote:Count:{tdata.id}");
                     var table_cancel = _azureStorage.GetCloudTableClient().GetTableReference("ChangeRecord");
@@ -270,7 +270,7 @@ namespace TEAMModelOS.FunctionV4
                             //await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}投票活动,:教研组活动:" +
 
                             //   $"{tchActivities.ToJsonString()}\n", GroupNames.醍摩豆服務運維群組);
-                            await ActivityService.SaveStuActivity(client, _dingDing, stuActivities, tmdActivities, tchActivities);
+                            await IESActivityService.SaveStuActivity(client, _dingDing, stuActivities, tmdActivities, tchActivities);
                             await StatisticsService.SendServiceBus(list, _configuration, _serviceBus, client);
                             //向学生或醍摩豆账号发起通知
                             #region

+ 8 - 8
TEAMModelOS.FunctionV4/ServiceBus/ActiveTaskTopic.cs

@@ -499,25 +499,25 @@ namespace TEAMModelOS.FunctionV4.ServiceBus
                 //await StuListService.FixStuCourse(client, stuListChange);
                 //Vote投票 Survey问卷 Exam评测 Learn学习活动 Homework作业活动
                 //名单变动修改学生问卷关联信息
-                await ActivityService.FixActivity(client, _dingDing, groupChange, "Survey");
+                await IESActivityService.FixActivity(client, _dingDing, groupChange, "Survey");
                 //名单变动修改学生投票关联信息
-                await ActivityService.FixActivity(client, _dingDing, groupChange, "Vote");
+                await IESActivityService.FixActivity(client, _dingDing, groupChange, "Vote");
                 //名单变动修改学生评测关联信息
-                await ActivityService.FixActivity(client, _dingDing, groupChange, "Exam");
+                await IESActivityService.FixActivity(client, _dingDing, groupChange, "Exam");
                 //名单变动修改学生研修关联信息
-                await ActivityService.FixActivity(client, _dingDing, groupChange, "Study");
+                await IESActivityService.FixActivity(client, _dingDing, groupChange, "Study");
                 //名单变动修改学生简易评测关联信息
-                await ActivityService.FixActivity(client, _dingDing, groupChange, "ExamLite");
+                await IESActivityService.FixActivity(client, _dingDing, groupChange, "ExamLite");
                 //名单变动修改学生作业活动信息
-                await ActivityService.FixActivity(client, _dingDing, groupChange, "Homework");
+                await IESActivityService.FixActivity(client, _dingDing, groupChange, "Homework");
                 //名单变动修改学生艺术评价活动信息
-                await ActivityService.FixActivity(client, _dingDing, groupChange, "Art");
+                await IESActivityService.FixActivity(client, _dingDing, groupChange, "Art");
                 //TODO学习活动
                 //await FixActivity(client, stuListChange, "Learn");
                 if (groupChange.type == null || !groupChange.type.Equals("research") || !groupChange.type.Equals("yxtrain") || !groupChange.type.Equals("activity"))
                 {
                     //课程名单变动修改学生课程关联信息
-                    await ActivityService.FixStuCourse(client, _dingDing, groupChange);
+                    await IESActivityService.FixStuCourse(client, _dingDing, groupChange);
                     //名单变动修改课例关联信息
                     //await ActivityService.FixLessonRecord(client, _dingDing, groupChange);
                 }

+ 46 - 15
TEAMModelOS.SDK/Models/Cosmos/Common/Activity.cs

@@ -35,6 +35,11 @@ namespace TEAMModelOS.SDK.Models
         [Required(ErrorMessage = "Required")]
         public string owner { get; set; }
         /// <summary>
+        /// "所有者名称",
+        /// </summary>
+        [Required(ErrorMessage = "Required")]
+        public string ownerName { get; set; }
+        /// <summary>
         /// "public公开/area区级/school校级",  public公开活动 只能是醍摩豆智慧学区才能选择,
         /// </summary>
         [Required(ErrorMessage = "Required")]
@@ -47,7 +52,11 @@ namespace TEAMModelOS.SDK.Models
         /// <summary>
         /// //区级活动时允许参与的学校,如果为空则全部学校
         /// </summary>
-        public List<ActivitySchool > schools { get; set; } = new List<ActivitySchool>();
+        public List<ActivityInvitedSchool> invitedSchools { get; set; } = new List<ActivityInvitedSchool>();
+        /// <summary>
+        /// 确认的学校
+        /// </summary>
+        public List<ActivityConfirmedSchool> confirmedSchools { get; set; } = new List<ActivityConfirmedSchool>();
         /// <summary>
         /// //邀请制,允许参加的教师
         /// </summary>
@@ -86,21 +95,28 @@ namespace TEAMModelOS.SDK.Models
         public string name { get; set; }
         public string picture { get; set; }
         public string school { get; set; }
+        public string schoolName { get; set; }
         /// <summary>
         /// 0 未确认,1已确认,用于区级发布, publish=1,joinMode=invite,学校可以去进行邀请某一些教师。
         /// </summary>
         public int status { get; set; }
     }
-    public class ActivitySchool
+
+    public class ActivityInvitedSchool
     {
         public string id { get; set; }
         public string name { get; set; }
         public string picture { get; set; }
-        public string school { get; set; }
+    }
+    public class ActivityConfirmedSchool: ActivityInvitedSchool
+    {
+       
         /// <summary>
         /// 0 未确认,1已确认,用于区级发布, publish=1,joinMode=invite,学校可以去进行邀请某一些教师。
         /// </summary>
         public int status { get; set; }
+        //public string areaId { get; set; }
+        //public string areaName { get; set; }
     }
 
     /// <summary>
@@ -143,13 +159,17 @@ namespace TEAMModelOS.SDK.Models
         ///  //default 默认,评审规则
         /// </summary>
         public string ruleId { get; set; }
+        public string ruleName { get; set; }
     }
     public class ReviewRule :CosmosEntity{
         public ReviewRule() {
             pk="ReviewRule";
-            code="ReviewRule";
+           
         }
-        //id  uuid
+        /// <summary>
+        ///  code="ReviewRule-disposable"; 存为活动   code="ReviewRule-template"; 存为模板
+        /// </summary>
+        //id 为活动的id ,以此用于来推断 模板评审规则或者活动评审规则的来源
         [Required(ErrorMessage = "Required")]
         public string name  { get; set; }
         /// <summary>
@@ -168,6 +188,10 @@ namespace TEAMModelOS.SDK.Models
         [Required(ErrorMessage = "Required")]
         public string type { get; set; }
         public List<RuleConfig> configs { get; set; } = new List<RuleConfig>(); 
+        /// <summary>
+        /// 模板来源 name 
+        /// </summary>
+        public string sourceName { get; set; }
     }
     public class RuleConfig {
         public string id { get; set; }
@@ -175,9 +199,13 @@ namespace TEAMModelOS.SDK.Models
         public string label {  get; set; }
       
         public double score {  get; set; }
-      
+        public int order { get; set; }
+        public List<string> cids { get; set; } = new List<string>();
+
+
     }
     public class ReviewRuleTree {
+
         [Required(ErrorMessage = "Required")]
         public string name { get; set; }
         /// <summary>
@@ -185,16 +213,12 @@ namespace TEAMModelOS.SDK.Models
         /// </summary>
         [Required(ErrorMessage = "Required")]
         public string owner { get; set; }
+
         /// <summary>
-        /// "public公开/area区级/school校级",  public公开活动 只能是醍摩豆智慧学区才能选择,
-        /// </summary>
-        //[Required(ErrorMessage = "Required")]
-        //public string scope { get; set; }
-        /// <summary>
-        /// template 模板,disposable 一次性的,如果存为模板,则需要再保存一份活动引用的
+        /// 是否将规则更新或保存为模板
         /// </summary>
-        [Required(ErrorMessage = "Required")]
-        public string type { get; set; }
+        public int upsertAsTemplate { get; set; } = 0;
+        public string sourceName { get; set; }
         public RuleConfigTree tree{ get; set; }
 
     }
@@ -374,10 +398,18 @@ namespace TEAMModelOS.SDK.Models
         public string id { get; set; }
         public string name { get; set; }
         public string mobile { get; set; }
+        public string picture { get; set; }
         /// <summary>
         /// 职称
         /// </summary>
         public string title { get; set; }
+        public List<ExpertSubject> subjects { get; set; } = new List<ExpertSubject>();
+        /// <summary>
+        /// 评审专家可以对哪些模块进行评审
+        /// </summary>
+        public List<string> modules { get; set; } = new List<string>();
+    }
+    public class ExpertSubject {
         /// <summary>
         /// 科目
         /// </summary>
@@ -386,6 +418,5 @@ namespace TEAMModelOS.SDK.Models
         /// 学段
         /// </summary>
         public string period { get; set; }
-        public string picture { get; set; }
     }
 }

+ 100 - 0
TEAMModelOS.SDK/Models/Service/Common/ActivityService.cs

@@ -0,0 +1,100 @@
+using HTEXLib.COMM.Helpers;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using TEAMModelOS.SDK.DI;
+using TEAMModelOS.SDK.Extension;
+using TEAMModelOS.SDK.Models;
+
+namespace TEAMModelOS.SDK
+{
+    public static class ActivityService
+    {
+        public static async Task<ReviewRule> UpsertReviewRule(ReviewRuleTree reviewRuleTree,Activity activity,AzureCosmosFactory _azureCosmos) 
+        {
+            var nodes = new List<RuleConfig>();
+            nodes= TreeToList(new List<RuleConfigTree> { reviewRuleTree.tree }, nodes);
+            ReviewRule reviewRule = new ReviewRule() {
+                id= activity.id,
+                code="ReviewRule-disposable",
+                pk="ReviewRule",
+                name=reviewRuleTree.name,
+                owner=activity.owner,
+                type="disposable",
+                configs=nodes,
+                sourceName=activity.name
+            };
+            await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS,Constant.Normal).UpsertItemAsync(reviewRule);
+            if (reviewRuleTree.upsertAsTemplate==1) {
+                reviewRule.code="ReviewRule-template";
+                reviewRule.type="template";
+             await   _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Normal).UpsertItemAsync(reviewRule);
+            }
+            return reviewRule;
+        }
+
+        public static List<RuleConfig> TreeToList(List<RuleConfigTree> trees, List<RuleConfig> nodes) {
+            trees = trees.OrderBy(x => x.order).ToList();
+            List<RuleConfig> list = new List<RuleConfig>();
+            trees.ForEach(x => {
+                List<string> cids = new List<string>();
+                if (x.children.IsNotEmpty())
+                {
+                    x.children.ForEach(y => cids.Add(y.id));
+                }
+                var node = new RuleConfig
+                {
+                    
+                    id = x.id,
+                    pid = x.pid,
+                    cids= cids,
+                    label = x.label,
+                    score = x.score,
+                    order = x.order,
+                };
+                list.Add(node);
+            });
+            nodes.AddRange(list);
+            foreach (RuleConfigTree tree in trees)
+            {
+                if (null != tree.children && tree.children.Count > 0)
+                {
+                    TreeToList(tree.children, nodes);
+                }
+            }
+            return nodes;
+        }
+
+        public static List<RuleConfigTree> ListToTree(List<RuleConfigTree> noes)
+        {
+            List<RuleConfigTree> list = noes.ToJsonString().ToObject<List<RuleConfigTree>>();
+            var res = from r in list group r by r.id into g select g;
+            Dictionary<string, RuleConfigTree> blockDict = new Dictionary<string, RuleConfigTree>();
+            foreach (var s in res)
+            {
+                blockDict.TryAdd(s.First().id, s.First());
+            }
+            return GetChild(list, blockDict);
+        }
+        private static List<RuleConfigTree> GetChild(List<RuleConfigTree> list, Dictionary<string, RuleConfigTree> dict)
+        {
+            List<RuleConfigTree> trees = new List<RuleConfigTree>();
+            trees = trees.OrderBy(x => x.order).ToList();
+            foreach (RuleConfigTree node in list)
+            {
+                bool flag = dict.TryGetValue(node.pid, out RuleConfigTree syllabus);
+                if (flag && syllabus != null)
+                {
+                    syllabus.children.Add(node);
+                }
+                else
+                {
+                    trees.Add(node);
+                }
+            }
+            return trees;
+        }
+    }
+}

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

@@ -17,7 +17,7 @@ using HTEXLib.COMM.Helpers;
 
 namespace TEAMModelOS.SDK
 {
-    public class ActivityService
+    public class IESActivityService
     {
         public static async Task FixActivity(CosmosClient client, DingDing _dingDing, GroupChange groupChange, string type)
         {
@@ -881,7 +881,7 @@ namespace TEAMModelOS.SDK
                 });
 
             }
-            await ActivityService.DeleteStuActivity(client, _dingDing, stuActivities, tmdActivities, tchActivities);
+            await IESActivityService.DeleteStuActivity(client, _dingDing, stuActivities, tmdActivities, tchActivities);
         }
         public static async Task<string> DeleteStuActivity(CosmosClient client, DingDing _dingDing, List<StuActivity> stuActivities, List<StuActivity> tmdActivities, List<StuActivity> tchActivities)
         {
@@ -916,6 +916,8 @@ namespace TEAMModelOS.SDK
             }
             return "";
         }
+
+      
     }
 
     public class ActivityList

+ 14 - 8
TEAMModelOS/ClientApp/public/lang/en-US.js

@@ -1152,7 +1152,7 @@ const LANG_EN_US = {
         cusTab11: 'Create Survey',
         cusTab12: 'Create Poll',
         cusTab13: 'Syllabus',
-        cusTab14: 'Sorting Syllabus',
+        cusTab14: 'Sort 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",
@@ -2986,7 +2986,7 @@ const LANG_EN_US = {
             calcing: 'Result data are being processed now.,',
             clickFresh: 'Click here to refresh',
             inCalc: 'The data is being processed, please check later',
-            total: 'Total Score',
+            total: 'Total',
             avgScore1: 'Total Average Score',
             totalLabel: 'Total Quantity',
             scoreMat: 'Score Segment',
@@ -4581,7 +4581,7 @@ const LANG_EN_US = {
             uploadTime: "Upload Time",
         },
         type: {
-            home: "Home",
+            home: "",
             activity: "Activity",
             studyview: "Self-directed Learning",
             hiteach: "HiTeach Lesson Record",
@@ -5632,7 +5632,13 @@ const LANG_EN_US = {
         importTip4: '3. Please follow the import template format strictly when editing.',
         importTip5: 'Parsing template...',
         importTip6: 'Select the Excel file to import',
-        importTip7: 'Batch Import Questions',
+        importTip7: 'Batch Import Outline & Questions',
+        importTip8: 'Import Preview',
+        importTip9: 'Note: Please check if the syllabus you have imported matches the following structure, if not, please adjust and re-import.',
+        importTip10: 'Cancel',
+        importTip11: 'Confirm',
+        importTip12: 'Please upload the correct format according to the template!',
+        importTip13: 'The file cannot be larger than 10M!',
         shareContentText1: 'Syllabus Sharing',
         shareContentText2: 'Sharer',
         shareContentText3: 'Syllabus Name',
@@ -6101,7 +6107,7 @@ const LANG_EN_US = {
         tableC4: 'File Size',
         tableC5: 'Number of associations',
         tableC6: 'Upload Date',
-        searchText: 'Please enter keywords to search',
+        searchText: 'Enter keywords to search',
         btnUpload: 'Upload Resources',
         tips1: 'Large Icons',
         tips2: 'List',
@@ -7756,8 +7762,8 @@ const LANG_EN_US = {
         APIerr: 'API request failed',  
         AddScoreSheet:'Add Score Sheet',
         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',          
+        addNewTable:'Add a "new" score sheet',
+        InheritTable:'"Continue with existing score sheet',
+        addTableNotice:'Note: The scores of the newly added score sheet are based on the current scores in the system, and the scores are not linked to other functions.',          
     }
 }

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

@@ -5631,6 +5631,12 @@ const LANG_ZH_CN = {
         importTip5: '模板解析中...',
         importTip6: '选择导入的Excel文件',
         importTip7: '批量导题',
+        importTip8: '导入预览',
+        importTip9: '温馨提示:请检查您导入的课纲数据是否与以下结构符合,如不符合请调整后重新导入',
+        importTip10: '取消导入',
+        importTip11: '确认导入',
+        importTip12: '请上传正确的模板格式!',
+        importTip13: '模板文件不能大于10M!',
         shareContentText1: '课纲分享',
         shareContentText2: '分享者',
         shareContentText3: '课纲名称',

+ 7 - 1
TEAMModelOS/ClientApp/public/lang/zh-TW.js

@@ -5633,7 +5633,13 @@ const LANG_ZH_TW = {
         importTip4: '3. 請嚴格遵循匯入模板格式進行編輯。',
         importTip5: '解析模板中...',
         importTip6: '選擇要匯入的Excel文件',
-        importTip7: '批次匯入題目',
+        importTip7: '批次匯入目錄與題目',
+        importTip8: '匯入預覽',
+        importTip9: '提示:請檢查您匯入的課綱資料是否與以下結構符合,如不符合請調整後重新匯入',
+        importTip10: '取消匯入',
+        importTip11: '確認匯入',
+        importTip12: '請依照範本上傳正確的格式!',
+        importTip13: '檔案不能大於10M!',
         shareContentText1: '課綱分享',
         shareContentText2: '分享者',
         shareContentText3: '課綱名稱',

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

@@ -59,7 +59,7 @@ export default {
       sessionStorage.setItem('areaAccess', this.areaList[this.curAreaIndex].access)
       // this.$EventBus.$emit('onGlobalLoading', true)
       this.$EventBus.$emit('onSwitchArea')
-      this.$emit('on-area-change', this.areaList[this.curAreaIndex].areaId)
+      this.$emit('on-area-change', this.areaList[this.curAreaIndex])
       // setTimeout(() => {
       // 	this.$EventBus.$emit('onGlobalLoading', false)
       // }, 500)

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

@@ -1136,17 +1136,6 @@
 											child: [],
 											isShow: this.hasDashAuth && !this.isGlobalSite && this.edition === "pro"
 										},
-										// 校园风采
-										{
-											icon: "iconfont icon-k-point",
-											name: this.$t("system.menu.schoolElegant"),
-											router: "/home/schoolElegant",
-											tag: "",
-											role: "admin",
-											permission: "dashboard-read",
-											menuName: "schoolElegant",
-											isShow: this.hasDashAuth && !this.isGlobalSite && this.edition === "pro"
-										},
 										// 学情分析
 										{
 											icon: "iconfont icon-xueqing",
@@ -1160,6 +1149,17 @@
 											child: [],
 											isShow: this.IES5Menu
 										},
+										// 校园风采
+										{
+											icon: "iconfont icon-k-point",
+											name: this.$t("system.menu.schoolElegant"),
+											router: "/home/schoolElegant",
+											tag: "",
+											role: "admin",
+											permission: "dashboard-read",
+											menuName: "schoolElegant",
+											isShow: this.hasDashAuth && !this.isGlobalSite && this.edition === "pro"
+										},
 										{
 											icon: "iconfont icon-basic-setting",
 											name: this.$t("system.menu.judgeSettings"),

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

@@ -515,7 +515,7 @@ export default {
       .item-content {
         font-size: 14px;
         margin-left: 20px;
-        word-break: break-all;
+        word-break: break-word;
         white-space: break-spaces;
         color: var(--second-text-color);
       }

+ 1 - 1
TEAMModelOS/ClientApp/src/components/student-web/ClassRecord/RecordView.vue

@@ -334,7 +334,7 @@ export default {
         },
         getRecordList() {
             let param = {
-                tmdid: this.courseNow.teacherId,
+                // tmdid: this.courseNow.teacherId, //同时获取协老师的课例,不传tmdid
                 scope: this.courseNow.scope, //school:传school,private:传tmdid
                 school: "",
                 category: [],

+ 14 - 7
TEAMModelOS/ClientApp/src/utils/public.js

@@ -552,6 +552,9 @@ export default {
 				.day
 			let startTime = ''
 			let endTime = ''
+			console.error(startMonth)
+			console.error(curYear)
+			console.error(settingSemesters[curSemeterIndex])
 			// 如果当前学校只设置了一个学期
 			if (settingSemesters.length === 1) {
 				startMonth = settingSemesters[0].month
@@ -603,14 +606,18 @@ export default {
 		const currentDay = today.getDate();
 		let currentSemester = semesters.find(i => i.start === 1);
 		let year = ''
+		// console.error(currentSemester)
+		// console.error(currentMonth)
+		// console.error(currentYear)
 		if (currentMonth >= currentSemester.month) {
-			if (currentDay > currentSemester.day) {
-				year = currentYear
-			} else if (currentMonth > currentSemester.month && currentDay < currentSemester.day) {
-				year = currentYear
-			} else {
-				year = currentYear - 1
-			}
+			// if (currentDay > currentSemester.day) {
+			// 	year = currentYear
+			// } else if (currentMonth > currentSemester.month && currentDay < currentSemester.day) {
+			// 	year = currentYear
+			// } else {
+			// 	year = currentYear - 1
+			// }
+			year = currentYear
 		} else {
 			year = currentYear - 1
 		}

+ 1 - 0
TEAMModelOS/ClientApp/src/view/areaArtExam/AcQuos.vue

@@ -315,6 +315,7 @@ export default {
     align-items: baseline;
     padding: 8px 0px;
 }
+
 .data-wrap {
     min-height: 300px;
     background: #f5f7fa;

+ 16 - 14
TEAMModelOS/ClientApp/src/view/areaMgmt/AreaLayout.vue

@@ -3,7 +3,7 @@
     <!-- 头部菜单栏 -->
     <Header class="header">
       <div class="logo-wrap">
-        <BaseAreaList @on-area-change="getAreaId"></BaseAreaList>
+        <BaseAreaList @on-area-change="getAreaItem"></BaseAreaList>
       </div>
       <div class="school-wrap">
       </div>
@@ -185,14 +185,15 @@ export default {
       // studentMenu: [], //学生综合素质监测
       // studyMenu: [],//学生学业质量监测
       srvAdr: '',
-      areaId: ''
+      areaId: '',
+      curArea:null
     }
   },
 
   methods: {
-    getAreaId (areaId) {
-      console.log(areaId)
-      this.areaId = areaId
+    getAreaItem (area) {
+      this.curArea = area
+      this.areaId = area.areaId
     },
     //获取快速登录的code
     getLoginCode () {
@@ -202,15 +203,16 @@ export default {
     },
     //教师个人跳转苏格拉底
     toPrivSokrate () {
-      this.getLoginCode().then(
-        res => {
-          this.loginCode = res.code
-          let url = `${this.curSiteConfig.sokrateUrl}/auth/login/callback-habook?code=${this.loginCode}`
-          window.open(url, '', 'noopener')
-        }
-      ).finally(() => {
-        this.isLoading = false
-      })
+      // this.getLoginCode().then(
+      //   res => {
+      //     this.loginCode = res.code
+      //     let url = `${this.curSiteConfig.sokrateUrl}/auth/login/callback-habook?code=${this.loginCode}`
+      //     window.open(url, '', 'noopener')
+      //   }
+      // ).finally(() => {
+      //   this.isLoading = false
+      // })
+      window.open('https://sokrates.teammodel.cn/district/CDHT#/')
     },
     toActivity () {
       this.getLoginCode().then(

+ 1 - 0
TEAMModelOS/ClientApp/src/view/artexam/AcQuos.vue

@@ -327,6 +327,7 @@ export default {
   align-items: baseline;
   padding: 8px 0px;
 }
+
 .data-wrap {
   min-height: 300px;
   background: #f5f7fa;

+ 47 - 6
TEAMModelOS/ClientApp/src/view/artexam/Create.vue

@@ -45,8 +45,11 @@
             <el-cascader ref="evtarget" size="small" :show-all-levels="false" clearable :options="csOptions" :props="props" @change="treeChange" style="width: 100%">
             </el-cascader>
           </FormItem>
-          <FormItem :label="$t('ae.ae7')" prop="time" style="margin-top: 30px">
-            <DatePicker v-model="artInfo.time" transfer type="datetimerange" placement="bottom-start" :placeholder="$t('ae.ae8')" @on-change="handleSetTime" style="width: 100%"></DatePicker>
+          <FormItem :label="`资料收集`"  prop="uploadTime" style="margin-top: 30px">
+            <DatePicker transfer type="datetimerange" v-model="artInfo.uploadTime" placement="bottom-start" :placeholder="`请设置艺术资料上传时间范围`" @on-change="handleSetUploadTime" style="width: 100%"></DatePicker>
+          </FormItem>
+          <FormItem :label="`正式测评`"  prop="time" style="margin-top: 30px">
+            <DatePicker transfer type="datetimerange" v-model="artInfo.time" placement="bottom-start" :placeholder="`请设置学生正式测评时间范围`" @on-change="handleSetTime" style="width: 100%"></DatePicker>
           </FormItem>
           <FormItem label="学段" prop="periodType" style="margin-top: 30px" v-if="isArea">
             <Select v-model="artInfo.periodType" placeholder="请选择施测适用学段">
@@ -132,6 +135,19 @@ export default {
       }
       callback();
     };
+    const validateUploadTime = (rule, value, callback) => {
+      if (!Array.isArray(value)) {
+        return callback(new Error('请设置资料收集时间'));
+      }
+      if (value.length != 2) {
+        return callback(new Error('请设置资料收集时间'));
+      }
+      let isFull = value.every(t => !!t)
+      if (!isFull) {
+        return callback(new Error('请设置资料收集时间'));
+      }
+      callback();
+    };
     let _this = this
     return {
       pdTypeList: this.$GLOBAL.PERIOD_TYPE_LIST(),
@@ -166,6 +182,9 @@ export default {
         startTime: 0,
         endTime: 0,
         time: [],
+        uploadTime:[],
+        uploadSTime:0,
+        uploadETime:0,
         targets: [],
         subjects: [],
         setting: [],
@@ -210,7 +229,15 @@ export default {
             message: '请设置时间',
             trigger: "blur"
           }
-        ]
+        ],
+        uploadTime: [
+          {
+            required: true,
+            validator: validateUploadTime,
+            message: '请设置资料收集时间',
+            trigger: "blur"
+          }
+        ],
       },
       // 学业指标相关设置
       studySetting: {
@@ -304,7 +331,7 @@ export default {
   },
   methods: {
     goBack(){
-      this.$router.go(-1)
+      this.$router.push(this.isArea ? 'areaArtMgt' : 'mgtArtExam')
     },
     test() {
       console.log(this.settingDetail, this.getZYData())
@@ -322,6 +349,12 @@ export default {
       this.artInfo.startTime = start ? new Date(start).getTime() : 0
       this.artInfo.endTime = end ? new Date(end).getTime() : 0
     },
+    handleSetUploadTime(value) {
+      let start = value[0]
+      let end = value[1]
+      this.artInfo.uploadSTime = start ? new Date(start).getTime() : 0
+      this.artInfo.uploadETime = end ? new Date(end).getTime() : 0
+    },
     toArtMgt() {
       this.$router.push({
         name: this.isArea ? 'areaArtMgt' : "mgtArtExam"
@@ -480,6 +513,8 @@ export default {
           settings: settings,
           startTime: this.artInfo.startTime,
           endTime: this.artInfo.endTime,
+          uploadSTime: this.artInfo.uploadSTime,
+          uploadETime: this.artInfo.uploadETime,
           subjects: this.subjectList.filter(item => this.artInfo.subjects.includes(item.id))
         }
         let promise
@@ -813,6 +848,10 @@ export default {
   margin-top: 30px;
   cursor: pointer;
 }
+
+/deep/ .create-form-wrap .ivu-tabs-nav .ivu-tabs-tab{
+  font-size: 16px;
+}
 .success-text {
   text-align: center;
   margin-top: 10px;
@@ -839,8 +878,10 @@ export default {
 }
 .check-item {
   user-select: none;
-  display: block;
+  // display: block;
   margin-bottom: 20px;
+  margin-right: 40px;
+  font-size: 16px;
 }
 .art-form {
   width: 1000px;
@@ -874,7 +915,7 @@ export default {
   cursor: pointer;
   text-align: center;
   padding: 5px 10px;
-  box-shadow: 0px 0px 3px rgba(0, 0, 0, 0.2);
+  // box-shadow: 0px 0px 3px rgba(0, 0, 0, 0.2);
   border-radius: 5px;
   color: #1cc0f3;
   font-size: 12px;

+ 3 - 3
TEAMModelOS/ClientApp/src/view/artexam/ExamSetting.vue

@@ -249,12 +249,12 @@ export default {
 
 <style lang="less" scoped>
 .attr-item {
-  margin-bottom: 10px;
+  margin-bottom: 20px;
 }
 .subject-content {
-  display: inline-block;
+  display: block;
   width: calc(100% - 80px);
-  vertical-align: top;
+  margin-top: 20px;
 }
 .back-to-list {
   color: #2d8cf0;

+ 5 - 4
TEAMModelOS/ClientApp/src/view/artexam/ExamSubject.vue

@@ -74,16 +74,17 @@ export default {
 
 <style lang="less" scoped>
 .content-block{
-	background: #f0f0f0;
-	padding: 8px 5px;
-	margin: 10px 0px;
+  background: #f7f7f7;
+  padding: 20px;
+  margin: 10px 20px 10px 0;
+  border-radius: 6px;
 }
 .check-item{
     margin-bottom: 10px;
 }
 .subject-name{
 	font-weight: 600;
-	color: black;
+	color: #2D8CF0;
 	margin-right: 10px;
 }
 </style>

+ 10 - 2
TEAMModelOS/ClientApp/src/view/artexam/QuoTree.vue

@@ -60,7 +60,7 @@ export default {
             h('Tag', {
               class: "type-setting",
               props: {
-                color: 'primary'
+                color: node.data.type == 2 ? 'geekblue' : 'green'
               },
               style: {
                 display: node.checked && (node.data.type == 1 || node.data.type == 2) ? undefined : "none"
@@ -135,10 +135,18 @@ export default {
 </style>
 <style lang="less">
 .type-setting {
-  float: right;
+  // float: right;
+  margin-left: 10px;
+  align-items: baseline;
 }
 .quo-tree .el-tree-node__content {
   height: fit-content;
   align-items: baseline;
+  padding: 8px 0;
+  font-size: 16px;
+}
+.quo-tree .el-tree-node:focus>.el-tree-node__content,
+.quo-tree .el-tree-node__content:hover{
+  background: #d4e1ff !important;
 }
 </style>

+ 4 - 4
TEAMModelOS/ClientApp/src/view/artexam/WorkSetting.vue

@@ -2,8 +2,8 @@
     <div class="work-setting-container">
         <!-- 作业描述 -->
 		<div class="attr-item">
-			<span style="vertical-align: top;">{{$t('ae.ae37')}}</span>
-            <Input @on-change="handleSetWork" v-model="workSetting.desc" type="textarea" maxlength="100" :rows="2" style="width:500px" :placeholder="$t('ae.ae38')"/>
+			<!-- <span style="vertical-align: top;">{{$t('ae.ae37')}}</span> -->
+            <Input @on-change="handleSetWork" v-model="workSetting.desc" type="textarea" maxlength="100" :rows="4" style="width:500px" :placeholder="$t('ae.ae38')"/>
 		</div>
         <!-- 截止时间 -->
 		<!-- <div class="attr-item">
@@ -56,9 +56,9 @@
 
 <style lang="less" scoped>
 .work-setting-container{
-    display: inline-block;
+    display: block;
     width: calc(100% - 80px);
-    vertical-align: top;
+    margin: 10px 0 20px 0;
 }
 .attr-item{
     margin-bottom: 10px;

+ 5 - 4
TEAMModelOS/ClientApp/src/view/artexam/WorkSubject.vue

@@ -87,15 +87,16 @@ export default {
 <style lang="less" scoped>
 .subject-name {
   font-weight: 600;
-  color: black;
+  color: #2D8CF0;
   margin-right: 10px;
 }
 .check-item {
   margin-bottom: 10px;
 }
 .content-block {
-  background: #f0f0f0;
-  padding: 8px 5px;
-  margin: 10px 0px;
+  background: #f7f7f7;
+  padding: 20px;
+  margin: 10px 20px 10px 0;
+  border-radius: 6px;
 }
 </style>

+ 7 - 0
TEAMModelOS/ClientApp/src/view/classrecord/ClassRecord.vue

@@ -631,6 +631,13 @@ export default {
               break
           }
         })
+        for (let i = 1; i < page.pageData.length; i++) {
+          // 星光大评分会操作几次就返回几条数据,所以需要去重只显示第一条
+          if(page.pageData[i - 1].Event === 'RatingStart' && page.pageData[i].Event === 'RatingStart' && page.pageData[i].RatingType && page.pageData[i].RatingType === 'GrandRating' && page.pageData[i - 1].Pgid === page.pageData[i].Pgid) {
+            page.pageData.splice(i, 1)
+            i--
+          }
+        }
         this.pageList.push(page)
       })
       console.log(this.pageList)

+ 1 - 1
TEAMModelOS/ClientApp/src/view/classrecord/eventchart/ExamGrade.vue

@@ -54,7 +54,7 @@ export default {
                             bottom: 30
                         },
                         title: {
-                            "text": `${this.isTotal ? this.$t('learnActivity.simple.total') : ''}${this.$t('learnActivity.simple.scoreMat')}`,
+                            "text": `${this.isTotal ? this.$t('learnActivity.simple.total') : ''} ${this.$t('learnActivity.simple.scoreMat')}`,
                             "left": "center",
                             "top": 8,
                             "textStyle": {

+ 177 - 109
TEAMModelOS/ClientApp/src/view/elegant/Elegant.vue

@@ -1,8 +1,20 @@
 <template>
 	<div class="elegant-container">
+		<Spin fix v-if="isLoading"></Spin>
 		<div class="header">
 			<span class="title">学生风采</span>
-			<Button type="success" size="small" @click="onAddElegant">+ 添加素材</Button>
+			<div>
+				<Button type="success" size="small" @click="onDownloadExcel" icon="md-download" style="margin-right: 10px">导出数据</Button>
+				<Button type="success" size="small" @click="onAddElegant">+ 添加素材</Button>
+			</div>
+		</div>
+		<div class="filter-wrap">
+			<!-- 类型筛选 -->
+			<Select v-model="filterTypeIndex" style="width:200px;"  @on-change="onFilterTypeChange">
+				<Option v-for="(item,index) in typeList" :value="index" :key="index">
+					{{ item }}
+				</Option>
+			</Select>
 		</div>
 		<div class="elegant-list">
 			<EmptyData :top="300" v-if="!elegantList.length"></EmptyData>
@@ -11,7 +23,8 @@
 					<p class="elegant-title">
 						<span>{{ item.title }}</span>
 						<span style="margin: 0 10px">
-							<Tag color="geekblue" v-if="item.bizType">{{ item.bizType.join('-') }}</Tag>
+							<Tag color="geekblue" v-if="item.bizType">{{ item.bizType.join("-") }}</Tag>
+							<Tag color="green" v-if="item.classes && item.classes.length">{{ getClassNameById(item.classes[0]) }}</Tag>
 							<Tag color="primary">{{ $tools.formatTime(item.createTime) }}</Tag>
 						</span>
 						<!-- <Icon type="md-create" color="#2d8cf0" @click="onEditElegant(item,index)"></Icon> -->
@@ -23,20 +36,20 @@
 							<img :src="getFullPath(img.url)" alt="" />
 						</div>
 					</div>
-          <div class="img-list" v-if="item.fileType === 'video'">
+					<div class="img-list" v-if="item.fileType === 'video'">
 						<div class="img-item" v-for="(img, imgIndex) in item.attachments" :key="imgIndex">
-							<video :src="getFullPath(img.url)" style="width: 300px;" controls="controls"></video>
+							<video :src="getFullPath(img.url)" style="width: 300px" controls="controls"></video>
 						</div>
 					</div>
-          <div class="file-list" v-if="item.fileType === 'doc'">
+					<div class="file-list" v-if="item.fileType === 'doc'">
 						<div class="file-item" v-for="(item, imgIndex) in item.attachments" :key="imgIndex" @click="onPreview(item)">
-              <img src="../../assets/source/word.png" v-if="item.type === 'doc' && docType.includes(getSuffix(item.blob))" />
-              <img src="../../assets/source/excel.png" v-else-if="item.type === 'doc' && excelType.includes(getSuffix(item.name))" />
-              <img src="../../assets/source/ppt.png" v-else-if="item.type === 'doc' && pptType.includes(getSuffix(item.name))" />
-              <img src="../../assets/source/pdf.png" v-else-if="item.type === 'doc' && getSuffix(item.name) === 'pdf'" />
-              <img src="../../assets/source/zip.png" v-else-if="getSuffix(item.name) === 'zip' || getSuffix(item.name) === 'rar'" />
-              <img src="../../assets/source/audio.png" v-else-if="item.type === 'audio'" />
-              <img src="../../assets/source/unknow.png" v-else />
+							<img src="../../assets/source/word.png" v-if="item.type === 'doc' && docType.includes(getSuffix(item.blob))" />
+							<img src="../../assets/source/excel.png" v-else-if="item.type === 'doc' && excelType.includes(getSuffix(item.name))" />
+							<img src="../../assets/source/ppt.png" v-else-if="item.type === 'doc' && pptType.includes(getSuffix(item.name))" />
+							<img src="../../assets/source/pdf.png" v-else-if="item.type === 'doc' && getSuffix(item.name) === 'pdf'" />
+							<img src="../../assets/source/zip.png" v-else-if="getSuffix(item.name) === 'zip' || getSuffix(item.name) === 'rar'" />
+							<img src="../../assets/source/audio.png" v-else-if="item.type === 'audio'" />
+							<img src="../../assets/source/unknow.png" v-else />
 							<p>{{ item.name }}</p>
 						</div>
 					</div>
@@ -49,15 +62,15 @@
 					<Input v-model="curElegantItem.title" :placeholder="`请输入活动名称...`"></Input>
 				</FormItem>
 				<FormItem :label="`参与对象`">
-          <Cascader :data="csOptions" v-model="curElegantItem.classes" :placeholder="`请选择活动参与班级...`" @on-change="onTargetChange"/>
+					<Cascader :data="csOptions" v-model="curElegantItem.classes" :placeholder="`请选择活动参与班级...`" @on-change="onTargetChange" />
 				</FormItem>
 				<FormItem :label="`活动描述`">
 					<Input v-model="curElegantItem.content" :placeholder="`请输入活动描述内容...`"></Input>
 				</FormItem>
 				<FormItem :label="`素材类别`">
-					<Cascader :data="bizTypeData" v-model="curElegantItem.bizType" @on-change="onTypeChange"/>
+					<Cascader :data="bizTypeData" v-model="curElegantItem.bizType" @on-change="onTypeChange" />
 				</FormItem>
-        <FormItem :label="`自定义类型`" v-if="isCustomType">
+				<FormItem :label="`自定义类型`" v-if="isCustomType">
 					<Input v-model="customType" :placeholder="`请输入自定义艺术活动类型`"></Input>
 				</FormItem>
 				<FormItem :label="`附件类型`">
@@ -110,17 +123,17 @@
 						</div>
 					</div>
 					<p style="margin-top: 10px; color: red">上传须知:</p>
-					<p style="color: red">1. 活动素材需为后缀名为mp4格式的图片文件,单个视频大小不得超出100M</p>
+					<p style="color: red">1. 活动素材需为后缀名为mp4格式的视频文件,单个视频大小不得超出100M</p>
 					<p style="color: red">2. 活动素材视频数量最多为1个</p>
 				</FormItem>
-        <FormItem :label="`活动素材`" v-if="fileType === 'doc'">
-					<Upload multiple type="drag" action="" :before-upload="onBeforeUpload" :show-upload-list="false"  :format="['mp3','ppt','pptx','doc','docx','xls','xlsx','pdf','zip','rar']" :on-format-error="handleDocFormatError">
+				<FormItem :label="`活动素材`" v-if="fileType === 'doc'">
+					<Upload multiple type="drag" action="" :before-upload="onBeforeUpload" :show-upload-list="false" :format="['mp3', 'ppt', 'pptx', 'doc', 'docx', 'xls', 'xlsx', 'pdf', 'zip', 'rar']" :on-format-error="handleDocFormatError">
 						<div style="padding: 40px 0">
 							<Icon type="ios-cloud-upload" size="52" style="color: #3399ff"></Icon>
 							<p>拖拽或者点击上传文档类素材</p>
 						</div>
 					</Upload>
-					<div class="img-list" v-if="fileType === 'doc'" style="display:flex;flex-direction:column">
+					<div class="img-list" v-if="fileType === 'doc'" style="display: flex; flex-direction: column">
 						<div v-for="(imgFile, imgFileIndex) in docFilePreviewList" :key="imgFileIndex" style="margin: 10px 0">
 							<p>
 								<span>{{ imgFile.name }}</span>
@@ -140,17 +153,22 @@
 
 <script>
 	import BlobTool from "@/utils/blobTool.js";
+	import excel from "@/utils/excel.js";
 	export default {
 		data() {
 			return {
+				isLoading:false,
+				allList:[],
+				typeList:['全部风采','德育风采','艺术特色'],
+				filterTypeIndex:0,
 				props: {
 					multiple: false,
 					value: "id",
 					label: "name"
 				},
-        docType: ['doc', 'docx'],
-        excelType: ['xls', 'csv', 'xlsx'],
-        pptType: ['ppt', 'pptx'],
+				docType: ["doc", "docx"],
+				excelType: ["xls", "csv", "xlsx"],
+				pptType: ["ppt", "pptx"],
 				elegantList: [],
 				curElegantItem: null,
 				curElegantIndex: 0,
@@ -159,13 +177,14 @@
 				btnLoading: false,
 				fileArr: [],
 				priviewSrc: "",
+				originList:[],
 				imgFilePreviewList: [],
 				videoFilePreviewList: [],
-        docFilePreviewList:[],
+				docFilePreviewList: [],
 				containerClient: null,
 				schoolProfile: null,
 				fileType: "image",
-        customType:'',
+				customType: "",
 				bizTypeData: [
 					{
 						value: "1",
@@ -235,39 +254,70 @@
 					etime: null //可选
 				})
 				.then((res) => {
-					this.elegantList = res.elegants.reverse();
-          this.getTargetList()
+					this.originList = res.elegants.reverse()
+					this.onFilterTypeChange();
+					this.getTargetList();
 				});
 		},
 		methods: {
-      onPreview(file){
-        let fullLink = this.getFullPath(file.url)
-        let fileSuffix = this.getSuffix(file.name)
-        let allDocTypes = [...this.docType,...this.excelType,...this.pptType]
-        if (fileSuffix === 'pdf') {
-          this.openPdf(fullLink, file.name)
-        } else if(allDocTypes.includes(fileSuffix)) {
-          this.openDoc(escape(fullLink))
-        }else{
-          this.$tools.doDownloadByUrl(fullLink, file.name)
-        }
-      },
-      /* 打开PDF文件进行预览 */
-    openPdf(url) {
-      window.open('/web/viewer.html?file=' + encodeURIComponent(url));
-    },
-    /* 预览文档 */
-    openDoc(url) {
-      window.open('https://view.officeapps.live.com/op/view.aspx?src=' + url);
-    },
+			onDownloadExcel(){
+				this.isLoading = true
+				let schoolName = this.$store.state.user.schoolProfile.school_base.name;
+				let periodName = this.curPeriod.name;
+				let list = this.originList.map(i => {
+					return {
+						schoolName: schoolName,
+						className: this.getClassNameById(i.classes[0]),
+						classId: i.classes[0],
+						bizType: i.bizType ? i.bizType.join('-') : '-', 
+						title:i.title,
+						content:i.content,
+						time: this.$tools.formatTime(i.createTime)
+					}
+				})
+				const params = {
+					title: ["学校名称", "班级名称", "班级ID", "素材类别", "活动名称", "活动描述", "活动时间"],
+					key: ["schoolName", "className", "classId", "bizType", "title", "content", "time"],
+					data: list,
+					autoWidth: true,
+					filename: schoolName + "-" + periodName + "- 学生风采数据汇总表"
+				};
+				excel.export_array_to_excel(params);
+				this.isLoading = false;
+			},
+			onFilterTypeChange(){
+				console.log(this.allList)
+				this.elegantList = this.filterTypeIndex === 0 ? this._.cloneDeep(this.originList) : this.originList.filter(i => i.bizType && i.bizType.includes(this.typeList[this.filterTypeIndex]))
+			},
+			onPreview(file) {
+				let fullLink = this.getFullPath(file.url);
+				let fileSuffix = this.getSuffix(file.name);
+				let allDocTypes = [...this.docType, ...this.excelType, ...this.pptType];
+				if (fileSuffix === "pdf") {
+					this.openPdf(fullLink, file.name);
+				} else if (allDocTypes.includes(fileSuffix)) {
+					this.openDoc(escape(fullLink));
+				} else {
+					this.$tools.doDownloadByUrl(fullLink, file.name);
+				}
+			},
+			/* 打开PDF文件进行预览 */
+			openPdf(url) {
+				window.open("/web/viewer.html?file=" + encodeURIComponent(url));
+			},
+			/* 预览文档 */
+			openDoc(url) {
+				window.open("https://view.officeapps.live.com/op/view.aspx?src=" + url);
+			},
 			onTargetChange(data) {
-        this.curElegantItem.classes = [data.at(-1)]
+				this.curElegantItem.classes = [data.at(-1)];
 			},
-      onTypeChange(data,origin) {
-        this.curElegantItem.bizType = origin.map(i => i.label)
+			onTypeChange(data, origin) {
+				this.curElegantItem.bizType = origin.map((i) => i.label);
 			},
 			getTargetList() {
 				if (!this.curPeriod?.id) return;
+				this.isLoading = true
 				let params = {
 					tmdid: this.$store.state.userInfo.TEAMModelId,
 					schoolId: this.$store.state.userInfo.schoolCode,
@@ -277,18 +327,20 @@
 				this.$api.common.getActivityTarget(params).then(
 					(res) => {
 						this.allList = res.groupLists;
+						this.isLoading = false
 					},
 					(err) => {
 						this.$Messag.error(this.$t("ae.ae15"));
+						this.isLoading = false
 					}
 				);
 			},
 			onAddElegant() {
 				this.editModal = true;
 				this.fileType = "image";
-        this.videoFilePreviewList = []
-        this.docFilePreviewList = []
-        this.imgFilePreviewList = []
+				this.videoFilePreviewList = [];
+				this.docFilePreviewList = [];
+				this.imgFilePreviewList = [];
 				this.curElegantItem = {
 					title: "",
 					content: "",
@@ -296,15 +348,15 @@
 					bizCode: "elegant",
 					target: "",
 					attachments: [],
-          classes:[],
-          bizType:[]
+					classes: [],
+					bizType: []
 				};
 			},
 			onDrawerChange() {
 				this.fileType = "image";
-        this.videoFilePreviewList = []
-        this.docFilePreviewList = []
-        this.imgFilePreviewList = []
+				this.videoFilePreviewList = [];
+				this.docFilePreviewList = [];
+				this.imgFilePreviewList = [];
 				this.curElegantItem = {
 					title: "",
 					content: "",
@@ -312,8 +364,8 @@
 					target: "",
 					createTime: 0,
 					attachments: [],
-          classes:[],
-          bizType:[]
+					classes: [],
+					bizType: []
 				};
 			},
 			onImgClick(imgList, index) {
@@ -324,8 +376,8 @@
 					imgList: imgList.map((i) => this.getFullPath(i.url))
 				});
 			},
-      onVideoClick(file) {
-        let full = this.getFullPath(file.url)
+			onVideoClick(file) {
+				let full = this.getFullPath(file.url);
 				console.log(full);
 			},
 			onEditElegant(item, index) {},
@@ -371,12 +423,12 @@
 					desc: file.name + " 文件格式错误"
 				});
 			},
-      handleDocFormatError(file) {
+			handleDocFormatError(file) {
 				this.$Notice.warning({
 					title: "上传素材文件格式错误",
 					desc: file.name + " 文件格式错误"
 				});
-        this.docFilePreviewList.pop()
+				this.docFilePreviewList.pop();
 			},
 			onBeforeUpload(file) {
 				if (this.fileType === "image") {
@@ -389,7 +441,7 @@
 						return;
 					}
 					this.imgFilePreviewList.push(file);
-				} else if(this.fileType === 'video') {
+				} else if (this.fileType === "video") {
 					if (this.videoFilePreviewList.length) {
 						this.$Message.warning("最多允许上传1个视频素材!");
 						return;
@@ -399,8 +451,8 @@
 						return;
 					}
 					this.videoFilePreviewList.push(file);
-				}else{
-          if (this.docFilePreviewList.length > 5) {
+				} else {
+					if (this.docFilePreviewList.length > 5) {
 						this.$Message.warning("最多允许上传5个素材!");
 						return;
 					}
@@ -409,16 +461,16 @@
 						return;
 					}
 					this.docFilePreviewList.push(file);
-        }
+				}
 			},
 			onRemoveFile(fileIndex) {
 				if (this.fileType === "image") {
 					this.imgFilePreviewList.splice(fileIndex, 1);
 				} else if (this.fileType === "video") {
 					this.videoFilePreviewList = [];
-				} else{
-          this.docFilePreviewList.splice(fileIndex, 1);
-        }
+				} else {
+					this.docFilePreviewList.splice(fileIndex, 1);
+				}
 			},
 			/* 保存上传 */
 			async onConfirm() {
@@ -427,7 +479,7 @@
 					this.$Message.warning("请填写完整!");
 					return;
 				}
-				if ((this.fileType === "image" && !this.imgFilePreviewList.length) || (this.fileType === "video" && !this.videoFilePreviewList.length)  || (this.fileType === "doc" && !this.docFilePreviewList.length)) {
+				if ((this.fileType === "image" && !this.imgFilePreviewList.length) || (this.fileType === "video" && !this.videoFilePreviewList.length) || (this.fileType === "doc" && !this.docFilePreviewList.length)) {
 					this.$Message.warning("素材附件不可为空!");
 					return;
 				}
@@ -437,10 +489,10 @@
 				this.btnLoading = true;
 				this.containerClient = new BlobTool(n.url, n.name, n.sas, "school");
 				try {
-          if(this.isCustomType){
-            params.bizType[2] = this.customType
-          }
-          let files = this.fileType === "image" ? this.imgFilePreviewList : this.fileType === "video" ? this.videoFilePreviewList : this.docFilePreviewList
+					if (this.isCustomType) {
+						params.bizType[2] = this.customType;
+					}
+					let files = this.fileType === "image" ? this.imgFilePreviewList : this.fileType === "video" ? this.videoFilePreviewList : this.docFilePreviewList;
 					let uploadResult = await this.uploadFiles(files, elegantId);
 					params.attachments = uploadResult;
 					params.id = elegantId;
@@ -483,14 +535,19 @@
 			}
 		},
 		computed: {
-      getSuffix() {
-      return name => {
-        return name.substr(name.lastIndexOf(".") + 1)
-      }
-    },
-      isCustomType(){
-        return this.curElegantItem.bizType.at(-1) === '自定义'
-      },
+			getClassNameById(){
+				return id => {
+					return this.allList.length ? this.allList.find(i => i.id === id)?.name : ''
+				}
+			},
+			getSuffix() {
+				return (name) => {
+					return name.substr(name.lastIndexOf(".") + 1);
+				};
+			},
+			isCustomType() {
+				return this.curElegantItem.bizType.at(-1) === "自定义";
+			},
 			/* 当前学段信息 */
 			curPeriod() {
 				return this.$store.state.user.curPeriod;
@@ -529,12 +586,12 @@
 						let child = this.allList.filter((classItem) => {
 							return classItem.year == curYear - index && classItem.type == "class";
 						});
-            if(child.length){
-              child.forEach(i => {
-                i.value = i.id
-                i.label = i.name
-              })
-            }
+						if (child.length) {
+							child.forEach((i) => {
+								i.value = i.id;
+								i.label = i.name;
+							});
+						}
 						dataItem.children = child.length ? child : undefined;
 						if (child.length) data.push(dataItem);
 					});
@@ -553,7 +610,15 @@
 					return url + "?" + this.schoolProfile.blob_sas;
 				};
 			}
-		}
+		},
+		watch: {
+			"$store.state.user.curSemester": {
+				deep: true,
+				handler(n, old) {
+          			this.getTargetList()
+				}
+			}
+		},	
 	};
 </script>
 
@@ -574,9 +639,9 @@
 			}
 		}
 
-    /deep/ .el-input__inner{
-      padding:  0 10px !important;
-    }
+		/deep/ .el-input__inner {
+			padding: 0 10px !important;
+		}
 
 		.elegant-list {
 			width: 100%;
@@ -586,7 +651,6 @@
 			align-items: center;
 			padding: 15px;
 			overflow: auto;
-      
 
 			.ivu-timeline {
 				height: 100%;
@@ -615,21 +679,25 @@
 				color: #817f7f;
 			}
 		}
-    .file-list{
-      display: flex;
-      flex-direction: column;
-      .file-item{
-        display: flex;
-        align-items: center;
-        color: #2d83ca;
-        margin:5px 0;
-        cursor: pointer;
-        img{
-          width: 20px;
-          margin-right: 10px;
-        }
-      }
-    }
+
+		.ivu-timeline-item{
+			padding-bottom: 40px !important;
+		}
+		.file-list {
+			display: flex;
+			flex-direction: column;
+			.file-item {
+				display: flex;
+				align-items: center;
+				color: #2d83ca;
+				margin: 5px 0;
+				cursor: pointer;
+				img {
+					width: 20px;
+					margin-right: 10px;
+				}
+			}
+		}
 		.img-list {
 			display: flex;
 			flex-wrap: wrap;

+ 1 - 1
TEAMModelOS/ClientApp/src/view/learnactivity/echarts/ScoreMatrix.vue

@@ -47,7 +47,7 @@ export default {
                             formatter: '{b} : {c}' + this.$t('unit.text7')
                         },
                         title: {
-                            "text": `${this.isTotal ? this.$t('learnActivity.simple.total'):''}${this.$t('learnActivity.simple.scoreMat')}`,
+                            "text": `${this.isTotal ? this.$t('learnActivity.simple.total'):''} ${this.$t('learnActivity.simple.scoreMat')}`,
                             "left": "center",
                             "top": 0,
                             "textStyle": {

+ 37 - 1
TEAMModelOS/ClientApp/src/view/mycourse/MyCourse.vue

@@ -547,7 +547,16 @@
 				}
 				let data = this.courseList.filter((item) => item.scope == this.listType);
 				if (data.length) {
-					this.courseId = data[0].id;
+					if(this.$route.params.courseId) {
+						let isCourse = data.find(item => {
+							return item.id === this.$route.params.courseId
+						})
+						if(isCourse) {
+							this.courseId = isCourse.id
+						}
+					} else {
+						this.courseId = data[0].id;
+					}
 				}
 				if (data.length) {
 					return data;
@@ -567,6 +576,14 @@
 			// this.getCourseList()
 			// this.BlobContainer()
 		},
+		beforeRouteLeave(to, from, next) {
+			if ((to.name === "classRecord" || to.name === 'evDetail' || to.name === 'manageHomeWork' || to.name === 'personalVote' || to.name === 'personalSurvey') && from.name === 'course') {
+				from.meta.isKeep = true;
+			} else {
+				from.meta.isKeep = false;
+			}
+			next();
+		},
 		beforeRouteEnter(to, from, next) {
 			next((vm) => {
 				vm.BlobContainer();
@@ -1321,6 +1338,16 @@
 							return info
 							// this.courseList.push(info)
 						})
+						if(this.$route.params.courseId) {
+							let isCourse = this.courseList.find(item => {
+								return item.id === this.$route.params.courseId
+							})
+							if(!isCourse) {
+								this.$Message.warning('未找到对应课程!')
+							} else {
+								this.listType = isCourse.scope
+							}
+						}
 					}
 				}).finally(() => {
 					this.listLoading = false
@@ -1387,6 +1414,15 @@
 									item.stulist = item.stulist || undefined; */
 									this.teaClassList.push(item)
 								});
+								if(this.$route.params.classId) {
+									let isClass = this.teaClassList.findIndex(item => {
+										return item.groupId === this.$route.params.classId
+									})
+									if(isClass != -1) {
+										this.selectClass(isClass)
+										this.tabName = "record"
+									}
+								}
 							} else {
 								this.$Message.error(this.$t("cusMgt.getListErr"));
 							}

+ 8 - 2
TEAMModelOS/ClientApp/src/view/mycourse/record/Record.less

@@ -20,6 +20,10 @@
         background: var(--active-item-start);
     }
 }
+
+.route-check {
+    background: var(--active-item-start);
+}
 .record-poster-wrap{
     width: 120px;
     height: 65px;
@@ -75,7 +79,9 @@
     position: relative;    
 }
 .exam-action-wrap{
-    position: absolute;
+    /* position: absolute;
     right: 15px;
-    top: 10px;
+    top: 10px; */
+    text-align: right;
+    margin: 5px 10px;
 }

+ 11 - 4
TEAMModelOS/ClientApp/src/view/mycourse/record/Record.vue

@@ -1,5 +1,8 @@
 <template>
   <div class="record-container">
+      <Alert v-show="rcdParams.scope == 'private'" show-icon type="warning" closable>
+        {{$t('cusMgt.recordTips')}}
+      </Alert>
     <div class="exam-action-wrap">
       <Checkbox v-model="onlyMe" style="margin-right:35px;user-select:none" @on-change="getRecordList(filterExpire)">
         {{ $t('cusMgt.filterClass') }}
@@ -16,10 +19,7 @@
       <i-switch :loading="sLoading" v-model="isAuto" size="small" @on-change="setAutoPublish" />
     </div>
     <vuescroll>
-      <Alert v-show="rcdParams.scope == 'private'" show-icon type="warning" closable>
-        {{$t('cusMgt.recordTips')}}
-      </Alert>
-      <div class="rcd-item" v-for="(item,index) in recordList" :key="index" @click="toClassRecoerd(index)">
+      <div :class="['rcd-item', isRouteParams === index ? 'route-check' : '']" v-for="(item,index) in recordList" :key="index" @click="toClassRecoerd(index)">
         <RcdPoster class="record-poster-wrap" :poster="item.poster"></RcdPoster>
         <div style="flex:1">
           <p class="record-name" style="padding-left:10px">
@@ -200,6 +200,7 @@ export default {
       recordList: [],
       editRdStatus: false,
       onlyMe: false,
+			isRouteParams: -1, //判断当前课堂记录是否为通知所找
     }
   },
   computed: {
@@ -533,6 +534,12 @@ export default {
             this.recordList.sort((a, b) => {
               return a.startTime - b.startTime > 0 ? -1 : 1
             })
+            if(this.$route.params.recordId) {
+              let isRecod = this.recordList.findIndex(item => {
+                return item.id === this.$route.params.recordId
+              })
+              this.isRouteParams = isRecod
+            }
           }
         },
         err => {

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

@@ -242,13 +242,13 @@
                             background-color: rgb(255, 217, 160);
                             padding: 0px 10px;
                             border-radius: 2px;
-                            font-size: 5px;
+                            font-size: 10px;
                         }
                         .type {
                             background-color: rgb(175, 227, 255);
                             padding: 0px 10px;
                             border-radius: 2px;
-                            font-size: 5px;
+                            font-size: 10px;
                             margin-top: 2px;
                         }
                     }

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

@@ -94,7 +94,7 @@
           </div>
         </div>
         <div class="body absolute">
-          <span style="font-size: 6px"> {{ $t("scoreCalc.tickIncludedScore") }} {{
+          <span style="font-size: 10px"> {{ $t("scoreCalc.tickIncludedScore") }} {{
             $t("scoreCalc.dropSort")
           }}</span>
           <div>
@@ -156,7 +156,7 @@
                           {{ $t("scoreCalc.logStudentScore") }}
                         </Button>
                       </Col>
-                      <Col flex="auto" class="center" style="font-size: 8px;">
+                      <Col flex="auto" class="center" style="font-size: 10px;">
                         {{$jsFn.secondTimeFormat(classData.startTime)}}
                       </Col>
                       <Col flex="20px" class="center">

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

@@ -77,7 +77,7 @@
     }
   }
   .s-text {
-    font-size: 8px;
+    font-size: 10px;
     color: #808695;
   }
 

+ 6 - 6
TEAMModelOS/ClientApp/src/view/syllabus/Syllabus.vue

@@ -215,13 +215,13 @@
     <!--汇入预览-->
     <Modal v-model="importPreviewModal" width="800" class="add-volume-modal">
       <div class="modal-header" slot="header">
-        <span>导入预览</span>
+        <span>{{ $t('syllabus.importTip8') }}</span>
       </div>
-      <p style="color:red;margin: -20px 0 10px 20px">* 温馨提示:请检查您导入的课纲数据是否与以下结构符合,如不符合请调整后重新导入</p>
+      <p style="color:red;margin: -20px 0 10px 20px">* {{ $t('syllabus.importTip9') }}</p>
       <Tree :data="previewTree"></Tree>
       <div slot="footer" align="center">
-        <Button class="btn" size="default" type="default" @click="doConfirmImport('cancel')" :disabled="isImporting">取消导入</Button>
-        <Button class="btn" size="default" type="primary" @click="doConfirmImport('ok')" :loading="isImporting">确认导入</Button>
+        <Button class="btn" size="default" type="default" @click="doConfirmImport('cancel')" :disabled="isImporting">{{ $t('syllabus.importTip10') }}</Button>
+        <Button class="btn" size="default" type="primary" @click="doConfirmImport('ok')" :loading="isImporting">{{ $t('syllabus.importTip11') }}</Button>
       </div>
     </Modal>
     <!--上传文件-->
@@ -577,11 +577,11 @@ export default {
       let maxSize = 10 * 1024 * 1024
       let nameType = file.name.split('.')[file.name.split('.').length - 1]
       if (!['xls', 'xlsx'].includes(nameType.toLowerCase())) {
-        this.$Message.warning(`请上传正确的模板格式!`);
+        this.$Message.warning(this.$t('syllabus.importTip12'));
         return false
       }
       if (file.size > maxSize) {
-        this.$Message.warning(`模板文件不能大于10M!`);
+        this.$Message.warning(this.$t('syllabus.importTip13'));
         return false
       }
       this.isImporting = true

+ 10 - 1
TEAMModelOS/ClientApp/src/view/teachermgmt/components/mgt/TeacherMgt.vue

@@ -1043,8 +1043,16 @@ export default {
     },
     //打开面板
     openPanel(panel, data) {
+      console.log(panel,data,'打开面板')
+      let pattern=panel;let userData={}
       if (this.activePanel) return
-      switch (panel) {
+      if(this.sltTeachers.length ==1){
+        pattern='single'
+        userData=this.sltTeachers[0]
+        this.openSingleAuth(pattern, userData)
+        return
+      }
+      switch (pattern) {
         case 'space':
           this.openSpaceAuth(panel, data)
           break
@@ -1138,6 +1146,7 @@ export default {
       }
     },
     selectTeachers(selections) {
+      console.log(selections,'选择')
       this.sltTeachers = selections
       if (this.setSpaceRule.rule) {
         this.batchSetSpace(this.setSpaceRule, false)

+ 272 - 8
TEAMModelOS/Controllers/Common/ActivityController.cs

@@ -21,6 +21,7 @@ using StackExchange.Redis;
 
 using System.Text.RegularExpressions;
 using Microsoft.AspNetCore.Authorization;
+using OpenXmlPowerTools;
 
 namespace TEAMModelOS.Controllers
 {
@@ -52,6 +53,72 @@ namespace TEAMModelOS.Controllers
             _azureRedis = azureRedis; 
             _coreAPIHttpService = coreAPIHttpService;
         }
+
+
+        /// <summary>
+        /// 添加活动参与对象,学校,教师
+        /// </summary>
+        /// <param name="request"></param>
+        /// <returns></returns>
+        [ProducesDefaultResponseType]
+        [AuthToken(Roles = "admin,area")]
+        [HttpPost("invite-target")]
+        [Authorize(Roles = "IES")]
+
+        public async Task<IActionResult> InviteTarget (JsonElement request) 
+        {
+            (string tmdid, _, _, string school) = HttpContext.GetAuthTokenInfo();
+            if (!request.TryGetProperty("grant_type", out JsonElement grant_type)) return BadRequest();
+            switch (true)
+            {
+                case bool when $"{grant_type}".Equals("schools", StringComparison.OrdinalIgnoreCase):
+                    {
+                        
+                        if (!request.TryGetProperty("scope", out JsonElement _scope)) return BadRequest();
+                        string sql = string.Empty;
+                        if (_scope.GetString().Equals("public", StringComparison.OrdinalIgnoreCase))
+                        {
+                            sql = "select c.id,c.name ,c.picture,c.region,c.province,c.city,c.areaId   from c where c.code='Base' ";
+                        }
+                        else {
+                            if (!request.TryGetProperty("areaId", out JsonElement _areaId)) return BadRequest();
+                            sql = $"select c.id,c.name ,c.picture,c.region,c.province,c.city,c.areaId   from c where c.code='Base' and c.areaId='{_areaId}' ";
+                        }
+                        var result = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.School).GetList<School>(sql, "Base");
+                        var sc= result.list.FindAll(z => !string.IsNullOrWhiteSpace(z.areaId));
+                        if (sc.IsNotEmpty()) {
+                            string areaSql = $"select value c from c where c.id in ({string.Join(",",sc.Select(z=>$"'{z.areaId}'").ToHashSet())})";
+                            var areaResult =  await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Normal).GetList<Area>(sql, "Base-Area");
+                            if (areaResult.list.IsNotEmpty()) {
+                                List<dynamic> schools= new List<dynamic>();
+                                foreach (var item in result.list) {
+                                    if (!string.IsNullOrWhiteSpace(item.areaId))
+                                    {
+                                        var area = areaResult.list.Find(z => z.id.Equals(item.areaId));
+                                        schools.Add(new { item.id, item.name, item.picture, item.region, item.province, item.city, item.areaId, areaName = area?.name });
+                                    }
+                                    else {
+                                        schools.Add(new { item.id, item.name, item.picture, item.region, item.province, item.city, item.areaId, areaName = string.Empty });
+                                    }
+                                }
+                            }
+                        }
+                        return Ok(new {code=200, schools = result.list.Select(z=>new { z.id,z.name,z.picture,z.region,z.province,z.city,z.areaId, areaName = string.Empty }) });
+                    }
+                case bool when $"{grant_type}".Equals("teachers", StringComparison.OrdinalIgnoreCase):
+                    {
+                        if (!string.IsNullOrWhiteSpace(school)) {
+                            School schoolbase = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.School).ReadItemAsync<School>(school, new PartitionKey("Base"));
+                            string  sql = $"select c.id,c.name ,c.picture   from c where c.code='Teacher-{school}' ";
+                            var result = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.School).GetList<SchoolTeacher>(sql, $"Teacher-{school}");
+                            return Ok(new { code = 200, teachers  = result.list.Select(z=>new { z.id,z.name,z.picture,school,schooName = schoolbase.name}) });
+                        }
+                        break;
+                    }
+            }
+            return Ok(new { code = 400 });
+        }
+
         /// <summary>
         /// 活动创建
         /// </summary>
@@ -75,17 +142,15 @@ namespace TEAMModelOS.Controllers
                     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";
-
                             //如果是区级活动,enroll报名制,则学校的确认状态默认为1 。
-                            if (activity.scope.Equals("area", StringComparison.OrdinalIgnoreCase) && activity.joinMode.Equals("enroll", StringComparison.OrdinalIgnoreCase)) 
-                            {
-                                activity.schools.ForEach(z => z.status=1);
-                            }
+                            //if (activity.scope.Equals("area", StringComparison.OrdinalIgnoreCase) && activity.joinMode.Equals("enroll", StringComparison.OrdinalIgnoreCase)) 
+                            //{
+                            //    activity.schools.ForEach(z => z.status=1);
+                            //}
                             //醍摩豆智慧学区
                             if (!activity.owner.Equals("02944f32-f534-3397-ea56-e6f1fc6c3714", StringComparison.OrdinalIgnoreCase)  && activity.scope.Equals("public", StringComparison.OrdinalIgnoreCase))
                             {
@@ -115,6 +180,20 @@ namespace TEAMModelOS.Controllers
                                                     ValidResult validResultContest = contest.Valid();
                                                     if (validResultContest.isVaild)
                                                     {
+
+                                                        if (contest.modules.Contains("review")) {
+                                                            if (!request.TryGetProperty("reviewConfig", out JsonElement _reviewConfig))
+                                                            {
+                                                                return Ok(new { error = ResponseCode._400ParamsError, msg = "评审未配置" });
+                                                            }
+                                                            if (contest.review== null) {
+                                                                return Ok(new { error = ResponseCode._400ParamsError, msg = "评审未配置" });
+                                                            }
+                                                            ReviewRuleTree ruleTree = _reviewConfig.ToObject<ReviewRuleTree>();
+                                                            var reviewRule =  await ActivityService.UpsertReviewRule(ruleTree, activity, _azureCosmos);
+                                                            contest.review.ruleId = reviewRule.id;
+                                                            contest.review.ruleName = reviewRule.name;
+                                                        }
                                                         await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Common).UpsertItemAsync(contest, new PartitionKey(contest.code));
                                                     }
                                                     else {
@@ -147,21 +226,206 @@ namespace TEAMModelOS.Controllers
                     case bool when $"{grant_type}".Equals("list-area", StringComparison.OrdinalIgnoreCase):
                         {
                             if (!request.TryGetProperty("areaId", out JsonElement _areaId)) return BadRequest();
-                            string sql = $"select value c from c where c.scope='area' and c.owner='{_areaId}'  ";
+                            string yearSql = $" and c.year={DateTimeOffset.Now.Year}";
+                            
+                            if (request.TryGetProperty("year", out JsonElement _year)) {
+                                yearSql = $" and c.year={_year}";
+                            }
+                            string sql = $"select value c from c where c.scope='area' and c.owner='{_areaId}' {yearSql}  ";
                             //醍摩豆智慧学区
                             if (_areaId.GetString().Equals("02944f32-f534-3397-ea56-e6f1fc6c3714", StringComparison.OrdinalIgnoreCase)) {
-                                sql = $"select value c from c where   c.owner='{_areaId}' and ( c.scope='area' or c.scope='public' ) ";
+                                sql = $"select value c from c where   c.owner='{_areaId}' {yearSql } and ( c.scope='area' or c.scope='public' ) ";
                             }
                             var  result =  await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Common).GetList<Activity>(sql, "Activity");
                             return Ok(new { activities = result.list.OrderByDescending(z=>z.stime) });
                         }
                     case bool when $"{grant_type}".Equals("list-school", StringComparison.OrdinalIgnoreCase):
                         {
+                            if (!string.IsNullOrWhiteSpace(school)) {
+                                School schoolbase = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.School).ReadItemAsync<School>(school, new PartitionKey("Base"));
+                                List<Activity> activities = new List<Activity>();   
+                                string yearSql = $" and c.year={DateTimeOffset.Now.Year}";
+
+                                if (request.TryGetProperty("year", out JsonElement _year))
+                                {
+                                    yearSql = $" and c.year={_year}";
+                                }
+                                //获取开放的
+                                {
+
+                                    //完全开放 所有的学校
+                                    string sqlOpen = $"select value c from c where c.scope='public'{yearSql}  and (c.publish=1 or c.publish=2 )  and( ARRAY_LENGTH(c.invitedSchools)=0 or IS_DEFINED(c.invitedSchools) = false  ) ";
+                                    var resultOpen= await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Common).GetList<Activity>(sqlOpen, "Activity");
+                                    activities.AddRange(resultOpen.list);
+                                    //部分学校
+                                    string sqlSchool = $"select value c from c  join s in c.invitedSchools  where c.scope='public'{yearSql}  and (c.publish=1 or c.publish=2 )  and s.id='{school}' ";
+                                    var resultSchool = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Common).GetList<Activity>(sqlSchool, "Activity");
+                                    resultSchool.list.ForEach(z => {
+                                        var confirmedSchool = z.confirmedSchools.Find(z => z.id.Equals(school));
+                                        if (confirmedSchool==null)
+                                        {
+                                            z.confirmedSchools.Add(new ActivityConfirmedSchool
+                                            {
+                                                id=schoolbase.id,
+                                                name=schoolbase.name,
+                                                picture=schoolbase.picture,
+                                                status=0
+                                            });
+                                        }
+                                    });
+                                    activities.AddRange(resultSchool.list);
+                                }
+                                //获取区级下放的
+                                {
+                                    
+                                    if (!string.IsNullOrWhiteSpace(schoolbase.areaId)) {
+                                        //区级所有学校
+                                        string sqlOpen = $"select value c from c where c.scope='area'{yearSql}   and (c.publish=1 or c.publish=2 )  and  c.owner='{schoolbase.areaId}'  and( ARRAY_LENGTH(c.invitedSchools)=0 or IS_DEFINED(c.invitedSchools) = false) ";
+                                        var resultOpen = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Common).GetList<Activity>(sqlOpen, "Activity");
+                                        resultOpen.list.ForEach(z => {
+                                            var confirmedSchool = z.confirmedSchools.Find(z => z.id.Equals(school));
+                                            if (confirmedSchool==null)
+                                            {
+                                                z.confirmedSchools.Add(new ActivityConfirmedSchool
+                                                {
+                                                    id=schoolbase.id,
+                                                    name=schoolbase.name,
+                                                    picture=schoolbase.picture,
+                                                    status=0
+                                                });
+                                            }
+                                        });
+                                        activities.AddRange(resultOpen.list);
+                                        //区级部分学校
+                                        string sqlSchool = $"select value c from c  join s in c.invitedSchools  where c.scope='area'{yearSql}  and (c.publish=1 or c.publish=2 )  and  c.owner='{schoolbase.areaId}' and s.id='{school}' ";
+                                        var resultSchool= await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Common).GetList<Activity>(sqlSchool, "Activity");
+                                        resultSchool.list.ForEach(z => {
+                                            var confirmedSchool = z.confirmedSchools.Find(z => z.id.Equals(school));
+                                            if (confirmedSchool==null)
+                                            {
+                                                z.confirmedSchools.Add(new ActivityConfirmedSchool
+                                                {
+                                                    id=schoolbase.id,
+                                                    name=schoolbase.name,
+                                                    picture=schoolbase.picture,
+                                                    status=0
+                                                });
+                                            }
+                                        });
+                                        activities.AddRange(resultSchool.list);
+                                    }
+                                }
+                                //获取学校自己的
+                                {
+                                    string sqlSchool = $"select value c from c where c.scope='school'{yearSql} and  c.owner='{school}'  ";
+                                    var resultSchool = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Common).GetList<Activity>(sqlSchool, "Activity");
+                                    activities.AddRange(resultSchool.list);
+                                } 
+                                return Ok(new { activities = activities.OrderByDescending(z => z.stime) });
+                            }
                             break;
+
                         }
                  
                     case bool when $"{grant_type}".Equals("list-teacher", StringComparison.OrdinalIgnoreCase):
                         {
+                            List<Activity> activities = new List<Activity>();
+                            string yearSql = $" and c.year={DateTimeOffset.Now.Year}";
+
+                            if (request.TryGetProperty("year", out JsonElement _year))
+                            {
+                                yearSql = $" and c.year={_year}";
+                            }
+                            if (!string.IsNullOrWhiteSpace(school)) {
+                                School schoolbase = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.School).ReadItemAsync<School>(school, new PartitionKey("Base"));
+                                //获取区级下放的
+                                {
+                                  
+                                    if (!string.IsNullOrWhiteSpace(schoolbase.areaId))
+                                    {
+                                        //区级所有学校
+                                        string sqlOpen = $"select value c from c join s in c.confirmedSchools where c.scope='area'{yearSql} and (c.publish=1 or c.publish=2 )  and  c.owner='{schoolbase.areaId}'  and( ARRAY_LENGTH(c.invitedSchools)=0 or IS_DEFINED(c.invitedSchools) = false) and s.id='{school}' and s.status=1 ";
+                                        var resultOpen = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Common).GetList<Activity>(sqlOpen, "Activity");
+                                        resultOpen.list.ForEach(z => {
+                                            //处理是否是邀请制 
+                                            if (z.joinMode.Equals("invite")) 
+                                            {
+                                                var inviteTeacher = z.inviteTeachers.Find(t => t.id.Equals(tmdid));
+                                                if (inviteTeacher!=null) {
+                                                    activities.Add(z);
+                                                }
+                                            }
+                                            else
+                                            {
+                                                activities.Add(z);
+                                            }
+                                        });
+                                        //区级部分学校
+                                        string sqlSchool = $"select value c from c  join i in c.invitedSchools join s in c.confirmedSchools  where c.scope='area'{yearSql} and (c.publish=1 or c.publish=2 )  and  c.owner='{schoolbase.areaId}' and i.id='{school}'    and s.id='{school}' and s.status=1 ";
+                                        var resultSchool = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Common).GetList<Activity>(sqlSchool, "Activity");
+                                        resultSchool.list.ForEach(z => {
+                                            //处理是否是邀请制 
+                                            if (z.joinMode.Equals("invite"))
+                                            {
+                                                var inviteTeacher = z.inviteTeachers.Find(t => t.id.Equals(tmdid));
+                                                if (inviteTeacher!=null)
+                                                {
+                                                    activities.Add(z);
+                                                }
+                                            }
+                                            else
+                                            {
+                                                activities.Add(z);
+                                            }
+                                        });
+                                    }
+                                }
+                                //获取学校自己的
+                                {
+                                    string sqlSchool = $"select value c from c where c.scope='school'{yearSql} and (c.publish=1 or c.publish=2 )  and  c.owner='{school}'  ";
+                                    var resultSchool = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Common).GetList<Activity>(sqlSchool, "Activity");
+
+                                    resultSchool.list.ForEach(z => {
+                                        //处理是否是邀请制 
+                                        if (z.joinMode.Equals("invite"))
+                                        {
+                                            var inviteTeacher = z.inviteTeachers.Find(t => t.id.Equals(tmdid));
+                                            if (inviteTeacher!=null)
+                                            {
+                                                activities.Add(z);
+                                            }
+                                        }
+                                        else {
+                                            activities.Add(z);
+                                        }
+                                    });
+                                }
+                            }
+                            //获取开放的
+                            {
+                                //完全开放 所有的学校
+                                string sqlOpen = $"select value c from c where c.scope='public'{yearSql} and (c.publish=1 or c.publish=2 )  and( ARRAY_LENGTH(c.invitedSchools)=0 or IS_DEFINED(c.invitedSchools) = false  ) ";
+                                var resultOpen = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Common).GetList<Activity>(sqlOpen, "Activity");
+                                activities.AddRange(resultOpen.list);
+                                //部分学校
+                                string sqlSchool = $"select value c from c  join i in c.invitedSchools join s in c.confirmedSchools  where c.scope='public'{yearSql} and (c.publish=1 or c.publish=2 )  and i.id='{school}'    and s.id='{school}' and s.status=1  ";
+                                var resultSchool = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Common).GetList<Activity>(sqlSchool, "Activity");
+                                resultSchool.list.ForEach(z => {
+                                    //处理是否是邀请制 
+                                    if (z.joinMode.Equals("invite"))
+                                    {
+                                        var inviteTeacher = z.inviteTeachers.Find(t => t.id.Equals(tmdid));
+                                        if (inviteTeacher!=null)
+                                        {
+                                            activities.Add(z);
+                                        }
+                                    }
+                                    else
+                                    {
+                                        activities.Add(z);
+                                    }
+                                });
+                            }
                             break;
                         }
                     case bool when $"{grant_type}".Equals("list-portal", StringComparison.OrdinalIgnoreCase):

+ 1 - 1
TEAMModelOS/Controllers/Common/CommonController.cs

@@ -173,7 +173,7 @@ namespace TEAMModelOS.Controllers.Common
                 }
                 await client.GetContainer(Constant.TEAMModelOS, "Common").ReplaceItemAsync<Dictionary<string, object>>(dy, dy["id"].ToString(), new Azure.Cosmos.PartitionKey(dy["code"].ToString()));
                 var content = new { id = $"{id}", code = $"{code}" };
-                await ActivityService.RefreshStuActivity(_coreAPIHttpService, client, _dingDing, $"{id}", $"{code}");
+                await IESActivityService.RefreshStuActivity(_coreAPIHttpService, client, _dingDing, $"{id}", $"{code}");
                 return Ok(new { code=200, isScore });
             }
             catch (Exception ex)