Ver código fonte

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

zhouj1203@hotmail.com 1 ano atrás
pai
commit
b4636f71a7
43 arquivos alterados com 2560 adições e 1227 exclusões
  1. BIN
      TEAMModelBI/ClientApp/src/assets/img/default1.png
  2. BIN
      TEAMModelBI/ClientApp/src/assets/img/default2.png
  3. BIN
      TEAMModelBI/ClientApp/src/assets/img/default3.png
  4. BIN
      TEAMModelBI/ClientApp/src/assets/img/default4.png
  5. 301 38
      TEAMModelBI/ClientApp/src/view/userInquire/details.vue
  6. 9 2
      TEAMModelBI/ClientApp/src/view/userInquire/ies.vue
  7. 18 1
      TEAMModelBI/ClientApp/src/view/userInquire/index.vue
  8. 12 5
      TEAMModelBI/ClientApp/src/view/userInquire/iot.vue
  9. 12 3
      TEAMModelBI/ClientApp/src/view/userInquire/socrates.vue
  10. 7 2
      TEAMModelBI/ClientApp/src/view/userInquire/ticket.vue
  11. 83 73
      TEAMModelBI/Controllers/BINormal/BatchAreaController.cs
  12. 20 21
      TEAMModelBI/Controllers/RepairApi/InitialAreaController.cs
  13. 3 3
      TEAMModelBI/TEAMModelBI.csproj
  14. 174 0
      TEAMModelOS.FunctionV4/Properties/ServiceDependencies/TEAMModelFunction - Zip Deploy/profile.arm.json
  15. 3 3
      TEAMModelOS.FunctionV4/TEAMModelOS.FunctionV4.csproj
  16. 29 0
      TEAMModelOS.SDK/Extension/JwtAuthExtension.cs
  17. 25 3
      TEAMModelOS.SDK/Models/Cosmos/Common/Activity.cs
  18. 3 1
      TEAMModelOS.SDK/Models/Service/Common/ActivityService.cs
  19. 2 3
      TEAMModelOS.SDK/Models/Service/Common/TeacherService.cs
  20. 3 3
      TEAMModelOS.SDK/TEAMModelOS.SDK.csproj
  21. 5 1
      TEAMModelOS/ClientApp/src/api/studentWeb.js
  22. 2 1
      TEAMModelOS/ClientApp/src/boot-app.js
  23. 24 7
      TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContentTypeTemplate/PaperViewBox/PaperView.vue
  24. 5 1
      TEAMModelOS/ClientApp/src/components/student-web/WrongQusetion/QuesList.vue
  25. 13 1
      TEAMModelOS/ClientApp/src/components/student-web/WrongQusetion/WrongQues.vue
  26. 1 1
      TEAMModelOS/ClientApp/src/router/routes.js
  27. 27 21
      TEAMModelOS/ClientApp/src/view/artexam/Create.vue
  28. 27 14
      TEAMModelOS/ClientApp/src/view/artexam/ExamSetting.vue
  29. 654 696
      TEAMModelOS/ClientApp/src/view/artexam/Mgt.vue
  30. 4 1
      TEAMModelOS/ClientApp/src/view/learnactivity/byStu/ByStuMark.vue
  31. 3 0
      TEAMModelOS/ClientApp/src/view/learnactivity/tabs/AnswerTable.vue
  32. 130 27
      TEAMModelOS/ClientApp/src/view/signupActivity/createActivity.vue
  33. 87 55
      TEAMModelOS/ClientApp/src/view/signupActivity/infoGoing.vue
  34. 171 81
      TEAMModelOS/ClientApp/src/view/signupActivity/infoReleased.vue
  35. 34 16
      TEAMModelOS/ClientApp/src/view/signupActivity/processActivity.vue
  36. 177 21
      TEAMModelOS/ClientApp/src/view/signupActivity/setActivity.vue
  37. 55 37
      TEAMModelOS/ClientApp/src/view/student-web/AppNew.vue
  38. 15 15
      TEAMModelOS/Controllers/Both/ScoreCalcController.cs
  39. 277 16
      TEAMModelOS/Controllers/Common/ActivityController.cs
  40. 107 16
      TEAMModelOS/Controllers/Common/ExamController.cs
  41. 4 4
      TEAMModelOS/TEAMModelOS.csproj
  42. 33 33
      TEAMModelOS/appsettings.Development.json
  43. 1 1
      TEAMModelOS/appsettings.json

BIN
TEAMModelBI/ClientApp/src/assets/img/default1.png


BIN
TEAMModelBI/ClientApp/src/assets/img/default2.png


BIN
TEAMModelBI/ClientApp/src/assets/img/default3.png


BIN
TEAMModelBI/ClientApp/src/assets/img/default4.png


+ 301 - 38
TEAMModelBI/ClientApp/src/view/userInquire/details.vue

@@ -1,8 +1,11 @@
 <template>
-    <div class="userdetails">
+    <div class="entirety">
+        <div class="groupdata" v-show="detailsData.length >1">
+        <span class="nowgroup-num">{{transferNum+1}}</span>/<span class="totalgroup-num">{{detailsData.length}}</span></div>
+    <div class="userdetails" v-loading="loading" element-loading-text="数据加载中...">
         <div class="topbox">
            <div class="img-name">
-            <div class="topbox-images"><el-image src="https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg" fit="fill" /></div>
+            <div class="topbox-images"><el-image :src="randomImage" fit="contain" /></div>
             <div class="topbox-name">
                 <div class="topbox-name-text">{{usernames}}</div>
                 <div class="topbox-name-advance" @click="userdetailState=true">查看账户进阶数据<el-icon><MoreFilled /></el-icon></div>
@@ -61,7 +64,8 @@
                     </div>
                 </div>
                 <el-divider />
-                <replaceHiTeach :detailsData="detailsIot"></replaceHiTeach>
+                <replaceHiTeach :detailsData="detailsIot" v-if="iotState"></replaceHiTeach>
+                <div v-else class="not-coupons">暂无数据</div>
             </div>
             <!-- <div class="hitadiv">
                 <div  class="common-header-title">
@@ -92,8 +96,8 @@
                 <div class="equitystate" v-else>
                     <div class="rightsbox">
                         <div class="rightsbox-content">
-                            <el-carousel trigger="click" height="62vh" :autoplay="false">
-                                <el-carousel-item v-for="item in powerList" :key="item">
+                            <el-carousel trigger="click" height="60vh" :autoplay="false">
+                                <el-carousel-item v-for="item in powerList" :key="item"  v-show="powerList.length >0">
                                     <div class="rightsbox-item">
                                         <div class="rightsbox-item-title">
                                             <p>[ID授权] {{item.name}}</p>
@@ -115,11 +119,12 @@
                                                  </span>
                                             </div>
                                             <!--产品扩充 end-->
-                                            <!--HiTeach 附加功能-->
-                                            <el-divider /> 
-                                            <div class="subjoin">
+                                        </div>
+                                         <!--HiTeach 附加功能-->
+                                         <el-divider /> 
+                                        <div class="subjoin" v-show="item.prodCode ==='Z6ELB6EZ'">
                                                 <div class="subjoin-item" >
-                                                    <p class="subjoin-title">{{subjoinData.hiteachTitle}}</p>
+                                                    <p class="subjoin-title">{{subjoinData.hiteachTitle}}123456</p>
                                                     <div class="subjoin-content" v-for="item in subjoinData.hiteach" :key="item.key">
                                                         <div>
                                                             <span>{{item.title}}</span>
@@ -141,11 +146,51 @@
                                             </div>
                                             <!--HiTeach 附加功能 end-->
                                         </div>
-                                        </div>
-                                        <!-- <div class="rightsbox-item-expansion" v-show="item.prodCode ==='IPALB6EY'">
+                                        <div class="rightsbox-item-expansion" v-show="item.prodCode ==='IPALB6EY'">
                                             <p class="expansion-title">产品详细</p>
                                             <div><span>空间大小:</span> <span class="expansion-sizenum">{{item.number}}   GB</span></div>
-                                        </div> -->
+                                        </div>
+                                    </div>
+                                </el-carousel-item>
+                                <el-carousel-item v-show="powerList.length ===0">
+                                    <div class="power-notdata">暂无权益内容</div>
+                                    <div class="subjoin">
+                                                <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 v-show="item.exps < notTime || item.end ===0">
+                                                            <span class="expire">{{item.title}}</span>
+                                                            <span class="ccnums expire" v-show="item.key==='webirs'">{{item.value}}</span>
+                                                            <!-- <span class="endtimes" v-if="item.exps >= notTime">(到期日:{{item.end}})</span> -->
+                                                            <span class="endtimes-error">(到期日:{{item.end}})</span>
+                                                        </div>
+                                                         <!--过期或未权限 end-->
+                                                         <!--有权限-->
+                                                        <div v-show="item.exps >= notTime">
+                                                            <span>{{item.title}}</span>
+                                                            <span class="ccnums" v-show="item.key==='webirs'">{{item.value}}</span>
+                                                            <span class="endtimes" >(到期日:{{item.end}})123</span>
+                                                        </div>
+                                                         <!--有权限 end-->
+                                                </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 v-show="itemc.end < notTime || itemc.end ===0">
+                                                            <span class="expire">{{itemc.title}}</span>
+                                                            <span class="ccnums expire">{{itemc.num}}</span>
+                                                            <span class="endtimes expire">(到期日:{{itemc.end}})</span>
+                                                        </div>
+                                                        <div v-show="itemc.end > notTime">
+                                                            <span>{{itemc.title}}</span>
+                                                            <span class="ccnums">{{itemc.num}}</span>
+                                                            <span class="endtimes">(到期日:{{itemc.end}})</span>
+                                                        </div>
+                                                    </div>
+                                            </div>
+                                            <!--HiTeach 附加功能 end-->
                                     </div>
                                 </el-carousel-item>
                             </el-carousel>
@@ -197,7 +242,7 @@
         <!--back end-->
         <!--所有登入时间dialog-->
         <div class="login-total">
-            <el-dialog v-model="loginDialog" title="所有登入情况" width="65%" :before-close="handleClose">
+            <el-dialog v-model="loginDialog" title="所有登入情况" width="70%" :before-close="handleClose">
                 <div class="logintotal-box">
                     <div class="logintotal-box-list">
                         <div :class="[item.state ? item.key:'notenabled-total','listbox-item-total']" v-for="item in productdata" :key="item.id">
@@ -216,7 +261,7 @@
                         <p class="logintotal-box-table-title">登入详细:</p>
                         <el-table :data="logintotalTable" style="width: 100%" height="35vh" border >
                             <el-table-column prop="product" label="平台" align="center"/>
-                            <el-table-column prop="time" label="时间" align="center"/>
+                            <el-table-column prop="timeText" label="时间" align="center"/>
                         </el-table>
                     </div>
                 </div>
@@ -229,9 +274,32 @@
         </div>
          <!--所有登入时间dialog end-->
     </div>
+    <div class="lastbtn" v-show="detailsData.length >1 && transferNum !==0" @click="lastUser()">
+        <div class="btn-ls">
+          <svg class="last-btns" aria-hidden="true">
+            <use xlink:href="#icon-icon-test"></use>
+          </svg>
+        </div>
+        <div class="btn-ls-text">
+          <!-- <p class="next-school">{{advancevalue.last.name}}</p> -->
+          <p class="next-title">上一个</p>
+        </div>
+    </div>
+    <div class="nextbtn" v-show="detailsData.length >1 && transferNum+1 !==detailsData.length" @click="nextUser()">
+        <div class="btn-ls">
+          <svg class="last-btns" aria-hidden="true">
+            <use xlink:href="#icon-icon-test1"></use>
+          </svg>
+        </div>
+        <div class="btn-ls-text">
+          <!-- <p class="next-school">{{advancevalue.next.name}}</p> -->
+          <p class="next-title">下一个</p>
+        </div>
+    </div>
+    </div>
 </template>
 <script setup>
-import { ref, getCurrentInstance, watch, h, nextTick,onMounted,defineProps,defineEmits } from 'vue'
+import { ref, getCurrentInstance, watch, h, nextTick,onMounted,defineProps,defineEmits,computed } from 'vue'
 import { Iphone,More,MoreFilled,ArrowLeft,Right } from '@element-plus/icons'
 import commonHeatmapVue from '../../components/echarts/commonHeatmap.vue'
 import commonGaugeVue from '../../components/echarts/commonGauge.vue'
@@ -246,7 +314,7 @@ import replaceHiTeach from './iot.vue'
 let props = defineProps({
     searchdata: Object,
 })
-let { proxy } = getCurrentInstance()
+let {ctx:that,proxy } = getCurrentInstance()
 let activeName = ref('basics')
 let userdetailState=ref(false)
 let tabPosition = ref('left')
@@ -532,9 +600,10 @@ let rightsdata=ref([
 ])
 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]
+let transferNum=ref(0)   //*滚动的数值*
+let ies5datas=ref(detailsData.value[transferNum.value].ies5)
+let sokratesData=ref(detailsData.value[transferNum.value].sokrates)
+let pointsData=ref(detailsData.value[transferNum.value])
 console.log(props.searchdata, '传输的值')
 console.log(detailsData.value, '最终数据')
 let powerExtension=ref([
@@ -576,6 +645,24 @@ let buttonSelect = ref([
 ])
 let loginDialog=ref(false)
 let logintotalTable=ref([])
+let iotState=ref(true)
+let advancevalue=ref({
+    last:'',
+    next:''
+})
+let loading=ref(false)
+let headeronly=ref(require('@/assets/img/default1.png'))
+let headerData=ref([
+    require('@/assets/img/default1.png'),
+    require('@/assets/img/default2.png'),
+    require('@/assets/img/default3.png'),
+    require('@/assets/img/default4.png'),
+    ])
+const randomImage = computed(()=>{
+    const randomIndex = Math.floor(Math.random() * headerData.value.length);
+    console.log(randomIndex,'随机头像')
+    return headerData.value[randomIndex];
+})
 const handleChildEvent = (payload) => {
     userdetailState.value=false
 }
@@ -585,7 +672,8 @@ const backClick = () => {
         emits('parentClick','default') // 使用方式和 vue2 this.$emit 一样
 }
 function initdata() {
-    let transmitData = detailsData.value[0]
+    console.log(detailsData.value,'进入方法查看')
+    let transmitData = detailsData.value[transferNum.value]
     console.log(transmitData,'数据')
     //header 基础信息 
     let { name,id,mobile, mail, schoolCode, schoolCodeW, points,country,province,city,} = transmitData
@@ -599,16 +687,23 @@ function initdata() {
     //产品使用及安装情况 登入时间
     let {login}=transmitData
     let logintimes = login.length >0 ? login.sort(function(a,b){return b.time-a.time}):'无'
+    console.log(logintimes,'时间!')
     logintimes !=='无' ?  userdata.value[5].value=proxy.$common.timestampToTime(logintimes[0].time,'all'):userdata.value[5].value='暂无'
     if(login.length >0){
+       logintotalTable.value=[]
        login.forEach(item => {
          let typename=item.product
          productdata.value.forEach(items=>{
             typename === items.key ? items.value === 0 ? (items.value=proxy.$common.timestampToTime(item.time,'all'),items.state=true): item.time > items.value ? (items.value=proxy.$common.timestampToTime(item.time,'all'),items.state=true):'':''
          })
-         item.time=proxy.$common.timestampToTime(item.time,'all')
+         item.timeText=proxy.$common.timestampToTime(item.time,'all')
          logintotalTable.value.push(item)
        })
+    }else{
+        logintotalTable.value=[]
+        productdata.value.forEach((item)=>{
+            item.state=false
+        })
     }
     //空间与权益
     let {usedSize,teachSize,totalSize,surplusSize}=transmitData.ies5
@@ -644,12 +739,13 @@ function initdata() {
         let days = Math.floor(timeDifference / (24 * 60 * 60 ));
         console.log(days,'天数')
         item.intervalTime=days
-        item.startDateText=item.startDateText ? proxy.$common.timestampToTime(item.startDate):0
-        item.endDateText= item.endDateText? proxy.$common.timestampToTime(item.endDate):0
+        item.startDateText=item.startDate ? proxy.$common.timestampToTime(item.startDate):0
+        item.endDateText= item.endDate? proxy.$common.timestampToTime(item.endDate):0
         powerList.value.push(item)  
     })
     //处理权益内 附加功能(HITeach附加 CC权限)
     let hiteachData=benefits.hiteach !==null ?benefits.hiteach:[] ; let ccData=benefits.hiteachcc !==null  ?benefits.hiteachcc:[]
+    console.log(hiteachData,ccData)
     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)
@@ -657,6 +753,7 @@ function initdata() {
                 funcKey === items.key ? (items.start=startTime,items.end=endTime,items.value=item.para,items.gets=item.get,items.exps=item.exp):''
             })
         })
+        console.log(subjoinData.value)
     }
     if(ccData.length >0 ){
         ccData.forEach((itemA)=>{
@@ -683,40 +780,98 @@ function initdata() {
     gaugedata.value.series[0].data[0].value=transitionUsed !==0 ? Math.round((transitionUsed/transitionTotal).toFixed(2)*100):0
     gaugedata.value.title[0].text=useGsize+' G'
     console.log(transitionUsed,transitionSuplus,transitionTeach,transitionTotal,'结果')
+
+    //处理上下一个
+    advancevalue.value.next=detailsData.value[transferNum.value+1]
+    loading.value=false
 }
 function bytesToGB(bytes) {
   const gb = bytes / (1024 * 1024 * 1024);
   return gb.toFixed(2); // 将结果保留两位小数
 }
 function init(){
+    loading.value=true
+    console.log(detailsData.value,transferNum.value,'查看IOT')
     //处理关于iot问题
-    detailsData.value[0].iot.hiteach.day ? 
-    (buttonSelect.value[0].click=true,detailsIot.value=detailsData.value[0].iot.hiteach.day):
-    detailsData.value[0].iot.hiteach.month ? 
-    (buttonSelect.value[1].click=true,detailsIot.value=detailsData.value[0].iot.hiteach.month):
-    detailsData.value[0].iot.hiteach.year ? 
-    (buttonSelect.value[2].click=true,detailsIot.value=detailsData.value[0].iot.hiteach.year):''
+    detailsData.value[transferNum.value].iot.hiteach.day !==null ? 
+    (buttonSelect.value[0].click=true,detailsIot.value=detailsData.value[transferNum.value].iot.hiteach.day,iotState.value=true):
+    detailsData.value[transferNum.value].iot.hiteach.month !==null ? 
+    (buttonSelect.value[1].click=true,detailsIot.value=detailsData.value[transferNum.value].iot.hiteach.month,iotState.value=true):
+    detailsData.value[transferNum.value].iot.hiteach.year !==null ? 
+    (buttonSelect.value[2].click=true,detailsIot.value=detailsData.value[transferNum.value].iot.hiteach.year,iotState.value=true):iotState.value=false
 
     //BTN显示问题
-    detailsData.value[0].iot.hiteach.day ? buttonSelect.value[0].disabled=false:buttonSelect.value[0].disabled=true
-    detailsData.value[0].iot.hiteach.month ? buttonSelect.value[1].disabled=false:buttonSelect.value[1].disabled=true
-    detailsData.value[0].iot.hiteach.year ? buttonSelect.value[2].disabled=false:buttonSelect.value[2].disabled=true
+    console.log(detailsData.value,'按钮问题')
+    detailsData.value[transferNum.value].iot.hiteach.day ? (buttonSelect.value[0].disabled=false,buttonSelect.value[0].click=true):(buttonSelect.value[0].disabled=true,buttonSelect.value[0].click=false)
+    detailsData.value[transferNum.value].iot.hiteach.month ? (buttonSelect.value[1].disabled=false,buttonSelect.value[1].click=true):(buttonSelect.value[1].disabled=true,buttonSelect.value[1].click=false)
+    detailsData.value[transferNum.value].iot.hiteach.year  ? (buttonSelect.value[2].disabled=false,buttonSelect.value[2].click=true):(buttonSelect.value[2].disabled=true,buttonSelect.value[2].click=false)
 }
 function selectTime(value){
     console.log(value,'值')
     value =='day' ? 
-    (buttonSelect.value[0].click=true,buttonSelect.value[1].click=false,buttonSelect.value[2].click=false,detailsIot.value=detailsData.value[0].iot.hiteach.day):
+    (buttonSelect.value[0].click=true,buttonSelect.value[1].click=false,buttonSelect.value[2].click=false,detailsIot.value=detailsData.value[transferNum.value].iot.hiteach.day):
     value =='month' ?
-    (buttonSelect.value[1].click=true,buttonSelect.value[0].click=false,buttonSelect.value[2].click=false,detailsIot.value=detailsData.value[0].iot.hiteach.month):
+    (buttonSelect.value[1].click=true,buttonSelect.value[0].click=false,buttonSelect.value[2].click=false,detailsIot.value=detailsData.value[transferNum.value].iot.hiteach.month):
     value =='year' ? 
-    (buttonSelect.value[2].click=true,buttonSelect.value[0].click=false,buttonSelect.value[1].click=false,detailsIot.value=detailsData.value[0].iot.hiteach.year):''
+    (buttonSelect.value[2].click=true,buttonSelect.value[0].click=false,buttonSelect.value[1].click=false,detailsIot.value=detailsData.value[transferNum.value].iot.hiteach.year):''
 
     console.log(detailsIot.value,'值')
 }
+//切换用户 需要执行的内容
+function cutUser(){
+    loading.value=true
+    console.log(detailsData,'数据组')
+    ies5datas.value=detailsData.value[transferNum.value].ies5
+    sokratesData.value=detailsData.value[transferNum.value].sokrates
+    pointsData.value=detailsData.value[transferNum.value]
+    powerList.value=[]
+    initdata()
+    init()
+    setTimeout(()=>{loading.value=false},800)
+    that.$forceUpdate()
+    // let nowNums=transferNum.value
+    // if(nowNums+1 === detailsData.value.length){
+    //     advancevalue.value.next={}
+    //     advancevalue.value.last=detailsData.value[nowNums-1]
+    // }else if(nowNums.value !==0 && nowNums.value+1 !== detailsData.value.length){
+    //     advancevalue.value.next=detailsData.value[nowNums+1]
+    //     advancevalue.value.last=detailsData.value[nowNums-1]
+    // }else if(nowNums.value ===0){
+    //     advancevalue.value.next=detailsData.value[nowNums+1]
+    // }
+}
+//下一个用户
+function nextUser(){
+    console.log(transferNum,'值')
+    transferNum.value=Number(transferNum.value)+1
+    if(transferNum.value === detailsData.value.length){}
+}
+//上一个用户
+function lastUser(){
+    if(transferNum.value !==0){
+        transferNum.value=Number(transferNum.value)-1
+        console.log(transferNum.value,'页数')
+        ies5datas.value=detailsData.value[transferNum.value].ies5
+        sokratesData.value=detailsData.value[transferNum.value].sokrates
+        pointsData.value=detailsData.value[transferNum.value]
+    }
+}
 onMounted(() => {
     initdata()
 })
 init()
+watch(()=>transferNum.value,(newnum)=>{
+    console.log(newnum,'变化的数字')
+    // nextTick(() => {
+        cutUser()
+    // })
+})
+// watch(() => props.detailsData, (newValue) => {
+//     //   propValue.value = newValue;
+//     let datas={detailsData:newValue}
+//     init(datas)
+//     console.log(datas,'有数据进来')
+//   });
 </script>
 <style>
 .userdetails{
@@ -1003,7 +1158,7 @@ init()
 }
 .login-echart{
     width:100%;
-    height:40vh;
+    height:54vh;
     display: flex;
     align-items: center;
 }
@@ -1067,6 +1222,7 @@ init()
 .rightsbox-item-expansion div{
     line-height: 18px;
     padding-left: 4px;
+    margin-bottom: 3px;
 }
 .subjoin-item{
     margin-top:5px;
@@ -1089,7 +1245,7 @@ init()
     color:#909399;
 }
 .subjoin{
-    height:20vh;
+    height:25vh;
     line-height: 20px;
     color:#73767a;
     overflow: hidden;
@@ -1164,7 +1320,7 @@ init()
 }
 .logintotal-box-list{
     display: flex;
-    flex-wrap: wrap;
+    flex-wrap: nowrap;
     overflow: hidden;
     overflow-x: auto;
 }
@@ -1178,6 +1334,113 @@ init()
     margin-bottom: 5px;
     color:#606266;
 }
+.not-coupons{
+    display: flex;
+    height:90%;
+    align-items: center;
+    justify-content: center;
+    font-size:22px;
+    font-weight: 700;
+    color: #73767a;
+    /* line-height: 160px; */
+}
+.power-notdata{
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    font-size:22px;
+    font-weight: 700;
+    color: #73767a;
+    height: 30vh;
+}
+.expire{
+    color: #b1b3b8;
+}
+.groupdata{
+    line-height: 16px;
+    text-align: right;
+    font-size:14px;
+    color:#909399;
+}
+.nowgroup-num,.totalgroup-num{
+    color:#409EFF;
+    margin:0px 3px;
+}
+
+.lastbtn {
+  width: 80px;
+  height: auto;
+  line-height: 20px;
+  position: absolute;
+  left: 0%;
+  top: 45%;
+  opacity: 0.5;
+  display: flex;
+  flex-wrap: wrap;
+  justify-content: center;
+  align-items: center;
+  border-radius: 5px;
+  font-size: 14px;
+  padding: 5px;
+}
+.nextbtn {
+  width: 80px;
+  height: auto;
+  line-height: 20px;
+  position: absolute;
+  right: 0%;
+  top: 43%;
+  opacity: 0.5;
+  display: flex;
+  flex-wrap: wrap;
+  justify-content: center;
+  align-items: center;
+  border-radius: 5px;
+  font-size: 14px;
+  padding: 5px;
+}
+.lastbtn:hover,
+.nextbtn:hover {
+  background-color: #dfe6e9;
+  opacity: 1;
+  box-shadow: rgba(6, 24, 44, 0.1) 0px 0px 0px 2px,
+    rgba(6, 24, 44, 0.1) 0px 4px 6px -1px,
+    rgba(255, 255, 255, 0.08) 0px 1px 0px inset;
+  cursor: pointer;
+}
+.lastbtn:hover .btn-ls-text,
+.nextbtn:hover .btn-ls-text {
+  opacity: 0.8;
+}
+.btn-ls {
+  width: 100%;
+}
+.btn-ls-text {
+  margin-top: 3px;
+  color: #303a5d;
+  opacity: 0;
+}
+.last-btns {
+  width: 40px;
+  height: 40px;
+  vertical-align: -0.5em;
+  fill: currentColor;
+}
+.btn-ls-text p {
+  margin-bottom: 5px;
+}
+.next-school {
+  font-size: 12px;
+  color: #303a5d;
+  font-weight: bold;
+}
+.next-title {
+  font-size: 8px;
+  color: #34495e;
+}
+.entirety{
+    position: relative;
+}
 </style>
 <style>
 .inquirebox-details .el-divider{

+ 9 - 2
TEAMModelBI/ClientApp/src/view/userInquire/ies.vue

@@ -289,6 +289,10 @@
  onMounted(() => {
     initdatas()
 })
+watch(()=>props,(newvalue)=>{
+    console.log(newvalue,'有新值进入IES')
+    newvalue.iesdata ? (allschoolList.value=[],initdatas()):''
+},{deep:true})
  </script>
  <style scoped>
  .ies-content{
@@ -460,17 +464,20 @@
     padding: 1%;
     display: flex;
     flex-wrap: wrap;
+    height:64vh;
+    overflow: hidden;
+    overflow-y: auto;
  }
  .dialogschool-item{
-    width:25%;
+    width:30%;
     padding: 2% 1%;
     justify-content: center;
     align-items: center;
     border:1px solid #ccc;
     border-radius: 5px;
-    margin:0px 10px;
     position:relative;
     overflow: hidden;
+    margin:1%;
  }
  .dialogschool-item-top,.item-content-tile{
     text-align: center;

+ 18 - 1
TEAMModelBI/ClientApp/src/view/userInquire/index.vue

@@ -62,7 +62,24 @@ function seachSole(datavalue) {
         ElMessage.info('请输入手机号码/ 醍魔豆账号 进行搜索!')
         return
     }
-    let data = {'tmids':[searchvalue.value]}
+    //处理TW用户 ID可能是以0开头的情况。删除掉开头的0
+    if(searchvalue.value.startsWith('0')){
+        searchvalue.value =searchvalue.value.substring(1)
+    }
+    //处理多名用户查询
+    let userArr=''
+    if(searchvalue.value.includes(',') || searchvalue.value.includes(',')){
+        searchvalue.value=searchvalue.value.replace(/,/g, ',');
+        userArr = searchvalue.value.split(',');
+    }
+    if(userArr.length >5){
+            ElMessage.info('同时查询人员不能大于5,请调整查询范围')
+            return
+    }
+    //let testdata=['1524738018','1595321354']
+    let data = ''
+    //let data={'tmids':testdata}
+    searchvalue.value.includes(',') || searchvalue.value.includes(',') ? data={'tmids':userArr}: data = {'tmids':[searchvalue.value]}
     console.log(data)
     searchLoading.value=true
     proxy.$api.getUserdatas(data).then((res) => { 

+ 12 - 5
TEAMModelBI/ClientApp/src/view/userInquire/iot.vue

@@ -631,7 +631,8 @@
     // props.pattern.state ==='school' ? appearState.value='default': props.pattern.state ==='area' ? appearState.value='area':appearState.value='default'
     
     let propsbox=transfervalue? transfervalue:props
-    console.log(propsbox, '数据')
+    
+    console.log(propsbox,props,'数据1234')
     //学校基础信息
     // let { name, region, province, city, dist } = propsbox.detailsData.school
     // schoolData.value.name = appearState.value ==='default' ? name: appearState.value ==='area' ? propsbox.detailsData.name:''
@@ -717,13 +718,19 @@
   function backbtn () {
     $myemit('myback', 'default')
   }
+onMounted(() => {
   init()
-  watch(() => props.detailsData, (newValue) => {
+})
+  watch(() => props, (newValue) => {
+    console.log()
     //   propValue.value = newValue;
-    let datas={detailsData:newValue}
+    let datas={detailsData:newValue.detailsData}
     init(datas)
-    console.log(datas,'有数据进来')
-  });
+  },{deep:true});
+  watch(()=>props,(newdata)=>{
+    console.log(newdata,'9999999999999999')
+  })
+
   </script>
   <style  scoped>
   .details-analysis {

+ 12 - 3
TEAMModelBI/ClientApp/src/view/userInquire/socrates.vue

@@ -377,6 +377,8 @@ function initdatas(){
         presupposed.value.data.general_observation=user_channels.general_observation,
         presupposed.value.state=true)
         :presupposed.value.state=false
+    }else{
+        presupposed.value.state=false
     }
     //处理total数据
     primary.value[0].value=hiteach_data.total.t_data ? hiteach_data.total.t_data:0
@@ -412,22 +414,29 @@ function selectTime(values) {
         element.value === values ? element.click=true: element.click=false
     });
     if(values ==='week'){
+        console.log(hiteach_data.this_week.t_green,hiteach_data.this_month.t_green)
         proportiondata.value.title[0].text='占同月T绿灯比例'
-        proportiondata.value.title[1].text=Math.round((hiteach_data.this_week.t_green/hiteach_data.this_month.t_green).toFixed(2)*100)+'%'
+        proportiondata.value.title[1].text= hiteach_data.this_week.t_green !=0 ? Math.round((hiteach_data.this_week.t_green/hiteach_data.this_month.t_green).toFixed(2)*100)+'%':0+'%'
         proportiondata.value.series[0].data[0].value=Math.round((hiteach_data.this_week.t_green/hiteach_data.this_month.t_green).toFixed(2)*100)
         proportiondata.value.series[0].data[1].value=100-(Math.round((hiteach_data.this_week.t_green/hiteach_data.this_month.t_green).toFixed(2)*100))
     }else if(values ==='month'){
+        console.log(hiteach_data.this_month.t_green,hiteach_data.this_year.t_green)
         proportiondata.value.title[0].text='占同年T绿灯比例'
-        proportiondata.value.title[1].text=Math.round((hiteach_data.this_month.t_green/hiteach_data.this_year.t_green).toFixed(2)*100)+'%'
+        proportiondata.value.title[1].text=hiteach_data.this_month.t_green !=0 ? Math.round((hiteach_data.this_month.t_green/hiteach_data.this_year.t_green).toFixed(2)*100)+'%':0+'%'
         proportiondata.value.series[0].data[0].value=Math.round((hiteach_data.this_month.t_green/hiteach_data.this_year.t_green).toFixed(2)*100)
         proportiondata.value.series[0].data[1].value=100-(Math.round((hiteach_data.this_month.t_green/hiteach_data.this_year.t_green).toFixed(2)*100))
     }else if(values ==='year'){
+        console.log(hiteach_data.this_year.t_green,hiteach_data.total.t_green)
         proportiondata.value.title[0].text='占全部T绿灯比例'
-        proportiondata.value.title[1].text=Math.round((hiteach_data.this_year.t_green/hiteach_data.total.t_green).toFixed(2)*100)+'%'
+        proportiondata.value.title[1].text=hiteach_data.this_year.t_green && hiteach_data.total.t_green ? Math.round((hiteach_data.this_year.t_green/hiteach_data.total.t_green).toFixed(2)*100)+'%':0+'%'
         proportiondata.value.series[0].data[0].value=Math.round((hiteach_data.this_year.t_green/hiteach_data.total.t_green).toFixed(2)*100)
         proportiondata.value.series[0].data[1].value=100-(Math.round((hiteach_data.this_year.t_green/hiteach_data.total.t_green).toFixed(2)*100))
     }
 }
+watch(()=>props,(newvalue)=>{
+    console.log(newvalue,'有新值进入苏格拉底')
+    newvalue.sokratesdatas ? (initdatas(),zb.value = proportiondata.value):''
+},{deep:true})
  </script>
  <style scoped>
  .socrates-header,.socrates-auxiliary{

+ 7 - 2
TEAMModelBI/ClientApp/src/view/userInquire/ticket.vue

@@ -142,6 +142,10 @@ let gaindata = computed(() => {
  onMounted(() => {
     inidatas()
 })
+watch(()=>props,(newdata)=>{
+    console.log(newdata,'有新值进入票卷')
+    newdata.pointsandcoupons ? (ticketlist.value=[],inidatas()):''
+},{deep:true})
 </script>
 <style scoped>
 .ticket-header{
@@ -285,9 +289,10 @@ let gaindata = computed(() => {
     padding-top:25px;
 }
 .not-coupons{
-    font-size:16px;
+    font-size:18px;
     color: #73767a;
-    line-height: 120px;
+    line-height: 160px;
+    font-weight: 700;
 }
 /* .usebox{
     color:rgba(255,255,255,.9) !important;

+ 83 - 73
TEAMModelBI/Controllers/BINormal/BatchAreaController.cs

@@ -598,31 +598,27 @@ namespace TEAMModelBI.Controllers.BINormal
                 #endregion
 
                 //执行复制操作
-                BatchCopyFile batchCopyFile = new BatchCopyFile();
-                batchCopyFile.blobCntr = "teammodelos";
-                batchCopyFile.oldFileName = $"{oldStandard}";
-                batchCopyFile.newFileName = $"{standard}";
-                batchCopyFile.tmdid = $"{_tmdId}";
-                batchCopyFile.tmdIds = new List<string> { $"{ _tmdId}" };
-                batchCopyFile.codeKey = partitionCode;
-                batchCopyFile.tmdName = $"{_tmdName}";
-                var messageBatchCopyFile = new ServiceBusMessage(batchCopyFile.ToJsonString());
-                messageBatchCopyFile.ApplicationProperties.Add("name", "CopyStandardFile");
-                //var activeTask = _configuration.GetValue<string>("Azure:ServiceBus:ActiveTask");
-
-                try
-                {
-                    //await  _serviceBus.GetServiceBusClient().SendMessageAsync(activeTask, messageBatchCopyFile);
-                    await serBusClient.SendMessageAsync(activeTask, messageBatchCopyFile);
-                }
-                catch (Exception)
-                {
-                    return Ok(new { state = 201, msg = "能力点复制成功,复制能力点的文件失败," });
-                }
- 
-
-            
-              
+                //去除新建区,复制能力点文件
+                //BatchCopyFile batchCopyFile = new BatchCopyFile();
+                //batchCopyFile.blobCntr = "teammodelos";
+                //batchCopyFile.oldFileName = $"{oldStandard}";
+                //batchCopyFile.newFileName = $"{standard}";
+                //batchCopyFile.tmdid = $"{_tmdId}";
+                //batchCopyFile.tmdIds = new List<string> { $"{ _tmdId}" };
+                //batchCopyFile.codeKey = partitionCode;
+                //batchCopyFile.tmdName = $"{_tmdName}";
+                //var messageBatchCopyFile = new ServiceBusMessage(batchCopyFile.ToJsonString());
+
+                //messageBatchCopyFile.ApplicationProperties.Add("name", "CopyStandardFile");
+
+                //try
+                //{
+                //    await serBusClient.SendMessageAsync(activeTask, messageBatchCopyFile);
+                //}
+                //catch (Exception)
+                //{
+                //    return Ok(new { state = 201, msg = "能力点复制成功,复制能力点的文件失败," });
+                //}
                 //保存操作记录
                 //await _azureStorage.SaveBILog("area-add", $"{_tmdName}【{_tmdId}】已操作创区功能模块:{name},当前标准【{standard}】,复制的微能力点:{tempStandard}", _dingDing, httpContext: HttpContext);
                 await AzureStorageBlobExtensions.SaveBILog(blobClient, tableClient, "area-add", $"{_tmdName}【{_tmdId}】已操作创区功能模块:{name},当前标准【{standard}】,复制的微能力点:{tempStandard}", _dingDing, httpContext: HttpContext);
@@ -686,20 +682,31 @@ namespace TEAMModelBI.Controllers.BINormal
                 // 檢查是否有大寫或是中文字
                 if ( Regex.IsMatch(shortCode, @"[^a-z0-9]") )
                 {
-                    return Ok(new { state = 1, message = "此區級簡碼必須為小寫字母!" });
+                    return Ok(new { state = 1, message = "此区级简码必须为小写字母!" });
                 }             
 
                 string sqlCheckRep =  $"SELECT c.id FROM c WHERE c.shortCode = '{shortCode}'";
                 var _azureCosmosClientCsv2Read = _azureCosmos.GetCosmosClient(name: "CoreServiceV2CnRead");
-                await foreach (var item in _azureCosmosClientCsv2Read.GetContainer("Core", "School").GetItemQueryStreamIterator(queryText: sqlCheckRep, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("district") })) 
-                {
-                    using var json = await JsonDocument.ParseAsync(item.ContentStream);
-                    if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetInt16() > 0)
-                    {
-                        return Ok(new { state = 1, message = "区级名称相同,请检测区级名称!" });
-                    }                    
-                }
-                
+                //學校資料庫驗證 [式樣待確認,先封印]
+                ///學區簡碼重複驗證
+                //await foreach (var item in _azureCosmosClientCsv2Read.GetContainer("Core", "School").GetItemQueryStreamIterator(queryText: sqlCheckRep, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("district") })) 
+                //{
+                //    using var json = await JsonDocument.ParseAsync(item.ContentStream);
+                //    if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetInt16() > 0)
+                //    {
+                //        return Ok(new { state = 1, message = "此学区简码与其他 [学区] 简码相同请检查!" });
+                //    }                    
+                //}
+                ///學校簡碼重複驗證
+                //await foreach (var item in _azureCosmosClientCsv2Read.GetContainer("Core", "School").GetItemQueryStreamIterator(queryText: sqlCheckRep, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("base") }))
+                //{
+                //    using var json = await JsonDocument.ParseAsync(item.ContentStream);
+                //    if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetInt16() > 0)
+                //    {
+                //        return Ok(new { state = 1, message = "此学区简码与其他 [学校] 简码相同请检查!" });
+                //    }
+                //}
+
                 //DB值更新
                 if (!string.IsNullOrWhiteSpace(name)) area.name = name;
                 if (!string.IsNullOrWhiteSpace(shortCode)) area.shortCode = shortCode;
@@ -942,25 +949,27 @@ namespace TEAMModelBI.Controllers.BINormal
                 _coreAPIHttpService.PushNotify(new List<IdNameCode> { new IdNameCode { id = targetTeacher.id, name = targetTeacher.name, code = targetTeacher.lang } }, "copy-file_area", Constant.NotifyType_IES5_Management, new Dictionary<string, object> { { "tmdname", $"{_tmdName}" } }, _option.Location, _configuration, _dingDing, _environment.ContentRootPath);
 
                 //执行复制操作
-                BatchCopyFile batchCopyFile = new();
-                batchCopyFile.blobCntr = "teammodelos";
-                batchCopyFile.oldFileName = $"{_newStandard}";
-                batchCopyFile.newFileName = $"{_oldStandard}";
-                batchCopyFile.tmdid = $"{_tmdId}";
-                batchCopyFile.tmdIds = new List<string> { $"{ _tmdId}" };
-                batchCopyFile.codeKey = partitionCode;
-                batchCopyFile.tmdName = $"{_tmdName}";
-                var messageBatchCopyFile = new ServiceBusMessage(batchCopyFile.ToJsonString());
-                messageBatchCopyFile.ApplicationProperties.Add("name", "CopyStandardFile");  
-                try
-                {
-                    //await _serviceBus.GetServiceBusClient().SendMessageAsync(activeTask, messageBatchCopyFile);  //先执行删除操作,在执行复制  单一
-                    await serBusClient.SendMessageAsync(activeTask, messageBatchCopyFile);  //先执行删除操作,在执行复制
-                }
-                catch (Exception)
-                {
-                    return Ok(new { state = 201 ,msg = "能力点复制成功,复制能力点的文件失败," });
-                }
+
+                //去除新建区,复制能力点文件
+                //BatchCopyFile batchCopyFile = new();
+                //batchCopyFile.blobCntr = "teammodelos";
+                //batchCopyFile.oldFileName = $"{_newStandard}";
+                //batchCopyFile.newFileName = $"{_oldStandard}";
+                //batchCopyFile.tmdid = $"{_tmdId}";
+                //batchCopyFile.tmdIds = new List<string> { $"{ _tmdId}" };
+                //batchCopyFile.codeKey = partitionCode;
+                //batchCopyFile.tmdName = $"{_tmdName}";
+                //var messageBatchCopyFile = new ServiceBusMessage(batchCopyFile.ToJsonString());
+                //messageBatchCopyFile.ApplicationProperties.Add("name", "CopyStandardFile");  
+                //try
+                //{
+                     
+                //    await serBusClient.SendMessageAsync(activeTask, messageBatchCopyFile);  //先执行删除操作,在执行复制
+                //}
+                //catch (Exception)
+                //{
+                //    return Ok(new { state = 201 ,msg = "能力点复制成功,复制能力点的文件失败," });
+                //}
 
               
                 //保存操作记录
@@ -1276,25 +1285,26 @@ namespace TEAMModelBI.Controllers.BINormal
                         string partitionCode = "DelBeforeCopyAbility-mark";
 
                         //执行复制操作
-                        BatchCopyFile batchCopyFile = new();
-                        batchCopyFile.blobCntr = "teammodelos";
-                        batchCopyFile.oldFileName = $"{newstand.standard}";
-                        batchCopyFile.newFileName = $"{area.standard}";
-                        batchCopyFile.tmdid = $"{_tmdId}";
-                        batchCopyFile.tmdIds = new List<string> { $"{_tmdId}" };
-                        batchCopyFile.codeKey = partitionCode;
-                        batchCopyFile.tmdName = $"{_tmdName}";
-                        var messageBatchCopyFile = new ServiceBusMessage(batchCopyFile.ToJsonString());
-                        messageBatchCopyFile.ApplicationProperties.Add("name", "CopyStandardFile");
-                        try
-                        {
-                            //await _serviceBus.GetServiceBusClient().SendMessageAsync(activeTask, messageBatchCopyFile);  //先执行删除操作,在执行复制  单一
-                            await serBusClient.SendMessageAsync(activeTask, messageBatchCopyFile);  //先执行删除操作,在执行复制
-                        }
-                        catch (Exception)
-                        {
-                            return Ok(new { state = 201, msg = "能力点复制成功,复制能力点的文件失败," });
-                        }
+                        //去除新建区,复制能力点文件
+                        //BatchCopyFile batchCopyFile = new();
+                        //batchCopyFile.blobCntr = "teammodelos";
+                        //batchCopyFile.oldFileName = $"{newstand.standard}";
+                        //batchCopyFile.newFileName = $"{area.standard}";
+                        //batchCopyFile.tmdid = $"{_tmdId}";
+                        //batchCopyFile.tmdIds = new List<string> { $"{_tmdId}" };
+                        //batchCopyFile.codeKey = partitionCode;
+                        //batchCopyFile.tmdName = $"{_tmdName}";
+                        //var messageBatchCopyFile = new ServiceBusMessage(batchCopyFile.ToJsonString());
+                        //messageBatchCopyFile.ApplicationProperties.Add("name", "CopyStandardFile");
+                        //try
+                        //{
+                        //    //await _serviceBus.GetServiceBusClient().SendMessageAsync(activeTask, messageBatchCopyFile);  //先执行删除操作,在执行复制  单一
+                        //    await serBusClient.SendMessageAsync(activeTask, messageBatchCopyFile);  //先执行删除操作,在执行复制
+                        //}
+                        //catch (Exception)
+                        //{
+                        //    return Ok(new { state = 201, msg = "能力点复制成功,复制能力点的文件失败," });
+                        //}
                     }
 
                     StandardFile saveFile = new();

+ 20 - 21
TEAMModelBI/Controllers/RepairApi/InitialAreaController.cs

@@ -262,27 +262,26 @@ namespace TEAMModelBI.Controllers.RepairApi
                 }
 
                 //执行复制操作
-                BatchCopyFile batchCopyFile = new();
-                batchCopyFile.blobCntr = "teammodelos";
-                batchCopyFile.oldFileName = $"{area.standard}";
-                batchCopyFile.newFileName = $"{standard}";
-                batchCopyFile.tmdid = $"{_tmdId}";
-                batchCopyFile.tmdIds = new List<string> { $"{_tmdId}" };
-                batchCopyFile.codeKey = partitionCode;
-                batchCopyFile.tmdName = $"{_tmdName}";
-                var messageBatchCopyFile = new ServiceBusMessage(batchCopyFile.ToJsonString());
-                messageBatchCopyFile.ApplicationProperties.Add("name", "CopyStandardFile");
-                //var activeTask = _configuration.GetValue<string>("Azure:ServiceBus:ActiveTask");
-
-                try
-                {
-                    await _serviceBus.GetServiceBusClient().SendMessageAsync(activeTask, messageBatchCopyFile);
-                    await serBusClient.SendMessageAsync(activeTask, messageBatchCopyFile);
-                }
-                catch (Exception)
-                {
-                    return Ok(new { state = 201, msg = "能力点复制成功,复制能力点的文件失败," });
-                }
+                //BatchCopyFile batchCopyFile = new();
+                //batchCopyFile.blobCntr = "teammodelos";
+                //batchCopyFile.oldFileName = $"{area.standard}";
+                //batchCopyFile.newFileName = $"{standard}";
+                //batchCopyFile.tmdid = $"{_tmdId}";
+                //batchCopyFile.tmdIds = new List<string> { $"{_tmdId}" };
+                //batchCopyFile.codeKey = partitionCode;
+                //batchCopyFile.tmdName = $"{_tmdName}";
+                //去除新建区,复制能力点文件
+                //var messageBatchCopyFile = new ServiceBusMessage(batchCopyFile.ToJsonString());
+                //messageBatchCopyFile.ApplicationProperties.Add("name", "CopyStandardFile");
+                //try
+                //{
+                //    await _serviceBus.GetServiceBusClient().SendMessageAsync(activeTask, messageBatchCopyFile);
+                //    await serBusClient.SendMessageAsync(activeTask, messageBatchCopyFile);
+                //}
+                //catch (Exception)
+                //{
+                //    return Ok(new { state = 201, msg = "能力点复制成功,复制能力点的文件失败," });
+                //}
  
             }
 

+ 3 - 3
TEAMModelBI/TEAMModelBI.csproj

@@ -65,9 +65,9 @@
 		<SpaRoot>ClientApp\</SpaRoot>
 		<DefaultItemExcludes>$(DefaultItemExcludes);$(SpaRoot)node_modules\**</DefaultItemExcludes>
 		<UserSecretsId>078b5d89-7d90-4f6a-88fc-7d96025990a8</UserSecretsId>
-		<Version>5.2311.1</Version>
-		<AssemblyVersion>5.2311.1.1</AssemblyVersion>
-		<FileVersion>5.2311.1.1</FileVersion>
+		<Version>5.2311.8</Version>
+		<AssemblyVersion>5.2311.8.1</AssemblyVersion>
+		<FileVersion>5.2311.8.1</FileVersion>
 		<Description>TEAMModelBI(BI)</Description>
 		<PackageReleaseNotes>BI版本说明版本切换标记2022000908</PackageReleaseNotes>
 		<PackageId>TEAMModelBI</PackageId>

+ 174 - 0
TEAMModelOS.FunctionV4/Properties/ServiceDependencies/TEAMModelFunction - Zip Deploy/profile.arm.json

@@ -0,0 +1,174 @@
+{
+  "$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#",
+  "contentVersion": "1.0.0.0",
+  "metadata": {
+    "_dependencyType": "compute.function.windows.appService"
+  },
+  "parameters": {
+    "resourceGroupName": {
+      "type": "string",
+      "defaultValue": "TEAMModelChengdu",
+      "metadata": {
+        "description": "Name of the resource group for the resource. It is recommended to put resources under same resource group for better tracking."
+      }
+    },
+    "resourceGroupLocation": {
+      "type": "string",
+      "defaultValue": "",
+      "metadata": {
+        "description": "Location of the resource group. Resource groups could have different location than resources, however by default we use API versions from latest hybrid profile which support all locations for resource types we support."
+      }
+    },
+    "resourceName": {
+      "type": "string",
+      "defaultValue": "TEAMModelFunction",
+      "metadata": {
+        "description": "Name of the main resource to be created by this template."
+      }
+    },
+    "resourceLocation": {
+      "type": "string",
+      "defaultValue": "[parameters('resourceGroupLocation')]",
+      "metadata": {
+        "description": "Location of the resource. By default use resource group's location, unless the resource provider is not supported there."
+      }
+    }
+  },
+  "resources": [
+    {
+      "type": "Microsoft.Resources/resourceGroups",
+      "name": "[parameters('resourceGroupName')]",
+      "location": "[parameters('resourceGroupLocation')]",
+      "apiVersion": "2019-10-01"
+    },
+    {
+      "type": "Microsoft.Resources/deployments",
+      "name": "[concat(parameters('resourceGroupName'), 'Deployment', uniqueString(concat(parameters('resourceName'), subscription().subscriptionId)))]",
+      "resourceGroup": "[parameters('resourceGroupName')]",
+      "apiVersion": "2019-10-01",
+      "dependsOn": [
+        "[parameters('resourceGroupName')]"
+      ],
+      "properties": {
+        "mode": "Incremental",
+        "expressionEvaluationOptions": {
+          "scope": "inner"
+        },
+        "parameters": {
+          "resourceGroupName": {
+            "value": "[parameters('resourceGroupName')]"
+          },
+          "resourceGroupLocation": {
+            "value": "[parameters('resourceGroupLocation')]"
+          },
+          "resourceName": {
+            "value": "[parameters('resourceName')]"
+          },
+          "resourceLocation": {
+            "value": "[parameters('resourceLocation')]"
+          }
+        },
+        "template": {
+          "$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
+          "contentVersion": "1.0.0.0",
+          "parameters": {
+            "resourceGroupName": {
+              "type": "string"
+            },
+            "resourceGroupLocation": {
+              "type": "string"
+            },
+            "resourceName": {
+              "type": "string"
+            },
+            "resourceLocation": {
+              "type": "string"
+            }
+          },
+          "variables": {
+            "storage_name": "[toLower(concat('storage', uniqueString(concat(parameters('resourceName'), subscription().subscriptionId))))]",
+            "appServicePlan_name": "[concat('Plan', uniqueString(concat(parameters('resourceName'), subscription().subscriptionId)))]",
+            "storage_ResourceId": "[concat('/subscriptions/', subscription().subscriptionId, '/resourceGroups/', parameters('resourceGroupName'), '/providers/Microsoft.Storage/storageAccounts/', variables('storage_name'))]",
+            "appServicePlan_ResourceId": "[concat('/subscriptions/', subscription().subscriptionId, '/resourceGroups/', parameters('resourceGroupName'), '/providers/Microsoft.Web/serverFarms/', variables('appServicePlan_name'))]",
+            "function_ResourceId": "[concat('/subscriptions/', subscription().subscriptionId, '/resourceGroups/', parameters('resourceGroupName'), '/providers/Microsoft.Web/sites/', parameters('resourceName'))]"
+          },
+          "resources": [
+            {
+              "location": "[parameters('resourceLocation')]",
+              "name": "[parameters('resourceName')]",
+              "type": "Microsoft.Web/sites",
+              "apiVersion": "2015-08-01",
+              "tags": {
+                "[concat('hidden-related:', variables('appServicePlan_ResourceId'))]": "empty"
+              },
+              "dependsOn": [
+                "[variables('appServicePlan_ResourceId')]",
+                "[variables('storage_ResourceId')]"
+              ],
+              "kind": "functionapp",
+              "properties": {
+                "name": "[parameters('resourceName')]",
+                "kind": "functionapp",
+                "httpsOnly": true,
+                "reserved": false,
+                "serverFarmId": "[variables('appServicePlan_ResourceId')]",
+                "siteConfig": {
+                  "alwaysOn": true
+                }
+              },
+              "identity": {
+                "type": "SystemAssigned"
+              },
+              "resources": [
+                {
+                  "name": "appsettings",
+                  "type": "config",
+                  "apiVersion": "2015-08-01",
+                  "dependsOn": [
+                    "[variables('function_ResourceId')]"
+                  ],
+                  "properties": {
+                    "AzureWebJobsStorage": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('storage_name'), ';AccountKey=', listKeys(variables('storage_ResourceId'), '2017-10-01').keys[0].value, ';EndpointSuffix=', 'core.windows.net')]",
+                    "FUNCTIONS_EXTENSION_VERSION": "~3",
+                    "FUNCTIONS_WORKER_RUNTIME": "dotnet"
+                  }
+                }
+              ]
+            },
+            {
+              "location": "[parameters('resourceGroupLocation')]",
+              "name": "[variables('storage_name')]",
+              "type": "Microsoft.Storage/storageAccounts",
+              "apiVersion": "2017-10-01",
+              "tags": {
+                "[concat('hidden-related:', concat('/providers/Microsoft.Web/sites/', parameters('resourceName')))]": "empty"
+              },
+              "properties": {
+                "supportsHttpsTrafficOnly": true
+              },
+              "sku": {
+                "name": "Standard_LRS"
+              },
+              "kind": "Storage"
+            },
+            {
+              "location": "[parameters('resourceGroupLocation')]",
+              "name": "[variables('appServicePlan_name')]",
+              "type": "Microsoft.Web/serverFarms",
+              "apiVersion": "2015-08-01",
+              "sku": {
+                "name": "S1",
+                "tier": "Standard",
+                "family": "S",
+                "size": "S1"
+              },
+              "properties": {
+                "name": "[variables('appServicePlan_name')]"
+              }
+            }
+          ]
+        }
+      }
+    }
+  ]
+}

+ 3 - 3
TEAMModelOS.FunctionV4/TEAMModelOS.FunctionV4.csproj

@@ -5,9 +5,9 @@
 		<OutputType>Exe</OutputType>
 		<_FunctionsSkipCleanOutput>true</_FunctionsSkipCleanOutput>
 		<SignAssembly>true</SignAssembly>
-		<Version>5.2311.1</Version>
-		<AssemblyVersion>5.2311.1.1</AssemblyVersion>
-		<FileVersion>5.2311.1.1</FileVersion>
+		<Version>5.2311.8</Version>
+		<AssemblyVersion>5.2311.8.1</AssemblyVersion>
+		<FileVersion>5.2311.8.1</FileVersion>
 		<PackageId>TEAMModelOS.FunctionV4</PackageId>
 		<Authors>teammodel</Authors>
 		<Company>醍摩豆(成都)信息技术有限公司</Company>

+ 29 - 0
TEAMModelOS.SDK/Extension/JwtAuthExtension.cs

@@ -254,6 +254,35 @@ namespace TEAMModelOS.SDK.Extension
                 return false;
             }
         }
+        /// <summary>
+        /// 刷新token,不验证过期时间
+        /// </summary>
+        /// <param name="token"></param>
+        /// <param name="salt"></param>
+        /// <returns></returns>
+        public static bool ValidateAuthTokenRefresh(string token, string salt)
+        {
+            try
+            {
+                var handler = new JwtSecurityTokenHandler();
+                var validationParameters = new TokenValidationParameters
+                {
+                    RequireExpirationTime = false,
+                    ValidateIssuer = false,
+                    ValidateAudience = false,
+                    IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(salt)),
+                    ValidateLifetime = true,
+                    ClockSkew = TimeSpan.Zero
+                };
+                ClaimsPrincipal principal = handler.ValidateToken(token, validationParameters, out SecurityToken securityToken);
+                return true;
+            }
+            catch (Exception)
+            {
+                //Trace.WriteLine(ex.Message);
+                return false;
+            }
+        }
 
         /// <summary>
         /// 第三方登录后的id_token

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

@@ -85,6 +85,7 @@ namespace TEAMModelOS.SDK.Models
         /// 0未发布,1已发布,2.已结束
         /// </summary>
         public int publish { get; set; }
+        public string sas { get; set; }
     }
 
 
@@ -313,9 +314,7 @@ namespace TEAMModelOS.SDK.Models
     }
     public class ContestSign
     {
-        /// <summary>
-        ///  "name",   "phone",   "period",   "subject",  "school", "contestType",  "job"
-        /// </summary>
+       
         public List<string> field { get; set; } = new List<string>();
         /// <summary>
         /// 报名类型,0个人,1团队
@@ -327,8 +326,25 @@ namespace TEAMModelOS.SDK.Models
         /// //报名人数限制
         /// </summary>
         public int limit { get; set; }
+
+        public List<ContestSignField> fields { get; set; }= new List<ContestSignField>();
     }
+    public class ContestSignField {
+        /// <summary>
+        ///  "name",   "phone",   "period",   "subject",  "school", "contestType",  "job"
+        /// </summary>
+        public string field { get; set; }
 
+        public string label { get; set; }
+        /// <summary>
+        /// text select radio checkbox  password  file  textarea
+        /// </summary>
+        public string type { get; set; }
+        /// <summary>
+        /// 单选,复选,下拉列表
+        /// </summary>
+        public List<string> item { get; set; }= new List<string> ();
+    }
     /// <summary>
     /// 在线培训模块的数据结构
     /// </summary>
@@ -478,6 +494,9 @@ namespace TEAMModelOS.SDK.Models
     
     public class ActivityExpert : CosmosEntity
     {
+        /// <summary>
+        /// 活动id
+        /// </summary>
         public ActivityExpert() {
             pk="ActivityExpert";
             code="ActivityExpert";
@@ -486,8 +505,11 @@ namespace TEAMModelOS.SDK.Models
     }
     public class Expert { 
         public string id { get; set; }
+        public string iname { get; set; }
         public string name { get; set; }
         public string mobile { get; set; }
+        public string email { get; set; }
+        public string tmdid { get; set; }
         public string picture { get; set; }
         /// <summary>
         /// 0 未确认,1 已确认  确认状态

+ 3 - 1
TEAMModelOS.SDK/Models/Service/Common/ActivityService.cs

@@ -1,4 +1,5 @@
-using HTEXLib.COMM.Helpers;
+using Azure.Storage.Blobs.Models;
+using HTEXLib.COMM.Helpers;
 using System;
 using System.Collections.Generic;
 using System.Linq;
@@ -7,6 +8,7 @@ using System.Threading.Tasks;
 using TEAMModelOS.SDK.DI;
 using TEAMModelOS.SDK.Extension;
 using TEAMModelOS.SDK.Models;
+using TEAMModelOS.Services;
 
 namespace TEAMModelOS.SDK
 {

+ 2 - 3
TEAMModelOS.SDK/Models/Service/Common/TeacherService.cs

@@ -30,9 +30,10 @@ namespace TEAMModelOS.Services
 {
     public static class TeacherService
     {
-        public static async Task<TeacherInfo> TeacherInfoLite(AzureCosmosFactory _azureCosmos, Teacher teacher, string name, string picture, string id,
+        public static async Task<TeacherInfo> TeacherInfoLite(AzureCosmosFactory _azureCosmos,  string name, string picture, string id,
            AzureStorageFactory _azureStorage, Option _option, AzureRedisFactory _azureRedis, string ip, HttpTrigger _httpTrigger, string lang)
         {
+            Teacher teacher = null;
             string defaultschool = null;
             //TODO 取得Teacher 個人相關數據(課程清單、虛擬教室清單、歷史紀錄清單等),學校數據另外API處理,多校切換時不同
             var client = _azureCosmos.GetCosmosClient();
@@ -179,7 +180,6 @@ namespace TEAMModelOS.Services
             List<string> roles = new List<string>() { "teacher" };
            
             //換取AuthToken,提供給前端
-            var auth_token = JwtAuthExtension.CreateAuthToken(_option.HostName, id, name?.ToString(), picture?.ToString(), _option.JwtSecretKey, Website: "IES", scope: Constant.ScopeTeacher, standard: "", roles: roles.ToArray(), expire: 1);
 
             //用户在线记录
             try
@@ -194,7 +194,6 @@ namespace TEAMModelOS.Services
             var (blob_uri, blob_sas) = _azureStorage.GetBlobContainerSAS(id, BlobContainerSasPermissions.Write | BlobContainerSasPermissions.Read | BlobContainerSasPermissions.List | BlobContainerSasPermissions.Delete);
             return new TeacherInfo
             {
-                auth_token = auth_token,
                 blob_uri = blob_uri,
                 blob_sas = blob_sas,
                 defaultschool = defaultschool,

+ 3 - 3
TEAMModelOS.SDK/TEAMModelOS.SDK.csproj

@@ -2,9 +2,9 @@
 
   <PropertyGroup>
     <TargetFramework>net6.0</TargetFramework>
-    <Version>5.2311.1</Version>
-    <AssemblyVersion>5.2311.1.1</AssemblyVersion>
-    <FileVersion>5.2311.1.1</FileVersion>
+    <Version>5.2311.8</Version>
+    <AssemblyVersion>5.2311.8.1</AssemblyVersion>
+    <FileVersion>5.2311.8.1</FileVersion>
     <PackageReleaseNotes>发版</PackageReleaseNotes>
   </PropertyGroup>
 

+ 5 - 1
TEAMModelOS/ClientApp/src/api/studentWeb.js

@@ -296,10 +296,14 @@ export default {
 			})
 		})
     },
-    // 获取错题新增
+    // 获取错题数
     getErrorItemCnt: function(data) {
         return post("/common/exam/get-error-item-cnt", data)
     },
+    // 获取错题新增数
+    getErrorItemToday: function(data) {
+        return post("/common/exam/get-error-item-today-cnt", data)
+    },
     // 课程——新增话题
     addTopic: function(data) {
         return post("/school/debate/upsert", data)

+ 2 - 1
TEAMModelOS/ClientApp/src/boot-app.js

@@ -21,7 +21,7 @@ import jwtDecode from 'jwt-decode'
 import GLOBAL from '@/static/Global.js'
 import echarts from 'echarts'
 import vuescroll from 'vuescroll/dist/vuescroll-native'
-import { Tree, Cascader } from 'element-ui' // 按需引入element Tree组件
+import { Tree, Cascader, Switch } from 'element-ui' // 按需引入element Tree组件
 import 'element-ui/lib/theme-chalk/tree.css'
 import 'element-ui/lib/theme-chalk/icon.css'
 import animated from 'animate.css'
@@ -82,6 +82,7 @@ Vue.use(commonComponents)
 Vue.use(evaluationComponents)
 Vue.use(Tree)
 Vue.use(Cascader)
+Vue.use(Switch)
 // 定义全局日志组件
 Vue.use(VueLogger, {
     isEnabled: true,

+ 24 - 7
TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContentTypeTemplate/PaperViewBox/PaperView.vue

@@ -216,6 +216,7 @@
                 canvas2: [],
                 allDom: [],
                 isPrint: false,
+                stusInfo: [],// 艺术评测接口返回的stus
             };
         },
         methods: {
@@ -319,6 +320,7 @@
                                 }
                             })
                         })
+                        this.stusInfo = res.stus
                         
                         if(subList.length) {
                             let ids = JSON.parse(decodeURIComponent(sessionStorage.getItem("ids")))
@@ -395,14 +397,29 @@
                                     cloudas: res.cloudas
                                 }
                                 this.getCorrectData(res)
-                                res.papers.forEach(item => {
-                                    if (item.scope) {
-                                        item.source = null
-                                        item.source = this.getItemTitle.source
-                                        item.qamode = this.getItemTitle.qamode
-                                        this.paperData.push(item)
+                                // 艺术评测试卷需先从stus -> paper 中获取blob, 再从res.papers中找出那一个试卷
+                                if(isArt) {
+                                    let blob = this.stusInfo.find(item => {
+                                        return item.paper[0].subject === res.subjects[0].id
+                                    })
+                                    if(blob) {
+                                        let paperArt = res.papers.find(papers => {
+                                            return papers.blob === blob.paper[0].blob
+                                        })
+                                        paperArt.source = this.getItemTitle.source || null
+                                        paperArt.qamode = this.getItemTitle.qamode
+                                        this.paperData.push(paperArt)
                                     }
-                                })
+                                } else {
+                                    res.papers.forEach(item => {
+                                        if (item.scope) {
+                                            item.source = null
+                                            item.source = this.getItemTitle.source
+                                            item.qamode = this.getItemTitle.qamode
+                                            this.paperData.push(item)
+                                        }
+                                    })
+                                }
                                 for (let i = 0; i < this.paperData.length; i++) {
                                     this.paperData[i].subject = res.subjects[i]
                                     this.paperData[i].allClass = res.claId

+ 5 - 1
TEAMModelOS/ClientApp/src/components/student-web/WrongQusetion/QuesList.vue

@@ -261,8 +261,12 @@ export default {
             let param = {
                 stuId: this.userInfo.sub,
                 subjectId: this.courseNow.subject.id ? this.courseNow.subject.id : this.courseNow.id,
+                teacherId: this.courseNow.teacherId
+            }
+            //if(this.userInfo.scope != 'tmduser') param.code = this.userInfo.azp
+            if(this.userInfo.scope === 'student') {
+                param.code = this.courseNow.school
             }
-            if(this.userInfo.scope != 'tmduser') param.code = this.userInfo.azp
             this.$api.studentWeb.getErrorItemCnt(param).then(res => {
                 if(res.avaliable) {
                     this.topicTotal = res.avaliable

+ 13 - 1
TEAMModelOS/ClientApp/src/components/student-web/WrongQusetion/WrongQues.vue

@@ -147,7 +147,7 @@ export default {
             this.isReview = true
         },
         getErrorNum() {
-            let param = {
+            /* let param = {
                 stuId: this.userInfo.sub,
                 subjectId: this.courseNow.subject.id ? this.courseNow.subject.id : this.courseNow.id,
             }
@@ -156,7 +156,19 @@ export default {
                 if(res.avaliable && res.avaliable > res.record) {
                     this.dayAdd = res.avaliable - res.record
                 }
+            }) */
+            // 改用 get-error-item-today-cnt
+            let param = {
+                stuId: this.userInfo.sub,
+                subjectId: this.courseNow.subject.id ? this.courseNow.subject.id : this.courseNow.id,
+                code: this.userInfo.scope != 'tmduser' ? this.userInfo.azp : null
+            }
+            this.$api.studentWeb.getErrorItemToday(param).then(res => {
+                if(res.avaliable) {
+                    this.dayAdd = res.avaliable
+                }
             })
+            
         },
         startReview(type) {
             this.isLoading = true

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

@@ -1940,7 +1940,7 @@ export const routes = [{
             name: 'areaActivitySet',
             component: () => import('@/view/signupActivity/setActivity.vue'),
             meta: {
-                activeName: 'areaActivityManage',
+                activeName: 'areaActivitySet',
             }
         }
     ]

+ 27 - 21
TEAMModelOS/ClientApp/src/view/artexam/Create.vue

@@ -451,16 +451,17 @@ export default {
                 if (this.isArea) {
                   quoSetting.settings.forEach(setting => {
                     let infoId = this.$jsFn.uuid()
-                    console.error(s)
                     s.task.push({
                       type,
                       subject: setting.subject,
                       isOrder: setting.unorder,
                       infoId: infoId || setting.paper
                     })
-                    setting.paper.infoId = infoId
-                    setting.paper.scope = 'school'
-                    this.examInfoList.push(setting.paper)
+                    setting.paper.forEach(paper => {
+                      paper.infoId = infoId
+                      paper.scope = 'school'
+                    })
+                    this.examInfoList.push(...setting.paper)
                   })
                 } else {
                   let examRes = await this.toSaveExamInfo(
@@ -527,6 +528,7 @@ export default {
             schoolId: this.artInfo.schools.length ? this.artInfo.schools : undefined,
             ArtMusic: this.getZYData()
           })
+          console.error(this.examInfoList)
         } else {
           params.period = {
             id: this.curPeriod.id,
@@ -540,6 +542,7 @@ export default {
         }
         promise?.then(
           (res) => {
+            console.error(this.examInfoList)
             this.$Message.success(this.$t('ae.ae12'))
             this.published = true
             this.saveLoading = false
@@ -561,9 +564,16 @@ export default {
         let promises = []
         let resourcePaper = []
         for (const setting of baseKn.settings) {
-          let apiPapers = await this.getPaperInfo(setting.paper)
-          apiPapers.time = setting.time
-          resourcePaper.push(setting.paper)
+          let simplePapers = setting.paper
+          console.log(simplePapers);
+          let paperPromiseArr = [];
+          simplePapers.forEach((paper) => {
+            paperPromiseArr.push(this.getPaperInfo(paper));
+          });
+          let apiPapers = await Promise.all(paperPromiseArr);
+          // let apiPapers = await this.getPaperInfo(setting.paper)
+          apiPapers.forEach(o => o.time = setting.time)
+          resourcePaper.push(...setting.paper)
           let requestData = {
             id: this.$jsFn.uuid(),
             code: this.$store.state.userInfo.schoolCode,
@@ -583,7 +593,7 @@ export default {
                   setting.subject === "subject_music" ? this.$t('ae.ae10') : this.$t('ae.ae11')
               }
             ],
-            papers: [apiPapers],
+            papers: apiPapers,
             // examType: this.evaluationInfo.examType,
             year: new Date().getFullYear(),
             // range: this.mode,
@@ -634,21 +644,16 @@ export default {
             )
             reses.forEach((res, index) => {
               let targetFolder = "exam/" + res.exam.id + "/paper/"
-              //这里评测都是单科处理
-              schoolBlob
-                .copyFolder(
-                  targetFolder +
-                  res.exam.subjects[0].id +
-                  "/",
-                  resourcePaper[index].blob.substring(1),
-                  paperBlob
-                )
-                .then(
-                  (res) => { },
+              // 231107 调整为艺术评测单科多试卷场景
+              let papers = res.exam.papers;
+              papers.forEach((paper) => {
+                schoolBlob.copyFolder(targetFolder + res.exam.subjects[0].id + "/" + paper.id + '/', resourcePaper[index].blob.substring(1), paperBlob).then(
+                  (res) => {},
                   (err) => {
-                    this.$Message.error(this.$t('ae.ae14'))
+                    this.$Message.error(this.$t("ae.ae14"));
                   }
-                )
+                );
+              });
             })
             r(reses)
           },
@@ -674,6 +679,7 @@ export default {
             apiPaper.code = fullPaper.code
             apiPaper.name = fullPaper.name
             apiPaper.blob = simplePaper.blob
+            apiPaper.subjectId = simplePaper.subjectBindId;
             apiPaper.scope = fullPaper.scope
             apiPaper.sheet = fullPaper.sheet //答题卡 //202108021讨论: 创建评测不需要传答题卡id,由更新答题卡id去做关联。
             apiPaper.multipleRule = fullPaper.multipleRule

+ 27 - 14
TEAMModelOS/ClientApp/src/view/artexam/ExamSetting.vue

@@ -3,11 +3,11 @@
     <!-- 评测试卷 -->
     <div class="attr-item">
       <span>{{$t('ae.ae16')}}</span>
-      <Tag v-if="subjectSetting.paper" color="blue">
-        {{ subjectSetting.paper.name }}
+      <Tag v-for="paper in subjectSetting.paper" color="blue">
+        {{ paper.name }}
       </Tag>
       <span class="choose-paper" @click="selectPaper()">
-        {{ subjectSetting.paper ? $t('ae.ae17') : $t('ae.ae18') }}
+        {{ subjectSetting.paper.length ? $t('ae.ae17') : $t('ae.ae18') }}
       </span>
     </div>
     <!-- 作答方式 -->
@@ -47,7 +47,7 @@
         </p>
         <div class="papaer-list-wrap">
           <vuescroll>
-            <div class="paper-item" v-for="(item,index) in paperListShow" :key="index">
+            <div :class="['paper-item', subjectSetting.paper.find(i => i.id === item.id) ? 'paper-item-active' : '']" v-for="(item,index) in paperListShow" :key="index">
               <div class="paper-item-name">
                 <span>{{(index + 1) + '.  ' + item.name}}</span>
               </div>
@@ -76,8 +76,9 @@
               <div v-else class="paper-item-tools">
                 <span @click.stop="choosePaper(index)">
                   <Icon custom="iconfont icon-choose" style="margin-right:5px;" />
-                  {{$t('learnActivity.manual.stPaper')}}
+                  {{ subjectSetting.paper.find(i => i.id === item.id) ? '取消选择' :  $t('learnActivity.manual.stPaper')}}
                 </span>
+                
                 <span @click.stop="previewTestPaper(index)" style="margin-left:20px;">
                   <Icon type="md-eye" style="margin-right:5px;" />
                   {{$t('learnActivity.manual.previewPaper')}}
@@ -115,7 +116,7 @@ export default {
     return {
       isArea: false,
       subjectSetting: {
-        paper: undefined,
+        paper: [],
         unorder: 0,
         startTime: 0,
         endTime: 0,
@@ -186,8 +187,16 @@ export default {
               content: this.$t('ae.ae43')
             })
           } else {
-            this.subjectSetting.paper = this.paperListShow[index]
-            this.sltPaperStatus = false
+            // this.subjectSetting.paper = this.paperListShow[index]
+            let findIndex = this.subjectSetting.paper.findIndex(i => i.id === this.paperListShow[index].id)
+            if(findIndex > -1){
+               this.subjectSetting.paper.splice(findIndex,1)
+               this.$Message.warning('取消选择')
+            }else{
+               this.subjectSetting.paper.push(this.paperListShow[index])
+               this.$Message.success('加入成功')
+            }
+            // this.sltPaperStatus = false
           }
         }
         sessionStorage.setItem('art_paper_resource', this.paperResource)
@@ -230,6 +239,7 @@ export default {
     subjectSetting: {
       deep: true,
       handler(n, o) {
+        console.error(this.subjectSetting)
         this.$emit('on-exam-change', {
           data: this.subjectSetting,
           subject: this.subject
@@ -266,24 +276,28 @@ export default {
   position: relative;
   // margin-left: 15px;
   margin-right: 15px;
-  margin-bottom: 5px;
+  margin-bottom: 15px;
   // background: #505050;
   align-items: start;
-  border-radius: 2px;
+  border-radius: 10px;
   position: relative;
 
+  &-active{
+    background: #e1eff6;
+  }
+
   &:hover {
     background: #e1eff6;
     // box-shadow: 0px 4px 5px #191919;
   }
 
   &-name {
-    font-size: 20px;
+    font-size: 18px;
     font-weight: bold;
   }
 
   &-tag {
-    font-size: 12px;
+    font-size: 14px;
     padding: 1px 10px;
     border-radius: 5px;
     color: #fff;
@@ -295,12 +309,11 @@ export default {
     margin-top: 15px;
 
     .info-item {
-      font-size: 12px;
+      font-size: 14px;
       padding: 0 10px;
       color: #757575;
 
       .info-bold {
-        font-weight: bold;
         color: #70b1e7;
       }
     }

Diferenças do arquivo suprimidas por serem muito extensas
+ 654 - 696
TEAMModelOS/ClientApp/src/view/artexam/Mgt.vue


+ 4 - 1
TEAMModelOS/ClientApp/src/view/learnactivity/byStu/ByStuMark.vue

@@ -227,6 +227,8 @@ export default {
           }
           this.studentAnswer.status = true
         }
+        // 切换学生后,上面步骤会触发studentAnswer.scores的监控事件,需重置编辑(isUpd)状态,否则会下一次切换学生时保存成绩
+        this.isUpd = false
       },
       deep: true,
       immediate: true
@@ -421,7 +423,8 @@ export default {
           this.$Message.success(this.$t('learnActivity.score.saveScoreOk'))
           this.isUpd = false
           this.$emit('updScore', [d])
-          if (!this.studentAnswer.scores.includes(-1)) {
+          // 切换学生时保存成绩,不需要展示下一位学生页面
+          if (!this.studentAnswer.scores.includes(-1) && this.studentInfo.id === requestData.studentId[0].id) {
             this.isComplete = true
           }
           if (res.isScore) this.$EventBus.$emit('onStatusChange')

+ 3 - 0
TEAMModelOS/ClientApp/src/view/learnactivity/tabs/AnswerTable.vue

@@ -446,6 +446,9 @@ export default {
                 }
                 if (n.papers && n.papers.length) {
                     if (n.owner == 'school') { //**现在不能通过scope判断是校本还是个人发布的评测,后面讨论添加了owner字段区分个人评测还是校本评测
+                        // 艺术评测id固定为subject_painting、subject_music
+                        // 和paperl里的科目id不一样
+                        // 暂不处理
                         let res = n.papers.find((item) => {
                             return item.subjectId == this.chooseSubject;
                         });

+ 130 - 27
TEAMModelOS/ClientApp/src/view/signupActivity/createActivity.vue

@@ -56,9 +56,8 @@
                                 <Icon type="md-alert" color="#ffad16" size="18" />
                                 不选学校则表示所有学校都可参加该活动
                             </p>
-                            <p>
+                            <p v-show="(createData.scope === 'public' && selSchools.length) || createData.scope != 'public'">
                                 <Icon type="ios-alert-outline" color="#ffad16" size="18" />
-                                <!-- <Icon type="ios-alert" color="#ffad16" size="18" /> -->
                                 发布活动后,需学校先确认参与活动,老师才可报名参与
                             </p>
                         </FormItem>
@@ -138,19 +137,15 @@
                                                     </span>
                                                 </FormItem>
                                                 <FormItem label="填报信息">
-                                                    <CheckboxGroup v-model="contestData.sign.field">
+                                                    <CheckboxGroup v-model="fields">
                                                         <Checkbox v-for="(item, index) in infoArr" :key="index" :label="item.value">
                                                             <span>
                                                                 {{ item.label }}
+                                                                <Icon type="md-create" size="18" color="orange" v-show="index > 4" @click.stop.native.prevent="changeFiled(2, item)" />
                                                                 <Icon type="md-trash" size="16" color="#6e6e6e" v-show="item.value === item.label" @click.stop.native.prevent="delField(index)" />
                                                             </span>
                                                         </Checkbox>
-                                                        <Icon type="md-add-circle" @click="addInfoType = true" v-show="!addInfoType" size="18" />
-                                                        <div v-show="addInfoType">
-                                                            <Input v-model="addInfo" placeholder="自定义填报的信息" style="width: 300px"></Input>
-                                                            <Icon type="md-checkmark-circle" size="18" style="margin: 0 10px;" @click="addInform()" />
-                                                            <Icon type="md-close-circle" size="18" @click="addInfoType = false" />
-                                                        </div>
+                                                        <Icon type="md-add-circle" @click="changeFiled(1)" size="18" />
                                                     </CheckboxGroup>
                                                 </FormItem>
                                                 <FormItem label="参赛方式">
@@ -358,6 +353,47 @@
                 </div>
             </template>
         </Drawer>
+        <Modal v-model="modalType" title="自定义填报信息" class="light-iview-form" @on-ok="addInform()">
+            <Form :label-width="100" :model="addInfoForm">
+                <FormItem label="信息名称">
+                    <Input v-model="addInfoForm.label" :disabled="addInfoType === 2" placeholder="自定义填报的信息" style="width: 300px"></Input>
+                </FormItem>
+                <FormItem label="报名时展示的填报格式">
+                    <RadioGroup v-model="addInfoForm.type">
+                        <Radio label="text">
+                            <span>文本</span>
+                        </Radio>
+                        <Radio label="select">
+                            <span>选择器</span>
+                        </Radio>
+                    </RadioGroup>
+                </FormItem>
+                <!-- <p v-show="addInfoForm.type === 'select'">请创建选项,无需选择,所有选项都会为老师展示</p> -->
+                <FormItem label="报名时可供选择的内容" v-show="addInfoForm.type === 'select'">
+                    <div>
+                        <Input v-model="addInfo" placeholder="请输入选项内容" style="width: 300px"></Input>
+                        <Icon type="md-add-circle" size="18" color="#1db40f" style="margin: 0 10px; cursor: pointer;" @click="changeFormValue('add')" />
+                        <!-- <Icon type="md-checkmark-circle" size="18" color="#1db40f" style="margin: 0 10px; cursor: pointer;" @click="changeFormValue('add')" /> -->
+                    </div>
+                    <span>报名时展示的内容:</span>
+                    <Select v-model="selectFileds" placeholder="仅作展示" style="width:300px" @on-select="selectFiledChange">
+                        <Option v-for="(item, index) in addInfoForm.item" :value="item" :key="index">
+                            {{ item }}
+                            <Icon type="md-trash" size="16" color="#9a9a9a" @click.stop.native.prevent="changeFormValue('delete', index)" />
+                        </Option>
+                    </Select>
+                    <!-- <RadioGroup v-model="animal">
+                        <Radio label="金斑蝶"></Radio>
+                        <Radio label="爪哇犀牛"></Radio>
+                        <Radio label="印度黑羚"></Radio>
+                    </RadioGroup> -->
+                    <!-- <span v-for="(item, index) in addInfoForm.item" :key="index" style="margin-right: 10px;">
+                        {{ item }}
+                        <Icon type="md-trash" size="16" color="#9a9a9a" @click="changeFormValue('delete', index)" />
+                    </span> -->
+                </FormItem>
+            </Form>
+        </Modal>
     </div>
 </template>
 
@@ -392,36 +428,52 @@ export default {
             current: 0,
             infoArr: [
                 {
-                    value: 'name',
+                    field: 'name',
                     label: '姓名',
+                    type: "text",
+                    item: []
                 },
                 {
-                    value: 'sex',
+                    field: 'sex',
                     label: '性别',
+                    type: "radio",
+                    item: []
                 },
                 {
-                    value: 'phone',
+                    field: 'phone',
                     label: '手机号码',
+                    type: "text",
+                    item: []
                 },
                 {
-                    value: 'email',
+                    field: 'email',
                     label: '电子邮箱',
+                    type: "text",
+                    item: []
                 },
                 {
-                    value: 'school',
+                    field: 'school',
                     label: '学校',
+                    type: "select",
+                    item: []
                 },
-                {
+                /* {
                     value: 'duties',
                     label: '职务',
-                },
+                    type: "text",
+                    item: []
+                }, */
                 {
-                    value: 'period',
+                    field: 'period',
                     label: '学段',
+                    type: "text",
+                    item: []
                 },
                 {
-                    value: 'subject',
+                    field: 'subject',
                     label: '学科',
+                    type: "text",
+                    item: []
                 },
             ],
             fileType: [
@@ -501,7 +553,7 @@ export default {
             contestData: {
                 modules: ['sign'],
                 sign: {
-                    field: [], //填报信息
+                    fields: [], //填报信息
                     stime: '',
                     etime: '',
                     limit: 0, //报名人数
@@ -526,6 +578,7 @@ export default {
                     etime: '',
                 }, */
             },
+            fields: [], //填报信息
             contestUpload: {
                 stime: '',
                 etime: '',
@@ -556,8 +609,16 @@ export default {
             showTeachers: [], //展示老师列表
             selTeachers: [], //选择的学校
             isUpload: false,
-            addInfoType: false,
+            modalType: false,
+            addInfoType: 0, //0: 不编辑  1:新增填报  2:编辑已有的
             addInfo: '', //自定义填报信息
+            addInfoForm: { //自定义信息类型
+                value: '',
+                label: '',
+                type: 'text',
+                item: [],
+            },
+            selectFileds: '',
             joinType: [
                 {
                     value: 0,
@@ -910,6 +971,8 @@ export default {
                 // 3. 若有新创建的评审规则,需先保存,保存大活动时由后端一起保存
                 this.saveActivity()
             } else {
+                this.createData.invitedSchools = []
+                this.contestData.sign.fields = []
                 this.showSchools.forEach(item => {
                     if(this.selSchools.includes(item.id)) {
                         this.createData.invitedSchools.push({
@@ -919,6 +982,12 @@ export default {
                         })
                     }
                 })
+                this.fields.forEach(item => {
+                    let infos = this.infoArr.find(info => {
+                        return info.field === item
+                    })
+                    if(infos) this.contestData.sign.fields.push(infos)
+                })
                 this.current += 1
             }
         },
@@ -1091,21 +1160,55 @@ export default {
             })
             this.tabName = this.createData.modules[data.length - 1]
         },
+        changeFiled(type, data) {
+            if(type === 2) {
+                this.addInfoForm = data
+            } else {
+                this.addInfoForm = {
+                    field: '',
+                    label: '',
+                    type: 'text',
+                    item: [],
+                }
+            }
+            this.addInfoType = type
+            this.modalType = true
+        },
         addInform() {
             let isHold =  this.infoArr.find(item => {
-                return item.label === this.addInfo
+                return item.label === this.addInfoForm.label
             })
-            if(isHold) {
+            if(isHold && this.addInfoType === 1) {
                 this.$Message.warning('该信息已存在!')
             } else {
-                this.infoArr.push({
-                    value: this.addInfo,
-                    label: this.addInfo,
-                })
-                this.addInfoType = false
+                if(this.addInfoType === 1) {
+                    this.addInfoForm.field = this.addInfoForm.label
+                    this.infoArr.push(this.addInfoForm)
+                } else {
+
+                }
+                this.modalType = false
+                this.addInfoType = 0
                 this.addInfo = ''
+                this.addInfoForm = {
+                    field: '',
+                    label: '',
+                    type: 'text',
+                    item: [],
+                }
+            }
+        },
+        changeFormValue(type, index) {
+            if(type === 'add' && this.addInfo) {
+                this.addInfoForm.item.push(this.addInfo)
+                this.addInfo = ''
+            } else if(type === 'delete') {
+                this.addInfoForm.item.splice(index, 1)
             }
         },
+        selectFiledChange(val) {
+            this.selectFileds = ''
+        },
         // 删除自定义填报信息
         delField(index) {
             this.infoArr.splice(index, 1)

+ 87 - 55
TEAMModelOS/ClientApp/src/view/signupActivity/infoGoing.vue

@@ -12,6 +12,9 @@
                     </span>
                     {{ actInfo.name }}
                 </p>
+                <div class="btn-box" v-show="isArea || actInfo.scope === 'school'">
+                    <Button type="warning" size="small" @click="delAct()">删除</Button>
+                </div>
                 <!-- <div class="btn-box">
                     <Steps :current="2" size="small">
                         <Step title="报名"></Step>
@@ -37,10 +40,13 @@
                             <p>承办:
                                 <span v-for="(item, index) in actInfo.cb" :key="index" style="margin-right: 10px;">{{ item }}</span>
                             </p>
-                            <p v-if="actInfo.scope === 'area' && isArea">学校:
-                                <span>
+                            <p v-if="actInfo.scope != 'school' && isArea">学校:
+                                <span v-if="actInfo.invitedSchools.length">
                                     <span v-for="item in actInfo.invitedSchools" :key="item.id" style="margin-right: 10px;">{{ item.name }}</span>
                                 </span>
+                                <span v-else>
+                                    所有学校
+                                </span>
                             </p>
                             <p v-else-if="actInfo.scope === 'school'">
                                 老师:
@@ -131,61 +137,63 @@
                                     </Table>
                                 </div>
                             </div>
-                            <div class="data-box">
-                                <div class="module-title">评审管理</div>
-                                <div class="module-data">
-                                    <div class="tab-header">
-                                        <Button @click="processShow = true">添加评审专家</Button>
-                                        <Button style="margin-left: 20px;" @click="workPro = true">自动分配评审作品</Button>
-                                        <Button style="float: right;" @click="ruleDrawer = true">评审规则</Button>
-                                    </div>
-                                    <Table :columns="processColumns" :data="processList">
-                                        <template #process="{}">
-                                            <Progress :percent="25" :stroke-width="10" />
-                                        </template>
-                                        <template #actions="{row, index}">
-                                            <Button type="error" size="small" @click="deleteApplica(row, index, 'process')">删除</Button>
-                                        </template>
-                                    </Table>
-                                </div>
-                            </div>
-                            <div class="data-box">
-                                <div class="module-title">成绩统计</div>
-                                <div class="module-data">
-                                    <div class="tab-header">
-                                        <div v-show="!awardsing">
-                                            <Button @click="setAwards()">设置奖项</Button>
-                                            <Button @click="openAch()" style="margin-left: 20px;">公示成绩</Button>
-                                        </div>
-                                        <div v-show="awardsing">
-                                            <Button @click="awardsShow = true">批量设置</Button>
-                                            <Button @click="awardTypes()" style="margin-left: 20px;">取消</Button>
+                            <template v-if="actInfo.scope === 'area' && isArea">
+                                <div class="data-box">
+                                    <div class="module-title">评审管理</div>
+                                    <div class="module-data">
+                                        <div class="tab-header">
+                                            <Button @click="processShow = true">添加评审专家</Button>
+                                            <Button style="margin-left: 20px;" @click="workPro = true">自动分配评审作品</Button>
+                                            <Button style="float: right;" @click="ruleDrawer = true">评审规则</Button>
                                         </div>
+                                        <Table :columns="processColumns" :data="processList">
+                                            <template #process="{}">
+                                                <Progress :percent="25" :stroke-width="10" />
+                                            </template>
+                                            <template #actions="{row, index}">
+                                                <Button type="error" size="small" @click="deleteApplica(row, index, 'process')">删除</Button>
+                                            </template>
+                                        </Table>
                                     </div>
-                                    <Table :columns="scoreColumns" :data="scoreList" height="600">
-                                        <template #score1="{row}">
-                                            <div v-show="!row.edit">
-                                                <span>{{ row.score1 }}</span>
-                                                <Icon type="md-nutrition" @click="row.edit = true" />
-                                            </div>
-                                            <div v-show="row.edit">
-                                                <InputNumber :min="0" v-model="row.score1" />
-                                                <Icon type="md-checkmark-circle" @click="row.edit = false" />
-                                                <Icon type="md-close-circle" @click="row.edit = false" />
+                                </div>
+                                <div class="data-box">
+                                    <div class="module-title">成绩统计</div>
+                                    <div class="module-data">
+                                        <div class="tab-header">
+                                            <div v-show="!awardsing">
+                                                <Button @click="setAwards()">设置奖项</Button>
+                                                <Button @click="openAch()" style="margin-left: 20px;">公示成绩</Button>
                                             </div>
-                                        </template>
-                                        <template #awards="{row}">
-                                            <span v-if="!awardsing">{{ row.awards ? row.awards : '-' }}</span>
-                                            <div v-else>
-                                                <Select v-model="row.awards" style="width:200px" :transfer="true">
-                                                    <Option v-for="item in awardsList" :value="item.value" :key="item.value">{{ item.label }}</Option>
-                                                </Select>
-                                                <Icon type="md-add-circle" @click="addAward = true" />
+                                            <div v-show="awardsing">
+                                                <Button @click="awardsShow = true">批量设置</Button>
+                                                <Button @click="awardTypes()" style="margin-left: 20px;">取消</Button>
                                             </div>
-                                        </template>
-                                    </Table>
+                                        </div>
+                                        <Table :columns="scoreColumns" :data="scoreList" height="600">
+                                            <template #score1="{row}">
+                                                <div v-show="!row.edit">
+                                                    <span>{{ row.score1 }}</span>
+                                                    <Icon type="md-nutrition" @click="row.edit = true" />
+                                                </div>
+                                                <div v-show="row.edit">
+                                                    <InputNumber :min="0" v-model="row.score1" />
+                                                    <Icon type="md-checkmark-circle" @click="row.edit = false" />
+                                                    <Icon type="md-close-circle" @click="row.edit = false" />
+                                                </div>
+                                            </template>
+                                            <template #awards="{row}">
+                                                <span v-if="!awardsing">{{ row.awards ? row.awards : '-' }}</span>
+                                                <div v-else>
+                                                    <Select v-model="row.awards" style="width:200px" :transfer="true">
+                                                        <Option v-for="item in awardsList" :value="item.value" :key="item.value">{{ item.label }}</Option>
+                                                    </Select>
+                                                    <Icon type="md-add-circle" @click="addAward = true" />
+                                                </div>
+                                            </template>
+                                        </Table>
+                                    </div>
                                 </div>
-                            </div>
+                            </template>
                         </div>
                     </vuescroll>
                 </TabPane>
@@ -589,11 +597,11 @@ export default {
             this.sasData.sas = this.$store.state.user.userProfile.areas.find(item => {
                 return item.areaId === this.actInfo.owner
             }).sas
+            this.getInviteTea()
         } else {
             this.sasData.sas = this.$store.state.user.schoolProfile.blob_sas
         }
         this.getActInfo()
-        this.getInviteTea()
     },
     computed: {
         actInfo() {
@@ -647,6 +655,28 @@ export default {
                 }
             })
         },
+        delAct() {
+            this.$Modal.confirm({
+                title: '确定删除本次活动吗?',
+                // content: `确定删除本次活动吗?`,
+                onOk: () => {
+                    let params = {
+                        grant_type: 'delete',
+                        activityId: this.actInfo.id,
+                        owner: this.actInfo.owner,
+                        scope: this.actInfo.scope,
+                    }
+                    this.$api.areaActivity.manageAct(params).then(res => {
+                        if(res.code === 201) {
+                            this.$Message.success('删除成功')
+                            this.$router.go(-1)
+                        } else if(res.code === 200) {
+                            this.$Message.warning('删除失败')
+                        }
+                    })
+                }
+            })
+        },
         deleteApplica(data, index, type) {
             console.log(data, index);
             if(type === 'join') {
@@ -728,9 +758,11 @@ export default {
         }
         .btn-box {
             position: absolute;
-            top: 12px;
+            /* top: 12px;
             right: 5px;
-            width: 320px;
+            width: 320px; */
+            top: 0;
+            right: 20px;
         }
     }
 

+ 171 - 81
TEAMModelOS/ClientApp/src/view/signupActivity/infoReleased.vue

@@ -1,6 +1,6 @@
 <template>
-    <div>
-        <div v-if="actInfo">
+    <div class="info-released">
+        <div v-if="actInfo" style="height: 100%;">
             <div class="info-header">
                 <div @click="$router.go(-1)">
                     <Icon type="ios-arrow-back" />
@@ -12,9 +12,16 @@
                     </span>
                     {{ actInfo.name }}
                 </p>
-                <div class="btn-box">
-                    <Button type="success" size="small" @click="publishNow()">立即发布</Button>
-                    <Button type="primary" size="small">修改</Button>
+                <div class="btn-box" v-if="actInfo.needConfirmed || !actInfo.publish">
+                    <template v-if="!actInfo.needConfirmed">
+                        <Button type="success" size="small" @click="publishNow()">立即发布</Button>
+                        <Button type="primary" size="small">修改</Button>
+                        <Button type="warning" size="small" @click="delAct()">删除</Button>
+                    </template>
+                    <template v-else>
+                        <Button type="success" size="small" @click="confirmedJoin(1)">加入</Button>
+                        <!-- <Button type="primary" size="small" @click="confirmedJoin(0)">退出</Button> -->
+                    </template>
                 </div>
             </div>
             <div class="info-box">
@@ -49,7 +56,7 @@
                                 <span v-for="(item, index) in actInfo.cb" :key="index" style="margin-right: 10px;">{{ item }}</span>
                             </span>
                         </div>
-                        <div v-if="actInfo.scope === 'area' && isArea">
+                        <div v-if="actInfo.scope != 'school' && isArea">
                             <span>学校:</span>
                             <span>
                                 <span v-for="item in actInfo.invitedSchools" :key="item.id">{{ item.name }}</span>
@@ -210,13 +217,19 @@ export default {
                     label: '学科',
                 },
             ],
-            ruleInfo: undefined,
+            ruleInfo: {
+                name: '',
+                desc: '',
+                trees: [],
+            },
             ruleDrawer: false,
         }
     },
     created () {
         if(this.actInfo) {
             this.getActInfo()
+        } else {
+            this.$router.go(-1)
         }
     },
     computed: {
@@ -278,101 +291,178 @@ export default {
                 }
             })
         },
+        delAct() {
+            this.$Modal.confirm({
+                title: '确定删除本次活动吗?',
+                // content: `确定删除本次活动吗?`,
+                onOk: () => {
+                    let params = {
+                        grant_type: 'delete',
+                        activityId: this.actInfo.id,
+                        owner: this.actInfo.owner,
+                        scope: this.actInfo.scope,
+                    }
+                    this.$api.areaActivity.manageAct(params).then(res => {
+                        if(res.code === 201) {
+                            this.$Message.success('删除成功')
+                            this.$router.go(-1)
+                        } else if(res.code === 200) {
+                            this.$Message.warning('删除失败')
+                        }
+                    })
+                }
+            })
+        },
+        confirmedJoin(type) {
+            this.$Modal.confirm({
+                // title: '删除专家',
+                content: `确认加入本次活动吗?`,
+                onOk: () => {
+                    let params = {
+                        grant_type: 'school-confirm',
+                        activityId: this.actInfo.id,
+                        confirm: type
+                    }
+                    this.$api.areaActivity.manageAct(params).then(res => {
+                        if(res.code === 200) {
+                            this.actInfo.needConfirmed = 0
+                        }
+                    })
+                }
+            })
+        },
     }
 
 }
 </script>
 
 <style lang="less" scoped>
-.info-header {
-    height: 45px;
-    line-height: 45px;
-    box-shadow: 0px 2px 5px #e9e9e9;
-    padding-left: 10px;
-    text-align: center;
-    position: relative;
+.info-released {
+    height: 100%;
+    .info-header {
+        height: 45px;
+        line-height: 45px;
+        box-shadow: 0px 2px 5px #e9e9e9;
+        padding-left: 10px;
+        text-align: center;
+        position: relative;
 
-    &>div:first-of-type{
-        /* display: inline-block;
-        float: left; */
-        cursor: pointer;
-        position: absolute;
-        top: 0;
-        left: 10px;
+        &>div:first-of-type{
+            /* display: inline-block;
+            float: left; */
+            cursor: pointer;
+            position: absolute;
+            top: 0;
+            left: 10px;
+        }
+        .title{
+            font-size: 18px;
+            font-weight: bold;
+            margin-right: 7%;
+            .type-box{
+                background-color: #a8a8a8;
+                font-size: 15px;
+                font-weight: normal;
+                color: #fff;
+                padding: 3px 6px;
+                border-radius: 5px;
+                margin-right: 5px;
+            }
+        }
+        .btn-box {
+            position: absolute;
+            top: 0;
+            right: 5px;
+            .ivu-btn {
+                margin-right: 10px;
+            }
+        }
     }
-    .title{
-        font-size: 18px;
-        font-weight: bold;
-        margin-right: 7%;
-        .type-box{
-            background-color: #a8a8a8;
-            font-size: 15px;
-            font-weight: normal;
+    .info-box {
+        display: flex;
+        height: 100%;
+
+        .time-border {
+            background-color: #319be7;
+            border-radius: 5px;
+            padding: 2px 10px;
             color: #fff;
-            padding: 3px 6px;
+            font-weight: 100;
+        }
+
+        .info-border {
+            background-color: #8cb553;
             border-radius: 5px;
+            padding: 2px 10px;
+            color: #fff;
             margin-right: 5px;
         }
-    }
-    .btn-box {
-        position: absolute;
-        top: 0;
-        right: 5px;
-        .ivu-btn {
-            margin-right: 10px;
-        }
-    }
-}
-.info-box {
-    display: flex;
 
-    .time-border {
-        background-color: #319be7;
-        border-radius: 5px;
-        padding: 2px 10px;
-        color: #fff;
-        font-weight: 100;
-    }
+        .activity-info {
+            width: 40%;
+            height: 100%;
+            margin: 10px 20px;
+            margin-right: 30px;
+            img {
+                /* width: 100%;
+                min-height: 300px; */
+                width: auto;
+                height: 300px;
+            }
+            &>div {
+                margin: 10px 0;
+                font-size: 16px;
+            }
+        }
 
-    .info-border {
-        background-color: #8cb553;
-        border-radius: 5px;
-        padding: 2px 10px;
-        color: #fff;
-        margin-right: 5px;
-    }
+        .check-module {
+            width: 60%;
+            .sk-title{
+                background-color: #eaeaea;
+                padding: 10px;
+                margin-bottom: 10px;
+                margin-top: 25px;
+                font-weight: bold;
+                border-radius: 10px;
 
-    .activity-info {
-        width: 40%;
-        margin: 10px 20px;
-        margin-right: 30px;
-        img {
-            width: 100%;
-        }
-        &>div {
-            margin: 10px 0;
-            font-size: 16px;
+                &>span:first-child {
+                    margin-right: 15px;
+                }
+            }
+            .sk-info{
+                // padding: 0 10px;
+                &>p {
+                    margin: 10px;
+                }
+            }
         }
     }
+    .create-form {
+        margin: 30px 30px 30px 10px;
+        // height: 100%;
 
-    .check-module {
-        width: 60%;
-        .sk-title{
-            background-color: #eaeaea;
-            padding: 10px;
-            margin-bottom: 10px;
-            margin-top: 25px;
-            font-weight: bold;
-            border-radius: 10px;
+        .tab-check{
+            margin-bottom: 15px;
+
+            .file-box {
+                display: flex;
+                & >label{
+                    width: 100px;
 
-            &>span:first-child {
-                margin-right: 15px;
+                }
             }
         }
-        .sk-info{
-            // padding: 0 10px;
-            &>p {
-                margin: 10px;
+
+        .unit-box {
+            position: relative;
+            .ivu-icon-md-add-circle {
+                position: absolute;
+                top: 6px;
+                right: 0;
+            }
+            .ivu-icon{
+                margin-left: 10px;
+                cursor: pointer;
             }
         }
     }

+ 34 - 16
TEAMModelOS/ClientApp/src/view/signupActivity/processActivity.vue

@@ -4,6 +4,7 @@
             <RadioGroup v-model="processType" type="button" button-style="solid">
                 <Radio :label="-1">全部</Radio>
                 <Radio :label="0">未发布</Radio>
+                <Radio :label="3" v-if="!isArea">未确认</Radio>
                 <Radio :label="1">进行中</Radio>
                 <Radio :label="2">已结束</Radio>
             </RadioGroup>
@@ -29,9 +30,14 @@
                         <p>主办:{{ item.zb.length }}</p>
                         <!-- <p>简介:{{ item.jianjie }}</p> -->
                         <div class="process-type">
-                            <span v-show="!item.publish" style="color: #ec8130;">待发布</span>
-                            <span v-show="item.publish === 1" style="color: #1e9a1e;">进行中</span>
-                            <span v-show="item.publish === 2" style="color: #696969;">已结束</span>
+                            <template v-if="item.needConfirmed">
+                                <span style="color: #305cec;">待确认</span>
+                            </template>
+                            <template v-else>
+                                <span v-show="!item.publish" style="color: #ec8130;">待发布</span>
+                                <span v-show="item.publish === 1" style="color: #1e9a1e;">进行中</span>
+                                <span v-show="item.publish === 2" style="color: #696969;">已结束</span>
+                            </template>
                         </div>
                     </div>
                 </div>
@@ -62,9 +68,14 @@ export default {
             handler(n, o) {
                 if(n === -1) {
                     this.activityListShow = this.activityList
+                } else if(n != 3) {
+                    this.activityListShow = this.activityList.filter(item => {
+                        return item.publish === n && !item.needConfirmed
+                        // return n ? (n === 1 ? item.publish === 1 : item.publish === 2) : item.publish === 0
+                    })
                 } else {
                     this.activityListShow = this.activityList.filter(item => {
-                        return n ? (n === 1 ? item.publish === 1 : item.publish === 2) : item.publish === 0
+                        return item.needConfirmed
                     })
                 }
             },
@@ -82,20 +93,26 @@ export default {
             this.$api.areaActivity.manageAct(params).then(res => {
                 if(res.activities) {
                     let sasData = {sas: ''}
-                    if(this.isArea) {
+                    /* if(this.isArea) {
                         sasData.sas = this.$store.state.user.userProfile.areas.find(item => {
                             return item.areaId === params.areaId
                         }).sas
                     } else {
                         sasData.sas = this.$store.state.user.schoolProfile.blob_sas
-                    }
+                    } */
                     this.activityList = res.activities.map(item => {
                         item.startTime = this.$tools.formatTime(item.stime, 'yyyy-MM-dd')
                         item.endTime = this.$tools.formatTime(item.etime, 'yyyy-MM-dd')
-                        item.poster = !item.poster ? require('@/assets/image/no-poster-cn1.png') : `${item.poster}?${sasData.sas}`
+                        item.poster = !item.poster ? require('@/assets/image/no-poster-cn1.png') : `${item.poster}?${item.sas}`
                         item.attachment.forEach(attach => {
-                            attach.url = `${attach.url}?${sasData.sas}`
+                            attach.url = `${attach.url}?${item.sas}`
                         })
+                        // 校级确认参加: confirmedSchools[0].status = 0(没确认)/1(已确认)
+                        // 区级都需要确认,公开只有选择了学校才需确认
+                        if(!this.isArea && ((item.scope === 'public' && item.invitedSchools.length) || item.scope === 'area')) {
+                            item.needConfirmed = !item.confirmedSchools[0].status
+                            // item.confirmed = item.confirmedSchools[0].status
+                        }
                         return item
                     })
                     this.activityListShow = this.activityList
@@ -103,14 +120,8 @@ export default {
             })
         },
         goInfo(data) {
-            if(data.publish) {
-                this.$router.push({
-                    name: this.isArea ? 'areaInfoActivity' : 'infoActivity',
-                    params: {
-                        info: data
-                    }
-                })
-            } else {
+            // 待确认页面 === 待发布页面
+            if(!data.publish || data.needConfirmed) {
                 this.$router.push(
                 {
                     name: this.isArea ? 'areaInfoProcess' : 'infoProcess',
@@ -118,6 +129,13 @@ export default {
                         info: data
                     },
                 })
+            } else {
+                this.$router.push({
+                    name: this.isArea ? 'areaInfoActivity' : 'infoActivity',
+                    params: {
+                        info: data
+                    }
+                })
             }
         },
         createActivity() {

+ 177 - 21
TEAMModelOS/ClientApp/src/view/signupActivity/setActivity.vue

@@ -2,21 +2,71 @@
     <div class="setting">
         <Tabs v-model="activeName">
             <TabPane label="分站管理" name="subs">
-                <Form :model="formItem" :label-width="120" style="width: 98%;">
-                    <FormItem label="授权单位/组织">
-                        <Input v-model="formItem.unit" placeholder="请输入..." :disabled="!editType"></Input>
-                    </FormItem>
-                    <FormItem label="分站代码">
-                        <Input v-model="formItem.code" placeholder="请输入..." :disabled="!editType"></Input>
-                    </FormItem>
-                    <FormItem label="分站路由">
-                        <Input v-model="formItem.route" placeholder="请输入..." disabled></Input>
-                    </FormItem>
-                    <FormItem>
-                        <Button @click="editType = true" v-show="!editType">更改</Button>
-                        <Button type="primary" @click="editType = false" v-show="editType">保存</Button>
-                    </FormItem>
-                </Form>
+                <!-- 醍摩豆学区可以查看所有区级路由 -->
+                <div v-if="isTMD" style="margin-bottom: 50px;">
+                    <Table :columns="websiteColumn" :data="websiteList" class="banner-set">
+                        <template #route="{row}">
+                            <span v-if="row.route">/{{ row.route }}</span>
+                            <span v-else>-</span>
+                        </template>
+                        <!-- <template #code="{row}">
+                            <p v-if="row.isEdit">
+                                <Input v-model="row.code" placeholder="default size" />
+                                <span>
+                                    <Icon size="20" color="#78ba20" type="md-checkmark-circle" />
+                                    <Icon size="20" color="#8f8f8f" type="md-close-circle" />
+                                </span>
+                            </p>
+                            <span v-else>{{ row.code }}</span>
+                        </template> -->
+                        <template #name="{row}">
+                            <div v-if="row.isEdit" class="website-name-edit">
+                                <Input v-model="row.name" placeholder="default size" style="width: 80%" />
+                                <!-- <div>
+                                    <Icon size="20" color="#78ba20" type="md-checkmark-circle" />
+                                    <Icon size="20" color="#8f8f8f" type="md-close-circle" />
+                                </div> -->
+                            </div>
+                            <span v-else>
+                                {{ row.name }}
+                                <Icon type="md-contact" v-if="areaId === row.id" />
+                            </span>
+                        </template>
+                        <template #allowPublic="{row}">
+                            <el-switch v-model="row.allowPublic" :disabled="!row.isEdit || areaId === row.id" active-text="允许" inactive-text="禁止" :active-value="1" :inactive-value="0" active-color="#13ce66" />
+                        </template>
+                        <template #action="{row, index}">
+                            <div v-if="!row.isEdit">
+                                <Button type="primary" size="small" @click="row.isEdit = true">编辑</Button>
+                                <!-- <Button type="error" size="small">删除</Button> -->
+                            </div>
+                            <div v-else>
+                                <Button type="success" size="small" @click="saveWebsit(row, true)" style="margin-right: 10px;">保存</Button>
+                                <Button size="small" @click="cancelEdit(row, index)" class="cancel-edit">取消</Button>
+                            </div>
+                        </template>
+                    </Table>
+                </div>
+                <template v-else>
+                    <Form :model="websitInfo" :label-width="120" style="width: 98%;" v-if="websitInfo">
+                        <FormItem label="授权单位/组织">
+                            <Input v-model="websitInfo.name" placeholder="请输入..." :disabled="!editType"></Input>
+                        </FormItem>
+                        <!-- <FormItem label="分站代码">
+                            <Input v-model="websitInfo.code" placeholder="请输入..." :disabled="!editType"></Input>
+                        </FormItem> -->
+                        <FormItem label="分站路由">
+                            <Input v-model="websitInfo.route" placeholder="暂无路由" disabled></Input>
+                        </FormItem>
+                        <FormItem>
+                            <Button @click="editType = true" v-show="!editType">更改</Button>
+                            <div v-show="editType">
+                                <Button type="primary" @click="saveWebsit(websitInfo, false)">保存</Button>
+                                <Button @click="cancelInfo()" class="cancel-edit">取消</Button>
+                            </div>
+                        </FormItem>
+                    </Form>
+                </template>
             </TabPane>
             <TabPane label="首页管理" name="homepage">
                 <Button @click="bannerShow = true">新增</Button>
@@ -33,7 +83,7 @@
                     <template #image="{row}">
                         <img :src="row.image" alt="">
                     </template>
-                    <template #action="{row}">
+                    <template #action="{}">
                         <Button type="error" size="small">删除</Button>
                     </template>
                 </Table>
@@ -53,11 +103,42 @@ export default {
     data () {
         return {
             activeName: 'subs',
-            formItem: {
-                unit: '醍摩豆(成都)信息技术有限公司',
-                code: 'TMD',
-                route: '/TeamModel',
-            },
+            websitInitial: undefined,
+            websitInfo: undefined,
+            websiteColumn: [
+                {
+                    title: '分站路由',
+                    slot: 'route',
+                    align: 'center',
+                },
+                /* {
+                    title: '分站代码',
+                    slot: 'code',
+                    align: 'center',
+                    // width: '100',
+                }, */
+                {
+                    title: '授权单位/组织',
+                    slot: 'name',
+                    align: 'center',
+                    // width: '200',
+                },
+                {
+                    title: '发布公开活动',
+                    slot: 'allowPublic',
+                    align: 'center',
+                    // width: '100',
+                },
+                {
+                    title: '操作',
+                    slot: 'action',
+                    width: '300',
+                    align: 'center',
+                }
+            ],
+            websiteInitial: [], //未被操作的数据
+            websiteList: [], //页面展示的数据
+
             columns: [
                 {
                     title: '活动名称',
@@ -126,7 +207,67 @@ export default {
             awardsSel: [],
         }
     },
+    computed: {
+        isTMD() { //是醍摩豆智慧学区
+            return sessionStorage.getItem('areaId') === '02944f32-f534-3397-ea56-e6f1fc6c3714'
+        },
+        areaId() {
+            return sessionStorage.getItem('areaId')
+        },
+    },
+    created () {
+        this.getWebsite()
+    },
     methods: {
+        getWebsite() {
+            let params = {
+                grant_type: 'list',
+                websiteId: this.areaId
+            }
+            this.$api.areaActivity.websiteManage(params).then(res => {
+                if(res.code === 200) {
+                    if(!this.isTMD) {
+                        this.websitInfo = res.websites[0]
+                        this.websitInitial = this._.cloneDeep(this.websitInfo)
+                    } else {
+                        this.websiteList = res.websites.map(item => {
+                            item.isEdit = false
+                            return item
+                        })
+                        this.websiteInitial = this._.cloneDeep(this.websiteList)
+                    }
+                }
+            })
+        },
+        saveWebsit(row, isTable) {
+            let params = {
+                grant_type: 'update',
+                areaId: this.areaId,
+                website: row,
+            }
+            this.$api.areaActivity.websiteManage(params).then(res => {
+                if(res.code === 200) {
+                    this.$Message.success('更新成功')
+                    if(isTable) {
+                        row.isEdit = false
+                    } else {
+                        this.editType = false
+                    }
+                } else {
+                    this.$Message.warning('更新失败')
+                }
+            })
+        },
+        cancelEdit(row, index) {
+            row.name = this.websiteInitial[index].name
+            row.allowPublic = this.websiteInitial[index].allowPublic
+            row.isEdit = false
+        },
+        cancelInfo() {
+            this.websitInfo.name = this.websitInitial.name
+            this.websitInfo.allowPublic = this.websitInitial.allowPublic
+            this.editType = false
+        },
         addBanner() {
             this.bannnerdata.push(this.awardsList[this.awardsSel])
         },
@@ -143,6 +284,21 @@ export default {
         text-align: center;
         padding: 5px 0;
     }
+    .website-name-edit {
+        display: flex;
+        align-items: center;
+        .ivu-icon {
+            margin-left: 10px;
+        }
+    }
+    .cancel-edit {
+        background-color: #959595;
+        border-color: #959595;
+        color: #fff;
+        &:hover {
+            border-color: #959595;
+        }
+    }
 }
 </style>
 <style lang="less">

+ 55 - 37
TEAMModelOS/ClientApp/src/view/student-web/AppNew.vue

@@ -57,59 +57,77 @@
                         <span class="no-show" v-show="MyNo != 2">{{ $t('studentWeb.course') }}</span>
                     </MenuItem> -->
                     <!-- 课堂记录 -->
-                    <MenuItem name="10" to="/studentWeb/classRecord" :title="$t('studentWeb.courseContent.classRecord')"
-                            v-show="getNowCourse && getNowCourse.id && selectClass && !onlySystem && getNowCourse.subject && !isScale"
-                    >
-                        <svg-icon icon-class="course" class="tabIcon1" />
-                        <span class="no-show" v-show="MyNo != 10">{{ $t('studentWeb.courseContent.classRecord') }}</span>
-                    </MenuItem>
+                    <Tooltip :content="$t('studentWeb.courseContent.classRecord')">
+                        <MenuItem name="10" to="/studentWeb/classRecord"
+                                v-show="getNowCourse && getNowCourse.id && selectClass && !onlySystem && getNowCourse.subject && !isScale"
+                        >
+                            <svg-icon icon-class="course" class="tabIcon1" />
+                            <span class="no-show" v-show="MyNo != 10">{{ $t('studentWeb.courseContent.classRecord') }}</span>
+                            
+                        </MenuItem>
+                    </Tooltip>
                     <!-- 评测 -->
-                    <MenuItem name="5" to="/studentWeb/examView" :title="$t('studentWeb.type.exam')" v-show="selectClass">
-                        <svg-icon icon-class="test" class="tabIcon3" />
-                        <span class="no-show" v-show="MyNo != 5">{{ $t('studentWeb.type.exam') }}</span>
-                    </MenuItem>
+                    <Tooltip :content="$t('studentWeb.type.exam')">
+                        <MenuItem name="5" to="/studentWeb/examView" v-show="selectClass">
+                            <svg-icon icon-class="test" class="tabIcon3" />
+                            <span class="no-show" v-show="MyNo != 5">{{ $t('studentWeb.type.exam') }}</span>
+                        </MenuItem>
+                    </Tooltip>
                     <!-- 作业 -->
-                    <MenuItem name="6" to="/studentWeb/homeworkView" :title="$t('studentWeb.type.homework')" v-show="selectClass && !isScale">
-                        <svg-icon icon-class="doc" class="tabIcon3" />
-                        <span class="no-show" v-show="MyNo != 6">{{ $t('studentWeb.type.homework') }}</span>
-                    </MenuItem>
+                    <Tooltip :content="$t('studentWeb.type.homework')">
+                        <MenuItem name="6" to="/studentWeb/homeworkView" v-show="selectClass && !isScale">
+                            <svg-icon icon-class="doc" class="tabIcon3" />
+                            <span class="no-show" v-show="MyNo != 6">{{ $t('studentWeb.type.homework') }}</span>
+                        </MenuItem>
+                    </Tooltip>
                     <!-- <MenuItem name="9" to="/studentWeb/studyView" :title="$t('studentWeb.type.studyview')">
                         <svg-icon icon-class="note" class="tabIcon2" />
                         <span class="no-show" v-show="MyNo != 9">{{ $t('studentWeb.type.studyview') }}</span>
                     </MenuItem> -->
                     <!-- 活动 -->
-                    <MenuItem name="3" to="/studentWeb/eventView" :title="$t('studentWeb.type.activity')" v-show="!isScale">
-                        <svg-icon icon-class="selflearning" class="tabIcon4" />
-                        <span class="no-show" v-show="MyNo != 3">{{ $t('studentWeb.type.activity') }}</span>
-                    </MenuItem>
+                    <Tooltip :content="$t('studentWeb.type.activity')">
+                        <MenuItem name="3" to="/studentWeb/eventView" v-show="!isScale">
+                            <svg-icon icon-class="selflearning" class="tabIcon4" />
+                            <span class="no-show" v-show="MyNo != 3">{{ $t('studentWeb.type.activity') }}</span>
+                        </MenuItem>
+                    </Tooltip>
                     <!-- 错题本(搭配学校卖出,限制学生才能使用) -->
                     <!-- 先放开醍摩豆登录限制,方便老师体验试用 -->
-                    <!-- <MenuItem v-if="userInfo.scope === 'student'" name="8" to="/studentWeb/practice/wrongQues" :title="$t('studentWeb.type.wrongTopic')" -->
-                    <MenuItem name="8" to="/studentWeb/practice/wrongQues" :title="$t('studentWeb.type.wrongTopic')"
-                            v-show="getNowCourse && getNowCourse.id && selectClass && !onlySystem && getNowCourse.subject && !isScale">
-                        <Icon custom="iconfont icon-cuotiji" size="17" class="tabIcon1" />
-                        <span class="no-show" v-show="MyNo != 8">{{ $t('studentWeb.type.wrongTopic') }}</span>
-                    </MenuItem>
-                    <MenuItem name="14" to="/studentWeb/discussionBoard" :title="$t('studentWeb.type.discussionBoard')"
-                            v-show="getNowCourse && getNowCourse.id && selectClass && !onlySystem && getNowCourse.subject && !isScale">
-                        <Icon type="ios-chatboxes-outline" size="18" style="font-weight: bold" />
-                        <span class="no-show" v-show="MyNo != 14">{{ $t('studentWeb.type.discussionBoard') }}</span>
-                    </MenuItem>
+                    <Tooltip :content="$t('studentWeb.type.wrongTopic')">
+                        <!-- <MenuItem v-if="userInfo.scope === 'student'" name="8" to="/studentWeb/practice/wrongQues" :title="$t('studentWeb.type.wrongTopic')" -->
+                        <MenuItem name="8" to="/studentWeb/practice/wrongQues"
+                                v-show="getNowCourse && getNowCourse.id && selectClass && !onlySystem && getNowCourse.subject && !isScale">
+                            <Icon custom="iconfont icon-cuotiji" size="17" class="tabIcon1" />
+                            <span class="no-show" v-show="MyNo != 8">{{ $t('studentWeb.type.wrongTopic') }}</span>
+                        </MenuItem>
+                    </Tooltip>
+                    <!-- 讨论区 -->
+                    <Tooltip :content="$t('studentWeb.type.discussionBoard')">
+                        <MenuItem name="14" to="/studentWeb/discussionBoard"
+                                v-show="getNowCourse && getNowCourse.id && selectClass && !onlySystem && getNowCourse.subject && !isScale">
+                            <Icon type="ios-chatboxes-outline" size="18" style="font-weight: bold" />
+                            <span class="no-show" v-show="MyNo != 14">{{ $t('studentWeb.type.discussionBoard') }}</span>
+                        </MenuItem>
+                    </Tooltip>
                     <!-- 精准练习 -->
                     <!-- <MenuItem name="12" to="/studentWeb/practice/preciseQues" :title="$t('studentWeb.type.wrongTopic1')" v-show="selectClass && !onlySystem && getNowCourse.subject">
                         <Icon custom="iconfont icon-cuotiji" size="17" class="tabIcon1" />
                         <span class="no-show" v-show="MyNo != 12">{{ $t('studentWeb.type.wrongTopic1') }}</span>
                     </MenuItem> -->
                     <!-- 成绩 -->
-                    <MenuItem name="7" to="/studentWeb/achievement" :title="$t('studentWeb.type.achievement')" v-show="selectClass && !isScale">
-                        <Icon custom="iconfont icon-chengjitongji" size="18" style="font-weight: bold" />
-                        <span class="no-show" v-show="MyNo != 7">{{ $t('studentWeb.type.achievement') }}</span>
-                    </MenuItem>
+                    <Tooltip :content="$t('studentWeb.type.achievement')">
+                        <MenuItem name="7" to="/studentWeb/achievement" v-show="selectClass && !isScale">
+                            <Icon custom="iconfont icon-chengjitongji" size="18" style="font-weight: bold" />
+                            <span class="no-show" v-show="MyNo != 7">{{ $t('studentWeb.type.achievement') }}</span>
+                        </MenuItem>
+                    </Tooltip>
                     <!-- 首页 -->
-                    <MenuItem name="1" to="/studentWeb/homeView" :title="$t('studentWeb.type.home')">
-                        <svg-icon icon-class="home" class="tabIcon2" />
-                        <span class="no-show" v-show="MyNo != 1">{{ $t('studentWeb.type.home') }}</span>
-                    </MenuItem>
+                    <Tooltip :content="$t('studentWeb.type.home')">
+                        <MenuItem name="1" to="/studentWeb/homeView">
+                            <svg-icon icon-class="home" class="tabIcon2" />
+                            <span class="no-show" v-show="MyNo != 1">{{ $t('studentWeb.type.home') }}</span>
+                        </MenuItem>
+                    </Tooltip>
                 </Menu>
                 <!-- 头像 -->
                 <div class="profile-dropdown">

+ 15 - 15
TEAMModelOS/Controllers/Both/ScoreCalcController.cs

@@ -2586,14 +2586,14 @@ namespace TEAMModelOS.Controllers
 
                 // 先排序傳進來的參數 aesc
                 List<sortItem> sortItemsrq = (from e in updateActItemSortRq.sortItems
-                                              orderby e.sort
-                                              select e).ToList();
+                                                orderby e.sort
+                                                select e).ToList();
 
                 // 先判斷是否為課堂紀錄 根據項目的類別需要調整
                 if (scoreCalcActivityBase.type == "lessonrecord")
                 {
                     ScoreCalcLsRecord scoreCalcLsRecordDB = await clientTeacher.ReadItemAsync<ScoreCalcLsRecord>(updateActItemSortRq.scoreCalcActId.ToString(), new PartitionKey($"ScoreCalcAct-{teammodelId}"));
-
+                    
                     //存放排序後的參數
                     List<List<double>> up_stuActAttendOrgVals = new();
                     List<List<double>> up_stuActAttendScores = new();
@@ -2604,11 +2604,11 @@ namespace TEAMModelOS.Controllers
                     List<double> up_itemRates = new();
 
                     //依照傳進來的id 順序修改 分數 比重 項目等順序
-                    for (int i = 0; i < sortItemsrq.Count; i++)
+                    for (int i = 0; i < sortItemsrq.Count; i++) 
                     {
                         for (int j = 0; j < scoreCalcLsRecordDB.items.Count; j++)
                         {
-                            if (sortItemsrq[i].id == scoreCalcLsRecordDB.items[j].id)
+                            if (sortItemsrq[i].id == scoreCalcLsRecordDB.items[j].id) 
                             {
                                 up_stuActAttendOrgVals.Add(scoreCalcLsRecordDB.stuActAttendOrgVals[j]);
                                 up_stuActAttendScores.Add(scoreCalcLsRecordDB.stuActAttendScores[j]);
@@ -2617,7 +2617,7 @@ namespace TEAMModelOS.Controllers
                                 up_stuActItactOrgVals.Add(scoreCalcLsRecordDB.stuActItactOrgVals[j]);
                                 up_stuActItactScores.Add(scoreCalcLsRecordDB.stuActItactScores[j]);
                                 up_itemRates.Add(scoreCalcLsRecordDB.itemRates[j]);
-                                scoreCalcLsRecordDB.items[j].sort = sortItemsrq[i].sort;
+                                scoreCalcLsRecordDB.items[j].sort = sortItemsrq[i].sort;                                
                             }
                         }
 
@@ -2631,17 +2631,17 @@ namespace TEAMModelOS.Controllers
                     scoreCalcLsRecordDB.itemRates = up_itemRates;
                     // 按照更新後的sort排序
                     scoreCalcLsRecordDB.items = (from e in scoreCalcLsRecordDB.items
-                                                 orderby e.sort
-                                                 select e).ToList();
-
+                                                  orderby e.sort
+                                                  select e).ToList();
+                    
                     scoreCalcLsRecordDB = await clientTeacher.ReplaceItemAsync(scoreCalcLsRecordDB, $"{scoreCalcLsRecordDB.id}", new PartitionKey(scoreCalcLsRecordDB.code));
                 }
                 else
                 {
                     ScoreCalcActivity scoreCalcActivityDB = await clientTeacher.ReadItemAsync<ScoreCalcActivity>(updateActItemSortRq.scoreCalcActId.ToString(), new PartitionKey($"ScoreCalcAct-{teammodelId}"));
-
+                    
                     //存放排序後的參數
-                    List<List<double>> up_stuActScores = new();
+                    List<List<double>> up_stuActScores =new();
                     List<List<double>> up_stuActScoresOrg = new();
                     List<double> up_itemRates = new();
 
@@ -2664,9 +2664,9 @@ namespace TEAMModelOS.Controllers
                     scoreCalcActivityDB.itemRates = up_itemRates;
                     // 按照更新後的sort排序
                     scoreCalcActivityDB.items = (from e in scoreCalcActivityDB.items
-                                                 orderby e.sort
-                                                 select e).ToList();
-
+                                                  orderby e.sort
+                                                  select e).ToList();
+                    
                     scoreCalcActivityDB = await clientTeacher.ReplaceItemAsync(scoreCalcActivityDB, $"{scoreCalcActivityDB.id}", new PartitionKey(scoreCalcActivityDB.code));
                 }
 
@@ -2684,7 +2684,7 @@ namespace TEAMModelOS.Controllers
                 return BadRequest();
             }
         }
-
+        
 
         /// <summary>
         /// (十七)查詢項目屬性資料

+ 277 - 16
TEAMModelOS/Controllers/Common/ActivityController.cs

@@ -33,6 +33,12 @@ using TEAMModelOS.SDK.Models.Service;
 using DocumentFormat.OpenXml.Office2010.Excel;
 using TEAMModelOS.Services;
 using OfficeOpenXml.FormulaParsing.LexicalAnalysis;
+using Azure.Storage.Sas;
+using DocumentFormat.OpenXml.Bibliography;
+using System.Runtime.Intrinsics.X86;
+using Microsoft.IdentityModel.Tokens;
+using TEAMModelOS.SDK.Models.Dtos;
+using System.Net.Http;
 
 namespace TEAMModelOS.Controllers
 {
@@ -513,7 +519,7 @@ namespace TEAMModelOS.Controllers
                             if (response.Status==200)
                             {
                                 Activity activity = JsonDocument.Parse(response.Content).RootElement.ToObject<Activity>();
-                                if (_scope.GetString().Equals("school")  && _owner.Equals(school))
+                                if (_scope.GetString().Equals("school")  && _owner.GetString().Equals(school))
                                 {
                                     await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Common).DeleteItemStreamAsync(activity.id, new PartitionKey("Activity"));
                                     return Ok(new { code = 201, activity }) ; //删除成功
@@ -543,6 +549,10 @@ namespace TEAMModelOS.Controllers
                             }
                             string sql = $"select value c from c where c.owner='{_areaId}' {yearSql}  ";
                             var result = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Common).GetList<Activity>(sql, "Activity");
+                            result.list.ForEach(z => {
+                                var (blob_uri, blob_sas) = _azureStorage.GetBlobContainerSAS(z.owner,  BlobContainerSasPermissions.Read | BlobContainerSasPermissions.List);
+                                z.sas=blob_sas;
+                            });
                             return Ok(new { activities = result.list.OrderByDescending(z => z.stime) });
                         }
                     //校级活动列表
@@ -628,6 +638,10 @@ namespace TEAMModelOS.Controllers
                                     var resultSchool = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Common).GetList<Activity>(sqlSchool, "Activity");
                                     activities.AddRange(resultSchool.list);
                                 }
+                                activities.ForEach(z => {
+                                    var (blob_uri, blob_sas) = _azureStorage.GetBlobContainerSAS(z.owner, BlobContainerSasPermissions.Read | BlobContainerSasPermissions.List);
+                                    z.sas=blob_sas;
+                                });
                                 return Ok(new { activities = activities.OrderByDescending(z => z.stime) });
                             }
                             break;
@@ -670,7 +684,7 @@ namespace TEAMModelOS.Controllers
 
                                 if (!string.IsNullOrWhiteSpace(schoolIdIn))
                                 {   //部分学校
-                                    string sqlSchool = $"select value c from c join i in c.confirmedSchools  where c.scope='public' and c.joinMode='enroll'  and (c.publish=1 or c.publish=2 )  and s.status=1 {yearSql}  {schoolIdIn}  ";
+                                    string sqlSchool = $"select value c from c join i in c.confirmedSchools  where c.scope='public' and c.joinMode='enroll'  and (c.publish=1 or c.publish=2 )  and i.status=1 {yearSql}  {schoolIdIn}  ";
                                     var resultSchool = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Common).GetList<Activity>(sqlSchool, "Activity");
                                     activities.AddRange(resultSchool.list);
                                 }
@@ -686,7 +700,7 @@ namespace TEAMModelOS.Controllers
                             //获取所有区级的
                             if (!string.IsNullOrWhiteSpace(areaOwnerIn) && !string.IsNullOrEmpty(schoolIdIn)) {
                                 
-                                string sqlOpen = $"select value c from c join i  in c.confirmedSchools where c.scope='area'  and c.joinMode='enroll' and (c.publish=1 or c.publish=2 ) and s.status=1 {yearSql}  {areaOwnerIn}   {schoolIdIn} ";
+                                string sqlOpen = $"select value c from c join i  in c.confirmedSchools where c.scope='area'  and c.joinMode='enroll' and (c.publish=1 or c.publish=2 ) and i.status=1 {yearSql}  {areaOwnerIn}   {schoolIdIn} ";
                                 var resultOpen = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Common).GetList<Activity>(sqlOpen, "Activity");
                                 activities.AddRange(resultOpen.list);
                             }
@@ -698,6 +712,11 @@ namespace TEAMModelOS.Controllers
                                 var resultSchool = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Common).GetList<Activity>(sqlSchool, "Activity");
                                 activities.AddRange(resultSchool.list);
                             }
+
+                            activities.ForEach(z => {
+                                var (blob_uri, blob_sas) = _azureStorage.GetBlobContainerSAS(z.owner,  BlobContainerSasPermissions.Read | BlobContainerSasPermissions.List);
+                                z.sas=blob_sas;
+                            });
                             return Ok(new { activities = activities.OrderByDescending(z => z.stime) });
                         }
                     //读取优课评选模块及评审规则
@@ -970,6 +989,174 @@ namespace TEAMModelOS.Controllers
                             }
                             return Ok(new { inviteTeachers });
                         }
+                    //导入评审专家
+                    case bool when $"{grant_type}".Equals("add-remove-experts", StringComparison.OrdinalIgnoreCase):
+                        {
+                            if (!request.TryGetProperty("activityId", out JsonElement _activityId)) return BadRequest();
+                            if (!request.TryGetProperty("experts", out JsonElement _experts) || !_experts.ValueKind.Equals(JsonValueKind.Array)) { return BadRequest(); }
+                            List<Expert> experts = _experts.ToObject<List<Expert>>();
+                            var tmdids = experts.Where(x => !string.IsNullOrWhiteSpace(x.tmdid)).Select(z => z.tmdid);
+                            var phones = experts.Where(x => !string.IsNullOrWhiteSpace(x.mobile)).Select(z => z.mobile);
+                            var emails = experts.Where(x => !string.IsNullOrWhiteSpace(x.email)).Select(z => z.email);
+                            List<string> keys = new List<string>();
+                            if (tmdids.Any())
+                            {
+                                keys.AddRange(tmdids);
+                            }
+                            if (phones.Any())
+                            {
+                                keys.AddRange(phones);
+                            }
+                            if (emails.Any())
+                            {
+                                keys.AddRange(emails);
+                            }
+                            ActivityExpert activityExpert = null;
+                            experts.ForEach(x => { x.status = 0;  x.iname = x.name; x.name = null; });
+                            List<CoreUser> coreUsers = new List<CoreUser>();
+                            if (keys.Any())
+                            {
+                                try
+                                {
+                                    var content = new StringContent(keys.ToJsonString(), Encoding.UTF8, "application/json");
+                                    string json = await _coreAPIHttpService.GetUserInfos(content);
+                                    if (!string.IsNullOrWhiteSpace(json))
+                                    {
+                                        coreUsers = json.ToObject<List<CoreUser>>();
+                                    }
+                                }
+                                catch (Exception ex)
+                                {
+                                    await _dingDing.SendBotMsg($"{_option.Location},导入名单时,查验key信息错误{ex.Message}\n{ex.StackTrace}\n\n{keys.ToJsonString()}", GroupNames.醍摩豆服務運維群組);
+                                }
+                            }
+                            if (coreUsers.IsNotEmpty())
+                            {
+                                foreach (var t in experts)
+                                {
+                                    if (!string.IsNullOrWhiteSpace(t.tmdid))
+                                    {
+                                        CoreUser coreUser = coreUsers.Find(x => x.id.Equals(t.tmdid));
+                                        if (coreUser != null)
+                                        {
+                                            t.id = coreUser.id;
+                                            t.name = coreUser.name;
+                                            t.picture = coreUser.picture;
+                                            t.tmdid = coreUser.id;
+                                            if (!string.IsNullOrWhiteSpace(coreUser.mobile))
+                                            {
+                                                t.mobile = coreUser.mobile;
+                                            }
+                                            if (!string.IsNullOrWhiteSpace(coreUser.mail))
+                                            {
+                                                t.email = coreUser.mail;
+                                            }
+                                        }
+                                    }
+                                    if (string.IsNullOrWhiteSpace(t.id))
+                                    {
+                                        if (!string.IsNullOrWhiteSpace(t.mobile))
+                                        {
+                                            CoreUser coreUser = coreUsers.Find(x => !string.IsNullOrWhiteSpace(x.mobile) && x.mobile.Equals(t.mobile));
+                                            if (coreUser != null)
+                                            {
+                                                t.id = coreUser.id;
+                                                t.name = coreUser.name;
+                                                t.picture = coreUser.picture;
+                                                t.tmdid = coreUser.id;
+                                                if (!string.IsNullOrWhiteSpace(coreUser.mobile))
+                                                {
+                                                    t.mobile = coreUser.mobile;
+                                                }
+                                                if (!string.IsNullOrWhiteSpace(coreUser.mail))
+                                                {
+                                                    t.email = coreUser.mail;
+                                                }
+                                            }
+                                        }
+                                    }
+                                    if (string.IsNullOrWhiteSpace(t.id))
+                                    {
+                                        if (!string.IsNullOrWhiteSpace(t.email))
+                                        {
+                                            CoreUser coreUser = coreUsers.Find(x => !string.IsNullOrWhiteSpace(x.mail) && x.mail.Equals(t.email));
+                                            if (coreUser != null)
+                                            {
+                                                t.id = coreUser.id;
+                                                t.name = coreUser.name;
+                                                t.picture = coreUser.picture;
+                                                t.tmdid = coreUser.id;
+                                                if (!string.IsNullOrWhiteSpace(coreUser.mobile))
+                                                {
+                                                    t.mobile = coreUser.mobile;
+                                                }
+                                                if (!string.IsNullOrWhiteSpace(coreUser.mail))
+                                                {
+                                                    t.email = coreUser.mail;
+                                                }
+                                            }
+                                        }
+                                    }
+
+                                }
+                                Azure.Response response = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Normal).ReadItemStreamAsync($"{_activityId}", new PartitionKey("ActivityExpert"));
+                                if (response.Status == 200)
+                                {
+                                     activityExpert = JsonDocument.Parse(response.Content).RootElement.Deserialize<ActivityExpert>();
+                                    experts.ForEach(x =>
+                                    {
+                                        Expert tch = null;
+                                        if (string.IsNullOrWhiteSpace(x.id))
+                                        {
+                                            tch = activityExpert.experts.Find(t => !string.IsNullOrWhiteSpace(t.iname) && t.iname.Equals(x.iname));
+                                        }
+                                        else
+                                        {
+                                            tch = activityExpert.experts.Find(t => !string.IsNullOrWhiteSpace(x.id) && !string.IsNullOrWhiteSpace(t.id) && t.id.Equals(x.id));
+                                        }
+                                        if (tch != null)
+                                        {
+                                            tch.status = x.status;
+                                            tch.name = x.name;
+                                            tch.iname = x.iname;
+                                            tch.picture = x.picture;
+                                            tch.mobile = x.mobile;
+                                            tch.tmdid = x.tmdid;
+                                            tch.email = x.email;
+                                            tch.id = x.id;
+                                        }
+                                        else
+                                        {
+                                            activityExpert.experts.Add(new Expert
+                                            {
+                                                status = x.status,
+                                                name = x.name,
+                                                iname = x.iname,
+                                                picture = x.picture,
+                                                mobile = x.mobile,
+                                                tmdid = x.tmdid,
+                                                email = x.email,
+                                                id = x.id,
+                                                title = x.title,
+                                                subjects = x.subjects,
+                                            });
+                                        }
+                                    });
+                                    await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Normal).UpsertItemAsync(activityExpert, new PartitionKey("ActivityExpert"));
+                                }
+                                else
+                                {
+                                      activityExpert = new ActivityExpert { id = $"{_activityId}", code = "ActivityExpert", pk = "ActivityExpert", experts = experts };
+                                    await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Normal).UpsertItemAsync(activityExpert, new PartitionKey("ActivityExpert"));
+                                }
+                            }
+                            else {
+                                  activityExpert = new ActivityExpert { id = $"{_activityId}", code = "ActivityExpert", pk = "ActivityExpert", experts = experts };
+                                await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Normal).UpsertItemAsync(activityExpert, new PartitionKey("ActivityExpert"));
+                            }
+                            return Ok(new { activityExpert});
+                        }
+
                 }
             } catch (Exception ex)
             {
@@ -1015,22 +1202,27 @@ namespace TEAMModelOS.Controllers
                     
                      }
             }
-            if (tmdid == null  && !string.IsNullOrWhiteSpace($"{_token}")) {
+            if (tmdid == null  && !string.IsNullOrWhiteSpace($"{_token}"))
+            {
                 var jwt = new JwtSecurityToken(_token.GetString());
-                tmdid = jwt.Payload.Sub;
-              
-                if (HttpContext.Request.Headers.TryGetValue("lang", out var _lang))
+                if (JwtAuthExtension.ValidateAuthTokenRefresh(_token.GetString(), _option.JwtSecretKey))
                 {
-                    head_lang = $"{_lang}";
+                    tmdid = jwt.Payload.Sub;
+
+                    if (HttpContext.Request.Headers.TryGetValue("lang", out var _lang))
+                    {
+                        head_lang = $"{_lang}";
+                    }
+                    jwt.Payload.TryGetValue("name", out name);
+                    jwt.Payload.TryGetValue("picture", out picture);
+                    jwt.Payload.TryGetValue("lang", out object _jwtlang);
+                    head_lang = !string.IsNullOrWhiteSpace($"{_jwtlang}") ? $"{_jwtlang}" : head_lang;
                 }
-                jwt.Payload.TryGetValue("name", out   name);
-                jwt.Payload.TryGetValue("picture", out   picture);
-                jwt.Payload.TryGetValue("lang", out object _jwtlang);
-                head_lang = !string.IsNullOrWhiteSpace($"{_jwtlang}") ? $"{_jwtlang}" : head_lang;
-            }
-            Teacher teacher = null;
-            teacherInfo = await TeacherService.TeacherInfo(_azureCosmos, teacher, $"{name}", $"{picture}", tmdid, _azureStorage, _option, _azureRedis, ip, _httpTrigger, head_lang);
+                else { return Ok(new { code = 2, msg = "Token验证失败" }); }
 
+            }
+            else { return Ok(new { code = 3, msg = "凭证验证失败" }); }
+            teacherInfo = await TeacherService.TeacherInfoLite(_azureCosmos,  $"{name}", $"{picture}", tmdid, _azureStorage, _option, _azureRedis, ip, _httpTrigger, head_lang);
             string sql = $"select value c from c where c.route='{_route}'";
             var result = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Normal).GetList<ActivityWebsite>(sql, "ActivityWebsite");
             ActivityWebsite website = null;
@@ -1046,10 +1238,79 @@ namespace TEAMModelOS.Controllers
 
                 }
             }
+            List<string > roles= new List<string>() { "teacher" };
+
+            CoreUser coreUser = await _coreAPIHttpService.GetUserInfo(new Dictionary<string, string> { { "key", teacherInfo.teacher.id } }, _option.Location, _configuration);
+            string sqlExpert = $"select value c from c join e in c.experts   where e.id='{teacherInfo.teacher.id}'";
+            if (!string.IsNullOrWhiteSpace(coreUser.mobile)) {
+                sqlExpert=$" {sqlExpert } or  e.mobile='{coreUser.mobile}' ";
 
-            return Ok(new { website, code =200, teacher});
+            }
+            var resultActivityExpert = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Normal).GetList<ActivityExpert>(sqlExpert, "ActivityExpert",pageSize:1);
+
+            if (resultActivityExpert.list.IsNotEmpty()) {
+                roles.Add("expert");
+            }
+            var payload = new JwtPayload {
+                { JwtRegisteredClaimNames.Iss, _option.HostName }, //發行者
+                { JwtRegisteredClaimNames.Sub, teacherInfo.teacher.id }, // 用戶ID                  
+                { JwtRegisteredClaimNames.Exp,DateTimeOffset.UtcNow.AddHours(2).ToUnixTimeSeconds()},  // 到期的時間,必須為數字
+                { "name",name}, // 用戶的顯示名稱
+                { "picture",picture}, // 用戶頭像
+                { "roles", roles}, // 登入者的角色,角色類型 (Admin、Teacher、Student) 
+                { JwtRegisteredClaimNames.Website,website.route},
+            };
+            var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_option.JwtSecretKey));
+            var signingCredentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256Signature);
+            var header = new JwtHeader(signingCredentials);
+            var secToken = new JwtSecurityToken(header, payload);
+            // 產出所需要的 JWT securityToken 物件,並取得序列化後的 Token 結果(字串格式)
+            var tokenHandler = new JwtSecurityTokenHandler();
+            var serializeToken = tokenHandler.WriteToken(secToken);
+            return Ok(new {  code =200,token =serializeToken, schools= teacherInfo.teacher.schools.Where(z=>z.status.Equals("join"))});
         }
 
+
+      
+        /// <summary>
+        /// 
+        /// </summary>
+        /// <param name="request"></param>
+        /// <returns></returns>
+        [ProducesDefaultResponseType]
+        [HttpPost("get-website")]
+        public async Task<IActionResult> GetWebsite(JsonElement request) {
+            if (!request.TryGetProperty("route", out JsonElement _route)) return BadRequest();
+            string sql = $"select value c from c where c.route='{_route}'";
+            var result = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Normal).GetList<ActivityWebsite>(sql, "ActivityWebsite");
+            ActivityWebsite website = null;
+            if (result.list.Count>1)
+            {
+                return Ok(new { code = 1, msg = "路由匹配多个区校" });
+            }
+            else
+            {
+                if (result.list.Count==1)
+                {
+                    website= result.list[0];
+                }
+            }
+            if (website!= null)
+            {
+                List<ActivityWebsite> websites = new List<ActivityWebsite>();
+                if (website.route.Equals("teammodel") )
+                {
+                    string sqlAll = $"select value c from c where IS_DEFINED(c.route) = true  and c.route<> null  and  c.route<>'' ";
+                    var resultAll = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Normal).GetList<ActivityWebsite>(sqlAll, "ActivityWebsite");
+                    websites=resultAll.list;
+                }
+                return Ok(new { code = 200, website ,websites });
+            }
+            else {
+                return Ok(new { code = 2, msg = "未匹配分站" }) ;
+            }
+          
+        }
         /// <summary>
         /// portal站的
         /// </summary>

+ 107 - 16
TEAMModelOS/Controllers/Common/ExamController.cs

@@ -38,6 +38,7 @@ using TEAMModelOS.SDK.Models.Cosmos.Student;
 using DocumentFormat.OpenXml.Drawing.Charts;
 using ClouDASLibx;
 using HTEXLib.Helpers.ShapeHelpers;
+using TEAMModelOS.SDK.Helper.Common.DateTimeHelper;
 
 namespace TEAMModelOS.Controllers
 {
@@ -267,8 +268,14 @@ namespace TEAMModelOS.Controllers
                         List<string> sheetIds = new List<string>();
                         foreach (PaperSimple simple in request.papers)
                         {
-                            simple.blob = $"/exam/{request.id}/paper/{request.subjects[n].id}";
-                            n++;
+                            if (!string.IsNullOrEmpty(simple.subjectId))
+                            {
+                                simple.blob = $"/exam/{request.id}/paper/{simple.subjectId}/{simple.id}";
+                            }
+                            else {
+                                simple.blob = $"/exam/{request.id}/paper/{request.subjects[n].id}";
+                                n++;
+                            }
                             simple.sheet = null;
                         }
                         exam = await client.GetContainer(Constant.TEAMModelOS, "Common").CreateItemAsync(request, new PartitionKey($"{request.code}"));
@@ -3701,17 +3708,18 @@ namespace TEAMModelOS.Controllers
             if (!request.TryGetProperty("subjectId", out JsonElement subjectId)) return BadRequest();
             string code = (request.TryGetProperty("code", out JsonElement codeJobj)) ? codeJobj.GetString() : string.Empty;
             string schCode = (!string.IsNullOrWhiteSpace(code)) ? code : "noschoolid";
-            //取得Redis該學生該科目的錯題數
+            string teacherid = (request.TryGetProperty("teacherId", out JsonElement teacherId)) ? teacherId.GetString() : string.Empty;
+            //取得Redis該學生該科目的錯題數 [算法變更,昨日錯題總數不再計入redis]
             int record = 0; //昨日取得的錯題數
-            var redisClient = _azureRedis.GetRedisClient(8);
-            string hkey = $"ErrorItems:{schCode}";
-            string hval = await redisClient.HashGetAsync(hkey, $"{stuId}");
-            List<stuErrorItemCnt> stuErrCntList = (hval != null) ? JsonSerializer.Deserialize<List<stuErrorItemCnt>>(hval) : new List<stuErrorItemCnt>();
-            stuErrorItemCnt stuErrCnt = stuErrCntList.Where(s => s.subjectId.Equals($"{subjectId}")).FirstOrDefault();
-            if (stuErrCnt != null)
-            {
-                record = stuErrCnt.number;
-            }
+            //var redisClient = _azureRedis.GetRedisClient(8);
+            //string hkey = $"ErrorItems:{schCode}";
+            //string hval = await redisClient.HashGetAsync(hkey, $"{stuId}");
+            //List<stuErrorItemCnt> stuErrCntList = (hval != null) ? JsonSerializer.Deserialize<List<stuErrorItemCnt>>(hval) : new List<stuErrorItemCnt>();
+            //stuErrorItemCnt stuErrCnt = stuErrCntList.Where(s => s.subjectId.Equals($"{subjectId}")).FirstOrDefault();
+            //if (stuErrCnt != null)
+            //{
+            //    record = stuErrCnt.number;
+            //}
             //取得CosmosDB該學生該科目現在錯題數
             int avaliable = 0; //現在取得的錯題數
             List<string> itemIdList = new List<string>();
@@ -3779,14 +3787,97 @@ namespace TEAMModelOS.Controllers
 
             foreach (var obj in dict)
             {
-                if (_azureStorage.GetBlobContainerClient(schCode).GetBlobClient($"{obj.Value}/{obj.Key}.json").Exists())
-                {// 去除blob不存在項目
-                    avaliable++;
+                if (schCode != null && schCode != "noschoolid")
+                {
+                    if (_azureStorage.GetBlobContainerClient(schCode).GetBlobClient($"{obj.Value}/{obj.Key}.json").Exists())
+                    {// 去除blob不存在項目
+                        avaliable++;
+                    }
+                }
+                else if (!string.IsNullOrWhiteSpace(teacherid))
+                {
+                    if (_azureStorage.GetBlobContainerClient(teacherid).GetBlobClient($"{obj.Value}/{obj.Key}.json").Exists())
+                    {
+                        avaliable++;
+                    }
                 }
             }
-
             return Ok(new { record, avaliable });
         }
+
+        //取得錯題庫新增數
+        [ProducesDefaultResponseType]
+        [Authorize(Roles = "IES")]
+        [AuthToken(Roles = "teacher,admin,student")]
+        [HttpPost("get-error-item-today-cnt")]
+        public async Task<IActionResult> getErrorItemsTodayCount(JsonElement request)
+        {
+            //輸入值
+            if (!request.TryGetProperty("subjectId", out JsonElement subjectId)) return BadRequest(); //科目ID
+            if (!request.TryGetProperty("stuId", out JsonElement _stuId)) return BadRequest(); //學生ID 或 TMID
+            string stuId = _stuId.GetString();
+            string schoolid = (request.TryGetProperty("code", out JsonElement codeJobj)) ? codeJobj.GetString() : string.Empty; //學校簡碼,TMID時為空
+            string tmdid = string.Empty;
+            if (string.IsNullOrWhiteSpace(schoolid)) tmdid = stuId;
+            string maLearnCode = string.Empty;
+            //檢查參數 必須有 teammodelId 或是同時有學校id及學號
+            if (!string.IsNullOrWhiteSpace(tmdid + ""))
+            {
+                maLearnCode = $"MaLearn-{tmdid}";
+            }
+            else if (!string.IsNullOrWhiteSpace(schoolid + "") && !string.IsNullOrWhiteSpace(stuId + ""))
+            {
+                maLearnCode = $"MaLearn-{schoolid}-{stuId}";
+            }
+            else { return BadRequest(); }
+
+            DateTime nowDate = DateTime.Now;
+            DateTime beforeDate = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, 0, 0, 0);
+            string qry_All = $"SELECT COUNT(1) as num FROM c WHERE c.type = 'answer' AND c.unitId = '{subjectId}' AND IS_DEFINED(c.createdTime) AND c.createdTime <= {DateTimeHelper.ToUnixTimestamp(nowDate)}";
+            string qry_before = $"SELECT COUNT(1) as num FROM c WHERE c.type = 'answer' AND c.unitId = '{subjectId}' AND IS_DEFINED(c.createdTime) AND c.createdTime <= {DateTimeHelper.ToUnixTimestamp(beforeDate)}  ";
+            var client = _azureCosmos.GetCosmosClient();
+            int avaliable = 0;
+            int allNum = 0;
+            int berforeNum = 0;
+
+            // 取全部錯題數量
+            await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "Student").GetItemQueryStreamIterator(queryText: qry_All, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"{maLearnCode}") }))
+            {
+                using var json = await JsonDocument.ParseAsync(item.ContentStream);
+                if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
+                {
+                    foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
+                    {
+                        if (obj.TryGetProperty("num", out JsonElement num))
+                        {
+                            allNum = num.GetInt32();
+                        }
+                    }
+                }
+            }
+            // 取今天之前錯題的數量
+            await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "Student").GetItemQueryStreamIterator(queryText: qry_before, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"{maLearnCode}") }))
+            {
+                using var json = await JsonDocument.ParseAsync(item.ContentStream);
+                if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
+                {
+                    foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
+                    {
+                        if (obj.TryGetProperty("num", out JsonElement num))
+                        {
+                            berforeNum = num.GetInt32();
+                        }
+                    }
+                }
+            }
+
+            avaliable = allNum - berforeNum;
+
+            return Ok(new { avaliable });
+        }
+
+
+
         //阅卷信息统计
         [ProducesDefaultResponseType]
         [Authorize(Roles = "IES")]

+ 4 - 4
TEAMModelOS/TEAMModelOS.csproj

@@ -74,11 +74,11 @@
     <SpaRoot>ClientApp\</SpaRoot>
     <DefaultItemExcludes>$(DefaultItemExcludes);$(SpaRoot)node_modules\**</DefaultItemExcludes>
     <UserSecretsId>078b5d89-7d90-4f6a-88fc-7d96025990a8</UserSecretsId>
-    <Version>5.2311.1</Version>
-    <AssemblyVersion>5.2311.1.1</AssemblyVersion>
-    <FileVersion>5.2311.1.1</FileVersion>
+    <Version>5.2311.8</Version>
+    <AssemblyVersion>5.2311.8.1</AssemblyVersion>
+    <FileVersion>5.2311.8.1</FileVersion>
     <Description>TEAMModelOS(IES5)</Description>
-    <PackageReleaseNotes>IES版本说明版本切换标记5.2311.1.1</PackageReleaseNotes>
+    <PackageReleaseNotes>IES版本说明版本切换标记5.2311.8.1</PackageReleaseNotes>
     <PackageId>TEAMModelOS</PackageId>
     <Authors>teammodel</Authors>
     <Company>醍摩豆(成都)信息技术有限公司</Company>

+ 33 - 33
TEAMModelOS/appsettings.Development.json

@@ -18,52 +18,52 @@
     "IdTokenSalt": "8263692E2213497BB55E74792B7900B4",
     "HttpTrigger": "https://teammodelosfunction-test.chinacloudsites.cn/api/",
     //"HttpTrigger": "http://localhost:7071/api/"
-    "Version": "5.2311.1.1"
+    "Version": "5.2311.8.1"
   },
   "Azure": {
     // 测试站数据库
-    //"Storage": {
-    //  "ConnectionString": "DefaultEndpointsProtocol=https;AccountName=teammodeltest;AccountKey=O2W2vadCqexDxWO+px+QK7y1sHwsYj8f/WwKLdOdG5RwHgW/Dupz9dDUb4c1gi6ojzQaRpFUeAAmOu4N9E+37A==;EndpointSuffix=core.chinacloudapi.cn"
-    //},
-    //"Cosmos": {
-    //  "ConnectionString": "AccountEndpoint=https://cdhabookdep-free.documents.azure.cn:443/;AccountKey=JTUVk92Gjsx17L0xqxn0X4wX2thDPMKiw4daeTyV1HzPb6JmBeHdtFY1MF1jdctW1ofgzqkDMFOtcqS46by31A==;"
-    //},
-    //"Redis": {
-    //  "ConnectionString": "52.130.252.100:6379,password=habook,ssl=false,abortConnect=False,writeBuffer=10240"
-    //},
-    //"ServiceBus": {
-    //  "ConnectionString": "Endpoint=sb://teammodelos.servicebus.chinacloudapi.cn/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=Sy4h4EQ8zP+7w/lOLi1X3tGord/7ShFHimHs1vC50Dc=",
-    //  "ActiveTask": "dep-active-task",
-    //  "ItemCondQueue": "dep-itemcond",
-    //  "GenPdfQueue": "dep-genpdf"
-    //},
-    //"SignalR": {
-    //  "ConnectionString": "Endpoint=https://channel.service.signalr.net;AccessKey=KrblW06tuA4a/GyqRPDU0ynFFmAWxbAvyJihHclSXbQ=;Version=1.0;"
-    //}
-    // 正式站数据库
     "Storage": {
-      "ConnectionString": "DefaultEndpointsProtocol=https;AccountName=teammodelos;AccountKey=Dl04mfZ9hE9cdPVO1UtqTUQYN/kz/dD/p1nGvSq4tUu/4WhiKcNRVdY9tbe8620nPXo/RaXxs+1F9sVrWRo0bg==;EndpointSuffix=core.chinacloudapi.cn"
+      "ConnectionString": "DefaultEndpointsProtocol=https;AccountName=teammodeltest;AccountKey=O2W2vadCqexDxWO+px+QK7y1sHwsYj8f/WwKLdOdG5RwHgW/Dupz9dDUb4c1gi6ojzQaRpFUeAAmOu4N9E+37A==;EndpointSuffix=core.chinacloudapi.cn"
     },
     "Cosmos": {
-      "ConnectionString": "AccountEndpoint=https://teammodelos.documents.azure.cn:443/;AccountKey=clF73GwPECfP1lKZTCvs8gLMMyCZig1HODFbhDUsarsAURO7TcOjVz6ZFfPqr1HzYrfjCXpMuVD5TlEG5bFGGg==;"
+      "ConnectionString": "AccountEndpoint=https://cdhabookdep-free.documents.azure.cn:443/;AccountKey=JTUVk92Gjsx17L0xqxn0X4wX2thDPMKiw4daeTyV1HzPb6JmBeHdtFY1MF1jdctW1ofgzqkDMFOtcqS46by31A==;"
     },
     "Redis": {
-      "ConnectionString": "CoreRedisCN.redis.cache.chinacloudapi.cn:6380,password=LyJWP1ORJdv+poXWofAF97lhCEQPg1wXWqvtzXGXQuE=,ssl=True,abortConnect=False"
+      "ConnectionString": "52.130.252.100:6379,password=habook,ssl=false,abortConnect=False,writeBuffer=10240"
     },
     "ServiceBus": {
-      "ConnectionString": "Endpoint=sb://coreiotservicebuscnpro.servicebus.chinacloudapi.cn/;SharedAccessKeyName=TEAMModelOS;SharedAccessKey=llRPBMDJG9w1Nnifj+pGhV0g4H2REcq0PjvX2qqpcOg=",
-      "ActiveTask": "active-task",
-      "ItemCondQueue": "itemcond",
-      "GenPdfQueue": "genpdf"
+      "ConnectionString": "Endpoint=sb://teammodelos.servicebus.chinacloudapi.cn/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=Sy4h4EQ8zP+7w/lOLi1X3tGord/7ShFHimHs1vC50Dc=",
+      "ActiveTask": "dep-active-task",
+      "ItemCondQueue": "dep-itemcond",
+      "GenPdfQueue": "dep-genpdf"
     },
     "SignalR": {
-      "ConnectionString": "Endpoint=https://channel.signalr.azure.cn;AccessKey=AtcB7JYFNUbUXb1rGxa3PVksQ2X5YSv3JOHZR9J88tw=;Version=1.0;"
-    },
-    "Speech": {
-      "SubscriptionKey": "a4f5f4e2e2e54c6e8b0a4a0b4a0a4a0b",
-      "Region": "chinanorth3",
-      "Endpoint": "https://chinanorth3.api.cognitive.azure.cn/sts/v1.0/issuetoken"
+      "ConnectionString": "Endpoint=https://channel.service.signalr.net;AccessKey=KrblW06tuA4a/GyqRPDU0ynFFmAWxbAvyJihHclSXbQ=;Version=1.0;"
     }
+    // 正式站数据库
+    //"Storage": {
+    //  "ConnectionString": "DefaultEndpointsProtocol=https;AccountName=teammodelos;AccountKey=Dl04mfZ9hE9cdPVO1UtqTUQYN/kz/dD/p1nGvSq4tUu/4WhiKcNRVdY9tbe8620nPXo/RaXxs+1F9sVrWRo0bg==;EndpointSuffix=core.chinacloudapi.cn"
+    //},
+    //"Cosmos": {
+    //  "ConnectionString": "AccountEndpoint=https://teammodelos.documents.azure.cn:443/;AccountKey=clF73GwPECfP1lKZTCvs8gLMMyCZig1HODFbhDUsarsAURO7TcOjVz6ZFfPqr1HzYrfjCXpMuVD5TlEG5bFGGg==;"
+    //},
+    //"Redis": {
+    //  "ConnectionString": "CoreRedisCN.redis.cache.chinacloudapi.cn:6380,password=LyJWP1ORJdv+poXWofAF97lhCEQPg1wXWqvtzXGXQuE=,ssl=True,abortConnect=False"
+    //},
+    //"ServiceBus": {
+    //  "ConnectionString": "Endpoint=sb://coreiotservicebuscnpro.servicebus.chinacloudapi.cn/;SharedAccessKeyName=TEAMModelOS;SharedAccessKey=llRPBMDJG9w1Nnifj+pGhV0g4H2REcq0PjvX2qqpcOg=",
+    //  "ActiveTask": "active-task",
+    //  "ItemCondQueue": "itemcond",
+    //  "GenPdfQueue": "genpdf"
+    //},
+    //"SignalR": {
+    //  "ConnectionString": "Endpoint=https://channel.signalr.azure.cn;AccessKey=AtcB7JYFNUbUXb1rGxa3PVksQ2X5YSv3JOHZR9J88tw=;Version=1.0;"
+    //},
+    //"Speech": {
+    //  "SubscriptionKey": "a4f5f4e2e2e54c6e8b0a4a0b4a0a4a0b",
+    //  "Region": "chinanorth3",
+    //  "Endpoint": "https://chinanorth3.api.cognitive.azure.cn/sts/v1.0/issuetoken"
+    //}
   },
   "HaBookAuth": {
     "CoreId": {

+ 1 - 1
TEAMModelOS/appsettings.json

@@ -18,7 +18,7 @@
     "Exp": 86400,
     "IdTokenSalt": "8263692E2213497BB55E74792B7900B4",
     "HttpTrigger": "https://teammodelosfunction.chinacloudsites.cn/api/",
-    "Version": "5.2311.1.1"
+    "Version": "5.2311.8.1"
   },
   "Azure": {
     "Storage": {