Przeglądaj źródła

Merge branch 'develop' into hhb/develop-course

CrazyIter_Bin 2 lat temu
rodzic
commit
ff63399079
100 zmienionych plików z 6912 dodań i 2537 usunięć
  1. 1 1
      TEAMModelBI/ClientApp/public/index.html
  2. 5 1
      TEAMModelBI/ClientApp/src/api/index.js
  3. BIN
      TEAMModelBI/ClientApp/src/assets/img/synchronization.png
  4. BIN
      TEAMModelBI/ClientApp/src/assets/img/view.png
  5. 62 62
      TEAMModelBI/ClientApp/src/components/public/personalPhoto/index.vue
  6. 6 1
      TEAMModelBI/ClientApp/src/store/index.js
  7. 1 0
      TEAMModelBI/ClientApp/src/until/common.js
  8. 564 351
      TEAMModelBI/ClientApp/src/view/areaServe/areamanage.vue
  9. 31 1
      TEAMModelBI/ClientApp/src/view/areaServe/setthird.vue
  10. 65 45
      TEAMModelBI/ClientApp/src/view/areamanage/statistics.vue
  11. 1 3
      TEAMModelBI/ClientApp/src/view/common/aside.vue
  12. 248 33
      TEAMModelBI/ClientApp/src/view/created/created.vue
  13. 115 104
      TEAMModelBI/ClientApp/src/view/index/index.vue
  14. 11 6
      TEAMModelBI/ClientApp/src/view/login.vue
  15. 142 42
      TEAMModelBI/ClientApp/src/view/participation/index.vue
  16. 38 3
      TEAMModelBI/ClientApp/src/view/schoolServe/school.vue
  17. 69 6
      TEAMModelBI/ClientApp/src/view/schoolServe/setSchooladmin.vue
  18. 154 38
      TEAMModelBI/ClientApp/src/view/schoolmanage/schoolAnalyse.vue
  19. 10 9
      TEAMModelBI/ClientApp/src/view/systemConfig/apimanage.vue
  20. 95 22
      TEAMModelBI/ClientApp/src/view/systemConfig/correlation.vue
  21. 4 4
      TEAMModelBI/ClientApp/src/view/teachermanage/manage.vue
  22. 116 12
      TEAMModelBI/ClientApp/src/view/teachermanage/traitmanage.vue
  23. 42 14
      TEAMModelBI/Controllers/BIHome/OnLineController.cs
  24. 8 0
      TEAMModelBI/Controllers/BINormal/AreaRelevantController.cs
  25. 310 24
      TEAMModelBI/Controllers/BINormal/BatchAreaController.cs
  26. 21 19
      TEAMModelBI/Controllers/BISchool/BatchSchoolController.cs
  27. 15 13
      TEAMModelBI/Controllers/BISchool/SchoolController.cs
  28. 6 8
      TEAMModelBI/Controllers/Core/BlobController.cs
  29. 36 0
      TEAMModelBI/Controllers/BITest/TestController.cs
  30. 349 0
      TEAMModelBI/Controllers/RepairApi/InitialAreaController.cs
  31. 3 3
      TEAMModelBI/TEAMModelBI.csproj
  32. 1 1
      TEAMModelBI/Tool/CommonFind.cs
  33. 11 9
      TEAMModelOS.FunctionV4/CosmosDB/TriggerArt.cs
  34. 12 6
      TEAMModelOS.FunctionV4/CosmosDB/TriggerExam.cs
  35. 74 59
      TEAMModelOS.FunctionV4/HttpTrigger/ScsYxptApis.cs
  36. 4 1
      TEAMModelOS.FunctionV4/Lang/en-us.json
  37. 4 1
      TEAMModelOS.FunctionV4/Lang/zh-cn.json
  38. 4 1
      TEAMModelOS.FunctionV4/Lang/zh-tw.json
  39. 18 4
      TEAMModelOS.SDK/DI/CoreAPI/CoreAPIHttpService.cs
  40. 1 1
      TEAMModelOS.SDK/DI/HttpTrigger/HttpTrigger.cs
  41. 5 0
      TEAMModelOS.SDK/Models/Cosmos/Common/ArtEvaluation.cs
  42. 21 0
      TEAMModelOS.SDK/Models/Cosmos/Common/ArtExam.cs
  43. 2 0
      TEAMModelOS.SDK/Models/Cosmos/Common/GroupList.cs
  44. 1 0
      TEAMModelOS.SDK/Models/Cosmos/Common/Inner/Attachment.cs
  45. 2 0
      TEAMModelOS.SDK/Models/Cosmos/Normal/ArtSetting.cs
  46. 1 1
      TEAMModelOS.SDK/Models/Cosmos/School/Knowledge.cs
  47. 6 1
      TEAMModelOS.SDK/Models/Cosmos/Student/StudentArtResult.cs
  48. 72 0
      TEAMModelOS.SDK/Models/Service/ArtService.cs
  49. 7 4
      TEAMModelOS.SDK/Models/Service/BatchCopyFileService.cs
  50. 3 2
      TEAMModelOS.SDK/Models/Service/Third/Sc/ScYxptModel.cs
  51. 4 1
      TEAMModelOS.SDK/Models/Service/Third/ThirdService.cs
  52. 2 1
      TEAMModelOS/ClientApp/babel.config.js
  53. 4 2
      TEAMModelOS/ClientApp/package.json
  54. 17 13
      TEAMModelOS/ClientApp/public/lang/en-US.js
  55. 11 6
      TEAMModelOS/ClientApp/public/lang/zh-CN.js
  56. 20 15
      TEAMModelOS/ClientApp/public/lang/zh-TW.js
  57. 2 1
      TEAMModelOS/ClientApp/src/.babelrc
  58. 14 0
      TEAMModelOS/ClientApp/src/api/areaArt.js
  59. 6 0
      TEAMModelOS/ClientApp/src/api/http.js
  60. 5 0
      TEAMModelOS/ClientApp/src/api/thirdparty.js
  61. BIN
      TEAMModelOS/ClientApp/src/assets/image/cus-import-cn.png
  62. BIN
      TEAMModelOS/ClientApp/src/assets/image/cus-import-en.png
  63. BIN
      TEAMModelOS/ClientApp/src/assets/image/cus-import-tw.png
  64. 3 7
      TEAMModelOS/ClientApp/src/boot-app.js
  65. 2 1
      TEAMModelOS/ClientApp/src/common/TipsInfo.vue
  66. 321 0
      TEAMModelOS/ClientApp/src/components/dashboard/art/BaseArtScatter.vue
  67. 15 6
      TEAMModelOS/ClientApp/src/components/dashboard/art/BaseClassLineBar.vue
  68. 14 3
      TEAMModelOS/ClientApp/src/components/dashboard/art/BaseGradeLineBar.vue
  69. 21 11
      TEAMModelOS/ClientApp/src/components/dashboard/art/BaseKnowPie.vue
  70. 3 4
      TEAMModelOS/ClientApp/src/components/dashboard/art/BasePointLineBar.vue
  71. 55 25
      TEAMModelOS/ClientApp/src/components/dashboard/art/BaseStuLineBar.vue
  72. 5 4
      TEAMModelOS/ClientApp/src/components/dashboard/art/LeftBottom.vue
  73. 16 0
      TEAMModelOS/ClientApp/src/components/dashboard/art/LeftCenter.vue
  74. 9 13
      TEAMModelOS/ClientApp/src/components/dashboard/art/LeftTop.vue
  75. 41 8
      TEAMModelOS/ClientApp/src/components/dashboard/art/RightBotL.vue
  76. 91 5
      TEAMModelOS/ClientApp/src/components/dashboard/art/RightBotR.vue
  77. 72 17
      TEAMModelOS/ClientApp/src/components/dashboard/art/RightTop.vue
  78. 3 1
      TEAMModelOS/ClientApp/src/components/mark/MarkCanvas.vue
  79. 6 10
      TEAMModelOS/ClientApp/src/components/selflearn/NewChooseContent.vue
  80. 1 1
      TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContentTypeTemplate/Homework.vue
  81. 110 24
      TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContentTypeTemplate/PaperViewBox/ArtView.vue
  82. 5 5
      TEAMModelOS/ClientApp/src/components/student-web/EventView/EventList.vue
  83. 13 8
      TEAMModelOS/ClientApp/src/components/student-web/HomeView/newHomeView.vue
  84. 311 313
      TEAMModelOS/ClientApp/src/router/routes.js
  85. 4 2
      TEAMModelOS/ClientApp/src/store/index.js
  86. 43 0
      TEAMModelOS/ClientApp/src/store/module/artDashboard.js
  87. 4 0
      TEAMModelOS/ClientApp/src/store/module/studentWeb.js
  88. 1 1
      TEAMModelOS/ClientApp/src/utils/public.js
  89. 2 3
      TEAMModelOS/ClientApp/src/view/Home.vue
  90. 336 0
      TEAMModelOS/ClientApp/src/view/areaArtExam/AcQuos.vue
  91. 312 0
      TEAMModelOS/ClientApp/src/view/areaArtExam/DataView.vue
  92. 305 0
      TEAMModelOS/ClientApp/src/view/areaArtExam/ExamData.vue
  93. 374 0
      TEAMModelOS/ClientApp/src/view/areaArtExam/Mgt.vue
  94. 277 0
      TEAMModelOS/ClientApp/src/view/areaArtExam/WorkData.vue
  95. 15 1
      TEAMModelOS/ClientApp/src/view/areaMgmt/AreaBase.vue
  96. 9 0
      TEAMModelOS/ClientApp/src/view/areaMgmt/AreaLayout.vue
  97. 140 140
      TEAMModelOS/ClientApp/src/view/art/SchoolArt.vue
  98. 315 131
      TEAMModelOS/ClientApp/src/view/artexam/AcQuos.vue
  99. 796 838
      TEAMModelOS/ClientApp/src/view/artexam/Create.vue
  100. 0 0
      TEAMModelOS/ClientApp/src/view/artexam/DataView.vue

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

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

+ 5 - 1
TEAMModelBI/ClientApp/src/api/index.js

@@ -75,7 +75,7 @@ export default {
     },
     //批量创校的数据验证
     verifyContent(data) {
-        return post('/batchschool/get-checlkexist', data)
+        return post('/batchschool/get-checkexists', data)
     },
     //获取学校简码
     getSchoolcode(data) {
@@ -108,6 +108,10 @@ export default {
     saveAbility(data) {
         return post('/biabilitymgmt/upd-currency', data)
     },
+    //同步微能力点
+    getSynchronize(data) {
+        return post('/batcharea/cut-full-statndard', data)
+    },
     //获取未加入区域的学校
     getNotjoinSchool(data) {
         return post('/schoolcheck/get-notarea', data)

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


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


+ 62 - 62
TEAMModelBI/ClientApp/src/components/public/personalPhoto/index.vue

@@ -1,80 +1,80 @@
 <template>
-    <div id="personalPhoto">
-        <div v-if="picture">
-            <img class="avatar" :src="picture">
-        </div>
-        <div v-else style="display: flex;align-items: center;justify-content: center;">
-            <div class="fakeAvatar" :style="{backgroundColor: randomColor, width: width, height: height, fontSize: fontSize}">
-                {{  getChinese(name) }}
-            </div>
-        </div>
+  <div id="personalPhoto">
+    <div v-if="picture">
+      <img class="avatar" :src="picture">
     </div>
+    <div v-else style="display: flex;align-items: center;justify-content: center;">
+      <div class="fakeAvatar" :style="{backgroundColor: randomColor, width: width, height: height, fontSize: fontSize}">
+        {{  getChinese(name) }}
+      </div>
+    </div>
+  </div>
 </template>
 <script>
 import { computed, ref } from 'vue'
 export default {
-    props: {
-        name: String,
-        picture: String,
-        color: String,
-        fontSize: {
-            type: String,
-            default: '14px',
-        },
-        width: {
-            type: String,
-            default: '50px',
-        },
-        height: {
-            type: String,
-            default: '50px',
-        },
+  props: {
+    name: String,
+    picture: String,
+    color: String,
+    fontSize: {
+      type: String,
+      default: '14px',
+    },
+    width: {
+      type: String,
+      default: '50px',
     },
-    setup() {
-        const randomColor = computed(() => {
-            let r = Math.floor(Math.random() * 255)
-            let g = Math.floor(Math.random() * 255)
-            let b = Math.floor(Math.random() * 255)
-            return 'rgba(' + r + ',' + g + ',' + b + ',0.8)'
-        })
-        function getChinese(strValue) {
-            if (!strValue) return ''
-            var reg = /[\u4e00-\u9fa5]/g
-            var names = strValue.match(reg)
-            if (names) {
-                return names.join('').substring(names.length - 2)
-            } else {
-                return strValue.substr(0, 2)
-            }
-        }
-        return { getChinese, randomColor }
+    height: {
+      type: String,
+      default: '50px',
     },
+  },
+  setup () {
+    const randomColor = computed(() => {
+      let r = Math.floor(Math.random() * 255)
+      let g = Math.floor(Math.random() * 255)
+      let b = Math.floor(Math.random() * 255)
+      return 'rgba(' + r + ',' + g + ',' + b + ',0.8)'
+    })
+    function getChinese (strValue) {
+      if (!strValue) return ''
+      var reg = /[\u4e00-\u9fa5]/g
+      var names = strValue.match(reg)
+      if (names) {
+        return names.join('').substring(names.length - 2)
+      } else {
+        return strValue.substr(0, 2)
+      }
+    }
+    return { getChinese, randomColor }
+  },
 }
 </script>
 <style>
 #personalPhoto {
-    display: flex;
-    align-items: center;
-    justify-content: center;
+  display: flex;
+  align-items: center;
+  justify-content: center;
 }
 .avatar {
-    display: flex;
-    width: 32px;
-    height: 32px;
-    border-radius: 50%;
-    align-items: center;
-    justify-content: center;
-    overflow: hidden;
+  display: flex;
+  width: 32px;
+  height: 32px;
+  /* border-radius: 50%; */
+  align-items: center;
+  justify-content: center;
+  overflow: hidden;
 }
 .fakeAvatar {
-    display: flex;
-    width: 36px;
-    border-radius: 50%;
-    align-items: center;
-    justify-content: center;
-    /* background-color: antiquewhite; */
-    height: 36px;
-    color: white;
-    font-size: 1.25rem;
+  display: flex;
+  width: 36px;
+  border-radius: 50%;
+  align-items: center;
+  justify-content: center;
+  /* background-color: antiquewhite; */
+  height: 36px;
+  color: white;
+  font-size: 1.25rem;
 }
 </style>

+ 6 - 1
TEAMModelBI/ClientApp/src/store/index.js

@@ -20,7 +20,8 @@ export default createStore({
         thirdCorrelation: {},
         thirdUser: {},
         areaClickschool: {},
-        areaClickCounselor: ''
+        areaClickCounselor: '',
+        areaClickRoles: '',
     },
     mutations: {
         //修改组织架构
@@ -89,6 +90,10 @@ export default createStore({
         //学区点击顾问查看详情
         clickCounselor(state, value) {
             state.areaClickCounselor = value
+        },
+        //学区点击人员查看详情 (传输身份)
+        clickRoles(state, value) {
+            state.areaClickRoles = value
         }
     },
     actions: {},

+ 1 - 0
TEAMModelBI/ClientApp/src/until/common.js

@@ -28,6 +28,7 @@ export default {
     //时间戳转日期
     getLocalTime(nS) {
         return new Date(parseInt(nS)).toLocaleString().replace(/:\d{1,2}$/, ' ');
+        // return new Date(parseInt(nS))
     },
     //计算总天数
     totalDay(startime, endtime) {

Plik diff jest za duży
+ 564 - 351
TEAMModelBI/ClientApp/src/view/areaServe/areamanage.vue


+ 31 - 1
TEAMModelBI/ClientApp/src/view/areaServe/setthird.vue

@@ -61,6 +61,9 @@
                     <!-- <el-input v-model="setform.token" :rows="2" type="textarea" @input="saveState=true" v-if="PowerShow" /> -->
                     <el-input v-model="setform.token" :rows="2" type="textarea" disabled />
                   </div>
+                  <div class="generatebox-copy">
+                    <el-button type="primary" size="small" @click="copykeys(setform.token)">复制秘钥</el-button>
+                  </div>
                   <div class="generatebox" v-if="PowerShow">
                     <el-button type="primary" size="small" @click="againGenerate">重新生成秘钥</el-button>
                   </div>
@@ -160,6 +163,7 @@
 import { ref, reactive, getCurrentInstance, watch } from 'vue'
 import { ElMessage, ElMessageBox } from 'element-plus'
 import { Search } from '@element-plus/icons-vue'
+import useClipboard from 'vue-clipboard3'
 export default {
   props: {
     clickDetails: {
@@ -172,6 +176,7 @@ export default {
   setup (props, context) {
     console.log(props, context, '父传值')
     let { proxy } = getCurrentInstance()
+    const { toClipboard } = useClipboard()
     let PowerShow = proxy.$access.identifyPosition(JSON.parse(localStorage.getItem('id_token')))
     let activeName = ref('first')
     let setform = ref({
@@ -229,6 +234,7 @@ export default {
       console.log(response, file, fileList, '上传成功的返回')
       setform.value.imageUrl = response.url
       uploadState.value = true
+      saveState.value = true
     }
     function handleUpdErr () {
       ElMessage.error('企业头像修改失败')
@@ -335,7 +341,7 @@ export default {
       let arr = schoolLists.value
       let newArr = arr.filter((item) => {
         // return (item.name && item.name.includes(filterText.value)) || (item.mobile && item.mobile.includes(filterText.value)) || (item.mobile && item.mobile.tmdId.includes(filterText.value))
-        return item.name.includes(serachValues.value)
+        return item.name.includes(serachValues.value) || item.id.includes(serachValues.value)
       })
       schoolLists.value = newArr
     }
@@ -361,6 +367,15 @@ export default {
         ElMessage.error('企业详情获取失败,API异常')
       })
     }
+    //复制
+    async function copykeys (value) {
+      try {
+        await toClipboard(value)
+        ElMessage.success('已复制秘钥')
+      } catch (e) {
+        console.error(e)
+      }
+    }
     watch(
       props,
       (newsProps) => {
@@ -406,6 +421,7 @@ export default {
       deltelogos,
       PowerShow,
       details,
+      copykeys
     }
   },
 }
@@ -499,9 +515,16 @@ export default {
   width: 100%;
 }
 .generatebox {
+  position: absolute;
+  bottom: 5px;
+  margin-left: 2%;
+}
+.generatebox-copy {
   position: absolute;
   top: 5px;
+  right: 45%;
   margin-left: 2%;
+  opacity: 0;
 }
 .remove {
   margin-right: 2%;
@@ -546,4 +569,11 @@ export default {
   width: 100%;
   height: 100%;
 }
+.token-box:hover .generatebox-copy {
+  opacity: 1 !important;
+}
+.token-box:hover .el-textarea__inner {
+  background: rgba(189, 195, 199, 0.45);
+  cursor: pointer;
+}
 </style>

+ 65 - 45
TEAMModelBI/ClientApp/src/view/areamanage/statistics.vue

@@ -70,40 +70,40 @@
           <el-tab-pane label="学区列表">
             <div class="area-data">
               <div class="area-item" v-for="(item,index) in areaLists" :key="item.id">
-              <div class="area-item-list">
-              <p class="area-item-name">{{item.name}}</p>
-              <p class="area-item-school"><span class="area-item-school-title">学区学校数:</span><span class="area-item-school-content">{{item.scCnt}}</span></p>
-            <!-- <p class="area-item-school"><span class="area-item-school-title">学区教师数:</span><span class="area-item-school-content">{{item.tchCnt}}</span></p>
+                <div class="area-item-list">
+                  <p class="area-item-name">{{item.name}}</p>
+                  <p class="area-item-school"><span class="area-item-school-title">学区学校数:</span><span class="area-item-school-content">{{item.scCnt}}</span></p>
+                  <!-- <p class="area-item-school"><span class="area-item-school-title">学区教师数:</span><span class="area-item-school-content">{{item.tchCnt}}</span></p>
             <p class="area-item-school"><span class="area-item-school-title">学区学生数:</span><span class="area-item-school-content">{{item.stuCnt}}</span></p> -->
+                </div>
+                <div class="item-detailsbtn" @click="getSchoolDistrict(item)">
+                  <span>详情 ></span>
+                </div>
               </div>
-              <div class="item-detailsbtn" @click="getSchoolDistrict(item)">
-              <span>详情 ></span>
-              </div>
-            </div>
             </div>
           </el-tab-pane>
           <el-tab-pane label="未归区学校">
             <div class="not-classify">
               <el-table :data="notClassifydata" style="width:100%" height="300" empty-text="暂无数据">
-            <el-table-column label="校徽" align="center">
-              <template #default="scope">
-                <div class="school-badge">
-                  <el-image :src="scope.row.picture" fit="fill" />
-                </div>
-              </template>
-            </el-table-column>
-            <el-table-column prop="name" label="名称" align="center" />
-            <el-table-column prop="id" label="简码" align="center" />
-            <el-table-column prop="size" label="空间" align="center" />
-            <el-table-column align="center">
-              <template #header>
-                <el-input v-model="areaSearchValue" size="small" placeholder="搜索学校名称/简码" clearable />
-              </template>
-              <template #default="scope">
-                <el-button type="primary" size="small" @click="detailsclick(scope.row)">详情</el-button>
-              </template>
-            </el-table-column>
-          </el-table>
+                <el-table-column label="校徽" align="center">
+                  <template #default="scope">
+                    <div class="school-badge">
+                      <el-image :src="scope.row.picture" fit="fill" />
+                    </div>
+                  </template>
+                </el-table-column>
+                <el-table-column prop="name" label="名称" align="center" />
+                <el-table-column prop="id" label="简码" align="center" />
+                <el-table-column prop="size" label="空间" align="center" />
+                <el-table-column align="center">
+                  <template #header>
+                    <el-input v-model="areaSearchValue" size="small" placeholder="搜索学校名称/简码" clearable />
+                  </template>
+                  <template #default="scope">
+                    <el-button type="primary" size="small" @click="detailsclick(scope.row)">详情</el-button>
+                  </template>
+                </el-table-column>
+              </el-table>
             </div>
           </el-tab-pane>
         </el-tabs>
@@ -374,8 +374,8 @@ export default {
       classLine: true,
       areaList: true,
     })
-    let notClassifydata=ref([])
-    let notClassifyoriginal=ref({})
+    let notClassifydata = ref([])
+    let notClassifyoriginal = ref({})
     let schooltableData = ref([])
     //某个学区顾问列表
     let CounselorList = ref([])
@@ -1599,11 +1599,11 @@ export default {
         })
     }
     //获取未归学区的学校列表
-    function notclassify(){
-      proxy.$api.getAreaSchoolList({}).then((res)=>{
-        console.log(res,'未归区的学校')
-        res.state === 200 ? (notClassifydata.value=res.areaSchool,notClassifyoriginal.value=res.areaSchool):''
-      }).catch((error)=>{
+    function notclassify () {
+      proxy.$api.getAreaSchoolList({}).then((res) => {
+        console.log(res, '未归区的学校')
+        res.state === 200 ? (notClassifydata.value = res.areaSchool, notClassifyoriginal.value = res.areaSchool) : ''
+      }).catch((error) => {
         ElMessage.error('API异常,获取未归区学校列表失败')
       })
     }
@@ -1840,6 +1840,7 @@ export default {
       // router.push('/home/campus')
       console.log(val, 3333)
       store.commit('clickCounselor', val.tmdId)
+      store.commit('clickRoles', val.roles)
       router.push({ path: 'campus', query: { pattern: 'area' } })
     }
     function detailsclick (value) {
@@ -1856,12 +1857,12 @@ export default {
       timer.value = setTimeout(fn, wait)
     }
     function personnelSearch () {
-      let arr = showPattern.value==='all'? notClassifydata.value:schooltableData.value
+      let arr = showPattern.value === 'all' ? notClassifydata.value : schooltableData.value
       let newArr = arr.filter((item) => {
         // return (item.name && item.name.includes(filterText.value)) || (item.mobile && item.mobile.includes(filterText.value)) || (item.mobile && item.mobile.tmdId.includes(filterText.value))
         return item.name.includes(areaSearchValue.value) || item.id.includes(areaSearchValue.value)
       })
-      showPattern.value==='all'?  notClassifydata.value=newArr:schooltableData.value = newArr
+      showPattern.value === 'all' ? notClassifydata.value = newArr : schooltableData.value = newArr
     }
     getAll()
     // getoption()
@@ -1872,7 +1873,7 @@ export default {
       if (newdata.trim().length !== 0) {
         debounce(personnelSearch, 500)
       } else {
-        showPattern.value==='all'? notClassifydata.value=notClassifyoriginal.value:schooltableData.value = originalSchool.value
+        showPattern.value === 'all' ? notClassifydata.value = notClassifyoriginal.value : schooltableData.value = originalSchool.value
       }
     })
     return {
@@ -2457,23 +2458,42 @@ export default {
   width: 100%;
   text-align: center;
 }
-.area-data{
-  width:100%;
-  height:25vh;
+.area-data {
+  width: 100%;
+  height: 35vh;
   display: flex;
   flex-wrap: wrap;
   overflow: auto;
 }
-.not-classify{
-  width:100%;
-  height:25vh;
+.not-classify {
+  width: 100%;
+  height: 35vh;
 }
 </style>
-
 <style>
 .statisticsbox-all .el-loading-spinner .circular,
 .statisticsbox .el-loading-spinner .circular {
   display: inline !important;
 }
-.area-listinfo .el-tabs{width:100%}
+.area-listinfo .el-tabs {
+  width: 100%;
+}
+@media screen and (max-width: 1920px) {
+  .area-data,
+  .not-classify {
+    height: 35vh !important;
+  }
+  .area-item {
+    width: 18% !important;
+  }
+}
+@media screen and (max-width: 1400px) {
+  .area-data,
+  .not-classify {
+    height: 45vh !important;
+  }
+  .area-item {
+    width: 22% !important;
+  }
+}
 </style>

+ 1 - 3
TEAMModelBI/ClientApp/src/view/common/aside.vue

@@ -72,7 +72,7 @@ export default {
         router: '/home/index',
         isShow: true,
         permission: '',
-        role: 'all',
+        role: ['admin', 'leader'],
         child: [],
         sort: 0,
       },
@@ -384,8 +384,6 @@ export default {
       if (lockState.value) {
         var lockInfo = document.getElementsByClassName('locks')
         var asibox = document.getElementsByClassName('MenuBox')
-        var testa = document.getElementsByClassName('eachSite')
-        console.log(testa)
         isCollapse.value = false
         lockInfo[0].style.overflow = 'hidden'
         lockInfo[0].style.width = 200 + 'px'

+ 248 - 33
TEAMModelBI/ClientApp/src/view/created/created.vue

@@ -40,6 +40,10 @@
             <el-form-item :label="$t(`areaManages.createdArea.project`)" class="capacity-box">
               <el-select v-model="formArea.capacityvalue" :placeholder="$t(`areaManages.createdArea.projecthint`)">
                 <el-option v-for="item in formArea.options" :key="item.id" :label="item.name + '——' + item.standardName" :value="item.id">
+                  <el-tag class="reminder" effect="dark" style="float: left" size="small" v-if="item.id ==='02944f32-f534-3397-ea56-e6f1fc6c3714' || item.id==='69e3d413-50a1-4f5e-844a-e0f7c9622ea3'">标准</el-tag>
+                  <el-tag class="reminder" type="info" effect="dark" style="float: left" size="small" v-else-if="(item.id !=='02944f32-f534-3397-ea56-e6f1fc6c3714' || item.id !=='69e3d413-50a1-4f5e-844a-e0f7c9622ea3') && item.id !=='bde5c011-2ae4-461a-b46c-5483ba72ae45'">普通</el-tag>
+                  <el-tag class="reminder" type="success" effect="dark" style="float: left" size="small" v-else-if="item.id ==='bde5c011-2ae4-461a-b46c-5483ba72ae45'">完整</el-tag>
+                  <span>{{ item.name}}</span>——<span>{{item.standardName}}</span>
                 </el-option>
               </el-select>
             </el-form-item>
@@ -150,7 +154,7 @@
         </p>
         <el-form ref="createdSchoolforms" :label-position="labelPosition" :model="item" label-width="120px" :rules="schoolRules">
           <el-form-item :label="$t(`schoolManages.createSchools.schoolinfo.name`)" class="school-name" prop="name">
-            <el-input v-model="item.name" :placeholder="$t(`schoolManages.createSchools.schoolinfo.name`)" @blur="verifySchooldata('name')">
+            <el-input v-model="item.name" :placeholder="$t(`schoolManages.createSchools.schoolinfo.name`)">
             </el-input>
             <p class="repetition-hint" v-if="repetition.name">该学校名称已存在,请检查学校名称</p>
           </el-form-item>
@@ -179,6 +183,7 @@
           </el-form-item>
           <el-form-item label="预设管理员" class="scholl-admin" prop="presupposeAdmin">
             <el-input v-model="item.presupposeAdmin" placeholder="输入醍摩豆账号或手机号,预设学校管理员" />
+            <p class="repetition-hint" v-if="repetition.admin">该管理员用户不存在,请检查</p>
           </el-form-item>
           <el-form-item label="归属学区ID" class="scholl-admin belong">
             <!-- <el-input v-model="item.areaIdcreated" placeholder="" /> -->
@@ -186,6 +191,7 @@
               <el-option v-for="item in createdAreds" :key="item.id" :label="item.id ? item.name+ '——' + item.institution:item.name" :value="item.id">
               </el-option>
             </el-select>
+            <p class="repetition-hint" v-if="repetition.area">该学区数据异常,请检查</p>
           </el-form-item>
           <el-form-item label="所属位置" class="school-location">
             <!-- <elui-china-area-dht :leave="3" @change="onChange(item.value,item.num)" placeholder="请选择地区" v-model="item.value"></elui-china-area-dht> -->
@@ -233,8 +239,8 @@
           </el-form-item>
         </el-form>
         <div class="confirmarea">
-          <el-button type="primary" @click="verifydataForbb()" v-if="verifyForstate.state ===false">数据核验</el-button>
-          <el-button type="primary" @click="createdSchool()" :loading="createdSchoolLoading" v-else-if="verifyForstate.state===true && verifyForstate.pass===true ">{{ $t(`schoolManages.createSchools.submit`) }}</el-button>
+          <el-button type="primary" @click="verifyBase()" v-if="verifyForstate.state ===false">数据核验</el-button>
+          <el-button type="success" @click="createdSchool()" :loading="createdSchoolLoading" v-else-if="verifyForstate.state===true && verifyForstate.pass===true ">{{ $t(`schoolManages.createSchools.submit`) }}</el-button>
           <!-- <el-button @click="closeandreturn('close', 'school')">重置</el-button> -->
           <el-button @click="resetForm()">重置</el-button>
         </div>
@@ -273,7 +279,7 @@
                 <template #default="scope">
                   <div v-if="scope.row.source !=='BI'">
                     <div class="bringbox">
-                      <el-button size="small" @click="formOperate">
+                      <el-button size="small" @click="formOperate(scope.row)">
                         <svg class="lockicon-bring" aria-hidden="true">
                           <use xlink:href="#icon-daoru"></use>
                         </svg>
@@ -381,6 +387,7 @@
             <el-table-column prop="period" label="学段类型" align="center" />
             <el-table-column prop="admin" label="预设管理员" align="center" />
             <el-table-column prop="areaId" label="所属学区ID" align="center" />
+            <el-table-column prop="id" label="简码" align="center" v-if="codeShow" />
             <el-table-column prop="size" :label="$t(`schoolManages.createSchools.schoolinfo.spacesize`)" align="center" />
             <el-table-column prop="region" :label="$t(`schoolManages.createSchools.schoolinfo.nation`)" align="center" />
             <el-table-column prop="province" :label="$t(`areaManages.selector.provinceName`)" align="center" />
@@ -417,6 +424,16 @@
                 </svg>
               </template>
             </el-table-column>
+            <el-table-column label="回传情况" align="center" v-if="backstatus">
+              <template #default="scope">
+                <svg class="resultIcon" aria-hidden="true" v-if="scope.row.backstate ==='add'">
+                  <use xlink:href="#icon-tijiaochenggong"></use>
+                </svg>
+                <svg class="resultIcon" aria-hidden="true" v-else>
+                  <use xlink:href="#icon-shibai"></use>
+                </svg>
+              </template>
+            </el-table-column>
           </el-table>
           <div class="batchs-btn">
             <el-button type="primary" size="medium" @click="quantity()" :loading="createdSchoolLoading" v-if="batchCreatedSchool===false && batchTablesArr && verifyState">
@@ -429,7 +446,7 @@
               <!-- {{ $t(`schoolManages.createSchools.batchBtn`) }} -->
               数据验证
             </el-button>
-            <el-button type="primary" size="medium" @click="batchList=false,batchCreatedSchool=false,batchData=[]" v-else-if="batchCreatedSchool===true">
+            <el-button type="primary" size="medium" @click="batchList=false,batchCreatedSchool=false,batchData=[],backstatus=false,codeShow=false" v-else-if="batchCreatedSchool===true">
               确认
             </el-button>
             <span v-if="batchCreatedSchool===false && batchTablesArr && verifyState===false" class="error-int">请完善表格,重新提交</span>
@@ -457,7 +474,7 @@
 <script>
 import { reactive, getCurrentInstance, toRefs, ref, onMounted, watch, nextTick } from 'vue'
 import { EluiChinaAreaDht } from 'elui-china-area-dht'
-import { ElMessage, ElLoading } from 'element-plus'
+import { ElMessage, ElLoading, } from 'element-plus'
 import { Search, StarFilled } from '@element-plus/icons'
 import { useStore } from 'vuex'
 import * as excel from '../../until/excel.js'
@@ -601,7 +618,7 @@ export default {
     let schoolregionParams = ref({
       label: 'name', //这里可以配置你们后端返回的属性
       value: 'code',
-      checkStrictly: false,
+      checkStrictly: true,
     })
     let loadingCreatedArea = ref(false)
     //批量创建学校显示结果状态
@@ -610,10 +627,14 @@ export default {
     let allSchool = ref([])
     let batchTablesArr = ref(true)
     let verifyState = ref(true)
+    let codeShow = ref(false)
+    let backstatus = ref(false)
     //本地数据验证需要数据
     let repetition = ref({
       name: false,
       code: false,
+      admin: false,
+      area: false,
     })
     let loadingSchool = ref(true)
     let loadingTexts = ref('正在加载相关数据,请稍等')
@@ -730,6 +751,9 @@ export default {
         console.log(res, '微能力点')
         createdAreds.value.push({ id: '', name: '暂不选择', institution: '无' })
         res.state === 200 ? ((formArea.value.options = res.areas), (res.areas.forEach((item) => { createdAreds.value.push(item) })), store.commit('getPoint', res.areas)) : ElMessage.error('获取微能力点方案失败,API异常')
+        formArea.value.options.map((item, index) => {
+          item.id === 'bde5c011-2ae4-461a-b46c-5483ba72ae45' ? formArea.value.options.unshift(formArea.value.options.splice(index, 1)[0]) : ''
+        })
         console.log(t(`areaManages.createdArea.notAreaname`))
       })
     }
@@ -1009,8 +1033,14 @@ export default {
     }
     //批量创校 数据验证
     function verifyData () {
+      const loadingverify = ElLoading.service({
+        lock: true,
+        text: '正在验证数据内容,请稍后...',
+        background: 'rgba(0, 0, 0, 0.7)',
+      })
       let batchDatas = batchData.value
       let schoolData = batchData.value
+      let presentData = []
       let scName = []; let scAdmin = []; let scAreaid = []
       batchDatas.forEach((item) => {
         item.name ? scName.push(item.name.toString()) : ''
@@ -1019,17 +1049,23 @@ export default {
         if (item.admin.includes(',')) {
           let adminA = item.admin.split(',')
           scAdmin = scAdmin.concat(adminA)
+          item.admin = scAdmin
         } else if (item.admin.includes(',')) {
           let adminA = item.admin.split(',')
           scAdmin = scAdmin.concat(adminA)
+          item.admin = scAdmin
         } else {
           scAdmin.push(item.admin.toString())
+          item.admin = scAdmin
         }
+        scAdmin = []
+        presentData.push(item)
       })
+      console.log(scAdmin, batchDatas, '处理结果')
       for (let i in schoolData) {
-        console.log(schoolData)
-        let ids = MathRand()
-        schoolData[i].id = ids
+        // console.log(schoolData)
+        // let ids = MathRand()
+        // schoolData[i].id = ids
         if (schoolData[i].name === '' || schoolData[i].name === undefined) {
           // createdSchoolLoading.value = false
           ElMessage.error('请完善创建学校名称')
@@ -1048,37 +1084,57 @@ export default {
           return
         }
       }
-      let data = { scNames: scName, accounts: scAdmin, areaIds: scAreaid }
+      let data = { checkScs: presentData }
+      console.log(data, '提交的数据')
       proxy.$api.verifyContent(data).then((res) => {
         console.log(res, '数据验证的结果')
-        res.state === 200 ? (batchTablesArr.value = true, batchDatas.forEach((item) => { item.dataState = true })) : ''
+        res.state === 200 ? (batchDatas.forEach((item) => { item.dataState = true }), batchVerify(presentData, loadingverify), codeShow.value = true) : ''
+        if (res.state === 200) {
+          //处理简码
+          res.createScInfo.forEach((items) => {
+            let name = items.name
+            let code = items.id
+            for (let c in batchDatas) {
+              batchDatas[c].name === name ? batchDatas[c].id = code : ''
+            }
+          })
+        }
         if (res.state === 201) {
+          codeShow.value = false
           batchTablesArr.value = true; verifyState.value = false
           batchDatas.forEach((item) => { item.dataState = true })
           if (res.existScNames.length !== 0) {
+            console.log('进入学校重复的')
             for (let i in res.existScNames) {
-              let name = res.existScNames[i]
+              let name = res.existScNames[i].name
               for (let n in batchDatas) {
                 batchDatas[n].name === name ? (batchDatas[n].abnormaltext = '该学校名称已存在', batchDatas[n].class = 'anomaly', batchDatas[n].dataState = false) : ''
               }
             }
-          } else if (res.noAccounts.length !== 0) {
+          }
+          if (res.noAccounts.length !== 0) {
+            console.log('进入管理员不存在的')
             for (let i in res.noAccounts) {
-              let admins = res.noAccounts[i]
+              let admins = res.noAccounts[i].name
               for (let n in batchDatas) {
-                batchDatas[n].admin === admins ? (batchDatas[n].abnormaltext = '当前创建管理员用户不存在', batchDatas[n].class = 'anomaly', batchDatas[n].dataState = false) : ''
+                batchDatas[n].name === admins ? (batchDatas[n].abnormaltext = batchDatas[n].abnormaltext ? batchDatas[n].abnormaltext + ',' + '当前创建管理员用户不存在' : '当前创建管理员用户不存在', batchDatas[n].class = 'anomaly', batchDatas[n].dataState = false) : ''
               }
             }
-          } else if (res.noAreaIds.length !== 0) {
+          }
+          if (res.noAreaIds.length !== 0) {
+            console.log('进入学区不存在的')
             for (let i in res.noAreaIds) {
-              let areaIdx = res.noAreaIds[i]
+              let areaIdx = res.noAreaIds[i].name
               for (let n in batchDatas) {
-                batchDatas[n].areaId == areaIdx ? (batchDatas[n].abnormaltext = '创建学校所归属学区ID异常', batchDatas[n].class = 'anomaly', batchDatas[n].dataState = false) : ''
+                batchDatas[n].areaId == areaIdx ? (batchDatas[n].abnormaltext = batchDatas[n].abnormaltext ? batchDatas[n].abnormaltext + ',' + '创建学校所归属学区ID异常' : '创建学校所归属学区ID异常', batchDatas[n].class = 'anomaly', batchDatas[n].dataState = false) : ''
               }
             }
           }
+          console.log(batchDatas, '返回后处理的数据')
+          loadingverify.close()
         }
       }).catch((error) => {
+        loadingverify.close()
         ElMessage.error('API异常,数据验证失败')
       })
     }
@@ -1094,7 +1150,7 @@ export default {
         } else if (schoolData[n].admin.includes(',')) {
           schoolData[n].admin = schoolData[n].admin.split(",")
         } else {
-          schoolData[n].admin = [schoolData[n].admin]
+          schoolData[n].admin = schoolData[n].admin
         }
       }
       console.log(schoolData, '处理ID后')
@@ -1103,13 +1159,14 @@ export default {
       proxy.$api.createdSchools(datas).then((res) => {
         console.log(res, '批量创校的返回')
         if (res.state === 200) {
+          codeShow.value = true
           batchData.value.forEach((y) => { y.state = true })
           batchCreatedSchool.value = true
           ElMessage.success(proxy.$t(`commonMsg.batchCreatedSuccess`))
           // batchList.value = false;
           // batchData.value = []; 
-          ElMessage.success('批量创建成功')
-          router.push({ path: '/home/schoolmanage' })
+          ElMessage.success('批量创建成功'), batchSuccessback(batchData.value)
+          // router.push({ path: '/home/schoolmanage' })
         } else if (res.state === 201) {
           console.log(batchData.value, '值!!!')
           for (let i in batchData.value) {
@@ -1149,7 +1206,7 @@ export default {
       })
     }
     function tableRowClassName (row) {
-      console.log(row.row, 'TABLE变化')
+      // console.log(row.row, 'TABLE变化')
       // if (row.row.zyl >= 80) {
       //         return 'warm-row';
       //     }
@@ -1270,7 +1327,7 @@ export default {
       let schoolname = ''; let schoolCode = ''
       let resultName = []; let resultCode = []
       param === 'name' ? (schoolname = schoolForm.value[0].name, resultName = schoolList.filter((item) => { return item.name === schoolname })) : param === 'code' ? (schoolCode = schoolForm.value[0].code, resultCode = schoolList.filter((item) => { return item.id === schoolCode })) : ''
-      resultName.length > 0 ? repetition.value.name = true : resultCode.length > 0 ? repetition.value.code = true : ''
+      resultName.length > 0 ? repetition.value.name = true : ''
       console.log(resultName, resultCode, '检查结果')
       resultName.length === 0 ? repetition.value.name = false : ''
       resultCode.length === 0 ? repetition.value.code = false : ''
@@ -1288,18 +1345,79 @@ export default {
       searchLoading.value = false
     }
     //表单操作
-    function formOperate () {
-      console.log('123456')
+    function formOperate (value) {
+      console.log(value)
+      //向表单赋值
+      schoolForm.value[0].name = value.name
+      schoolForm.value[0].code = value.code
+      let periodList = []
+      if (value.period !== null) {
+        value.period.includes('21') ? periodList.push('小学') : ''
+        value.period.includes('31') ? periodList.push('初中') : ''
+        value.period.includes('34') ? periodList.push('高中') : ''
+        value.period.includes('41') ? periodList.push('大学') : ''
+      }
+      schoolForm.value[0].radio1 = periodList
+      let locationCode = []
+      value.provinceId ? locationCode.push(value.provinceId) : ''
+      value.cityId ? locationCode.push(value.cityId) : ''
+      // locationCode.push('510104')
+      provinceOptions.value.provinceValue = locationCode
+      console.log(provinceOptions, '位置')
+      schoolForm.value[0].address = value.address
     }
-    //学校创建前,数据验证(BB向)
-    function verifydataForbb () {
-      // loadingTexts.value = '正在验证数据有效性,请稍等...'
-      // loadingSchool.value = true
+    //创校数据验证 本服务器端
+    function verifyBase () {
+      console.log(schoolForm.value)
       const verifySchool = ElLoading.service({
         lock: true,
         text: '数据验证中,请稍后...',
         background: 'rgba(0, 0, 0, 0.6)',
       })
+      let schoolData = schoolForm.value
+      let adminList = []
+      if (schoolData[0].presupposeAdmin.includes(',')) {
+        adminList = schoolData[0].presupposeAdmin.split(',')
+      } else if (schoolData[0].presupposeAdmin.includes(',')) {
+        adminList = schoolData[0].presupposeAdmin.split(',')
+      } else {
+        adminList.push(schoolData[0].presupposeAdmin)
+      }
+      let schoolonly = {
+        name: schoolData[0].name,
+        admin: adminList,
+        period: schoolData[0].radio1,
+        size: schoolData[0].pitchSpace,
+        region: '中国',
+        province: schoolData[0].schoolLocation.province,
+        city: schoolData[0].schoolLocation.city,
+        dist: schoolData[0].schoolLocation.area,
+        address: schoolData[0].address,
+        areaId: schoolData[0].areaIdcreated,
+        type: Number(schoolData[0].type),
+      }
+      let datas = { checkScs: [schoolonly] }
+      console.log(datas)
+      proxy.$api.verifyContent(datas).then((res) => {
+        console.log(res, '返回内容')
+        res.state === 200 ? (verifydataForbb(verifySchool), ElMessage({ showClose: false, message: '已通过数据,点击下方按钮进行创校操作', type: 'success', duration: 4000 })) : ''
+        if (res.state === 201) {
+          res.existScNames.length !== 0 ? repetition.value.name = true : ''
+          res.noAccounts.length !== 0 ? repetition.value.admin = true : ''
+          res.noAreaIds.length !== 0 ? repetition.value.area = true : ''
+          verifyForstate.value.state = false, verifyForstate.value.pass = false
+          ElMessage({ showClose: false, message: '数据存在异常,请检查表单内容', type: 'error', duration: 4000 })
+        }
+        verifySchool.close()
+      }).catch((error) => {
+        ElMessage.error('API异常,数据核验失败')
+        verifySchool.close()
+      })
+    }
+    //学校创建前,数据验证(BB向)
+    function verifydataForbb (loadingV) {
+      // loadingTexts.value = '正在验证数据有效性,请稍等...'
+      // loadingSchool.value = true
       // let site=siteValue === 'cn' ? "CN":siteValue ==='international' ? 
       let verifyData = []
       let users = JSON.parse(localStorage.getItem('userData'))
@@ -1330,12 +1448,25 @@ export default {
       let datas = { regionId: "CN", name: verifyData[0].name, nameFuzzy: false, shortCode: verifyData[0].code, shortCodeFuzzy: false }
       proxy.$api.verifyDatainbb(datas).then((res) => {
         console.log(res.length, '核验数据')
-        res.length == 0 ? (verifyForstate.value.state = true, verifyForstate.value.pass = true, ElMessage.success('数据已通过核验,可执行创校')) : (verifyForstate.value.state = false, verifyForstate.value.pass = false)
+        res.length == 0 ? (verifyForstate.value.state = true, verifyForstate.value.pass = true, ElMessage.success('数据已通过核验,可执行创校')) : (verifyForstate.value.state = false, verifyForstate.value.pass = false, ElMessage.error('数据未通过核验,请检查表单内容'))
       }).catch((error) => {
         ElMessage.error('API异常,数据核验失败')
       })
       console.log(verifyData, '准备核验的数据')
-      verifySchool.close()
+      loadingV.close()
+    }
+    //批量创建学校 数据验证BB
+    function batchVerify (value, lodingverify) {
+      let scArr = value
+      let snameList = []; let scodeList = []; let shortArr = [];
+      scArr.forEach((item) => { snameList.push(item.name); scodeList.push(item.id); shortArr.push(item.id) })
+      let data = { regionId: "CN", nameList: snameList, codeList: scodeList, shortCodeList: shortArr }
+      proxy.$api.verifyDatainbb(data).then((res) => {
+        res.length == 0 ? (ElMessage.success('数据已通过核验,可执行创校'), batchTablesArr.value = true) : (ElMessage.error('数据未通过核验,请检查表单内容'), batchTablesArr.value = true, verifyState.value = false)
+      }).catch((error) => {
+        ElMessage.error('API异常,批量数据核验失败')
+      })
+      lodingverify.close()
     }
     //学校创建成功后,数据回报给BB
     function backTobb (value, loadingDom) {
@@ -1398,6 +1529,81 @@ export default {
         ElMessage.error('API异常,数据回报失败')
       })
       loadingDom.close()
+      router.push('/home/schoolmanage')
+    }
+    //批量创校成功后,数据回报BB
+    function batchSuccessback (data) {
+      console.log(data, '准备回报数据')
+      let schoolObj = data
+      let regionsJosn = siteValue === 'cn' ? option_cn : option_gl
+      let periodData = [
+        { label: '小学', value: '21' },
+        { label: '初中', value: '31' },
+        { label: '高中', value: '34' },
+        { label: '职高', value: '36' },
+        { label: '大学', value: '41' },
+      ]
+      for (let i in schoolObj) {
+        console.log(schoolObj[i])
+        //取出学段
+        schoolObj[i].backPeriod = '';
+        schoolObj[i].provinceCode = ''
+        schoolObj[i].cityCode = ''
+        let periodValue = []
+        let phaseName = schoolObj[i].period
+        //按照BB向 处理学段里面的值
+        for (let x in phaseName) {
+          let names = phaseName[x]
+          for (let d in periodData) {
+            periodData[d].label === names ? periodValue.push(periodData[d].value) : ''
+          }
+        }
+        //赋值给新学段内容
+        schoolObj[i].backPeriod = periodValue
+        //处理省 市 编码
+        for (let p in regionsJosn) {
+          if (regionsJosn[p].name === schoolObj[i].province) {
+            schoolObj[i].provinceCode = regionsJosn[p].code
+            let childrenData = regionsJosn[p].children
+            for (let c in childrenData) {
+              childrenData[c].name === schoolObj[i].city ? schoolObj[i].cityCode = childrenData[c].code : ''
+            }
+          }
+        }
+      }
+      console.log(schoolObj, '处理数据后的内容')
+      let submitData = []
+      schoolObj.forEach((item) => {
+        submitData.push({
+          code: item.id,
+          name: item.name,
+          regionId: "CN",
+          regionName: "中国",
+          provinceId: item.provinceCode,
+          provinceName: item.province,
+          cityId: item.cityCode,
+          cityName: item.city,
+          address: item.address,
+          lang: "zh-cn",
+          shortCode: item.id,
+          period: item.backPeriod,
+          source: "BI"
+        })
+      })
+      console.log(submitData, '即将回报的数据')
+      proxy.$api.successBack(submitData).then((res) => {
+        console.log(res, '回报APi返回')
+        if (res.length !== 0) {
+          backstatus.value = true
+          res.forEach((item) => {
+            for (let i in batchData.value) {
+              batchData.value[i].name = item.name ? batchData.value[i].backstate = item.status : ''
+            }
+          })
+        }
+      }).catch((error) => {
+        ElMessage.error('API异常,数据回报失败')
+      })
     }
     getAllschool()
     getCapacitys()
@@ -1475,7 +1681,12 @@ export default {
       verifyForstate,
       siteValue,
       backTobb,
-      loadingTexts
+      loadingTexts,
+      batchVerify,
+      verifyBase,
+      batchSuccessback,
+      codeShow,
+      backstatus
     }
   },
 }
@@ -1896,6 +2107,10 @@ export default {
   font-size: 12px;
   margin-left: 1%;
 }
+.reminder {
+  margin-top: 5px;
+  margin-right: 5px;
+}
 </style>
 <style>
 .areabox .el-form {

+ 115 - 104
TEAMModelBI/ClientApp/src/view/index/index.vue

@@ -1,6 +1,7 @@
 <template>
   <!--首页(管理员以及研发页面)-->
   <div class="eachSite">
+    <div class="gain-time">数据时间:{{dataTime}}</div>
     <div class="statisticsbox-all" v-if="siteValue==='china'">
       <c-scrollbar ref="scrollbarRef" width="100%" height="100%" trigger="hover" direction="y">
         <div class="headerinbox" v-if="screen.icon === '#icon-tuichuquanping-fill-copy'">
@@ -271,6 +272,7 @@ export default {
     let { proxy } = getCurrentInstance()
     const routers = useRouter()
     let siteValue = ref('china')
+    let dataTime = ref('')
     let showPattern = ref([])
     let aspectsData = ref([
       { id: 1, title: '区内学校', num: 0, icon: '#icon-renshixuexiao', classname: 'school' },
@@ -452,7 +454,7 @@ export default {
     //国际站模式(避免重复加载)
     let worldState = ref(true)
     const discrepancy = Math.abs((new Date().getTimezoneOffset() / 60))
-    // console.log(discrepancy,'差异时间')
+    console.log(discrepancy, '差异时间')
     //首页
     let optionsData = ref({
       values: 'now',
@@ -1093,7 +1095,7 @@ export default {
               show: true,
             },
             // data: nowTimeQuantum.value.classinToday,
-             data: nowTimeQuantum.value.todayUpload,
+            data: nowTimeQuantum.value.todayUpload,
           },
           {
             name: '开课',
@@ -1189,7 +1191,7 @@ export default {
             //     },
             // },
             // data: nowTimeQuantum.value.classinYesterday,
-             data: nowTimeQuantum.value.classinYesterday,
+            data: nowTimeQuantum.value.classinYesterday,
           },
         ],
       },
@@ -2077,6 +2079,8 @@ export default {
         .then((res) => {
           console.log(res, 'all返回')
           if (res.state === 200) {
+            console.log(Date.parse(new Date()), res.datetime, '时间戳对比')
+            dataTime.value = proxy.$common.getLocalTime(res.datetime)
             if (siteValue.value === 'china') {
               areaAspectsData.value[0].num = parseInt(res.onStuCnt) + parseInt(res.onTchCnt)
               areaAspectsData.value[0].teach = res.onTchCnt
@@ -2101,7 +2105,7 @@ export default {
               worldareaAspectsData.value[4].num = res.stuCnt
               worldareaAspectsData.value[4].increase = res.todayStuCnt
             }
-
+            console.log(proxy.$common.getLocalTime(Date.parse(new Date()), '-------', proxy.$common.getLocalTime(res.datetime)))
             getOnlineData()
             getClassLivelys()
             getVersionsData()
@@ -2211,101 +2215,99 @@ export default {
       // let ids = id ? { areaId: id } : {}
       // loading.value.active = false
       let datas = siteValue.value === 'international' ? { "site": "Global" } : {}
-      proxy.$api
-        .getLessontrend(datas)
-        .then((res) => {
-          console.log(res, '课例和开课')
-          if (res.state === 200) {
-            let replaceTime = allTime.value
-            let schoolClassInfo = res.hours.sdOpenLesn.sort(function (a, b) { return a.key - b.key })
-            let uploadClssInfo = res.hours.sdUpdLesn.sort(function (a, b) { return a.key - b.key })
-            let hoursInfo = new Date().getHours()
-            if (res.hours.sdOpenLesn.length === 0) {
-              for (let i in replaceTime) {
-                let nums = parseInt(i) + 1
-                replaceTime[i].value === hoursInfo ? replaceTime.splice(nums) : ''
-              }
-              replaceTime.forEach((x) => {
-                x.schoolClass = 0
-              })
-            } else {
-              for (let i in replaceTime) {
-                let nums = parseInt(i) + 1
-                replaceTime[i].value === hoursInfo ? replaceTime.splice(nums) : ''
-              }
-              res.hours.sdOpenLesn.forEach((item) => {
-                item.key + discrepancy > 24 ? item.key = (item.key + discrepancy) - 24 : item.key = item.key + discrepancy
-              })
-              res.hours.sdUpdLesn.forEach((item) => {
-                item.key + discrepancy > 24 ? item.key = (item.key + discrepancy) - 24 : item.key = item.key + discrepancy
-              })
-              //处理HiTeach开课
-              schoolClassInfo.forEach((s) => {
-                for (let c in replaceTime) {
-                  replaceTime[c].value === s.key ? (replaceTime[c].schoolClass = s.value) : ''
-                }
-              })
-              //处理HiTeach开课(个人课例)
-              uploadClssInfo.forEach((t) => {
-                for (let e in replaceTime) {
-                  replaceTime[e].value === t.key ? (replaceTime[e].teachClass = t.value) : ''
-                }
-              })
+      proxy.$api.getLessontrend(datas).then((res) => {
+        console.log(res, '课例和开课')
+        if (res.state === 200) {
+          let replaceTime = allTime.value
+          let schoolClassInfo = res.hours.sdOpenLesn.sort(function (a, b) { return a.key - b.key })
+          let uploadClssInfo = res.hours.sdUpdLesn.sort(function (a, b) { return a.key - b.key })
+          let hoursInfo = new Date().getHours()
+          if (res.hours.sdOpenLesn.length === 0) {
+            for (let i in replaceTime) {
+              let nums = parseInt(i) + 1
+              replaceTime[i].value === hoursInfo ? replaceTime.splice(nums) : ''
             }
-            console.log(replaceTime, '345345')
-            if (siteValue.value === 'china') {
-              replaceTime.forEach((q) => {
-                nowTimeQuantum.value.classTime.push(q.time)
-                nowTimeQuantum.value.classinToday.push(q.schoolClass)
-                nowTimeQuantum.value.todayUpload.push(q.teachClass)
-                nowTimeQuantum.value.totalattendClass.push(parseInt(q.schoolClass))
-              })
-            } else if (siteValue.value === 'international') {
-              replaceTime.forEach((q) => {
-                worldnowTimeQuantum.value.classTime.push(q.time)
-                worldnowTimeQuantum.value.classinToday.push(q.schoolClass)
-                worldnowTimeQuantum.value.todayUpload.push(q.teachClass)
-                worldnowTimeQuantum.value.totalattendClass.push(parseInt(q.schoolClass))
-              })
+            replaceTime.forEach((x) => {
+              x.schoolClass = 0
+            })
+          } else {
+            for (let i in replaceTime) {
+              let nums = parseInt(i) + 1
+              replaceTime[i].value === hoursInfo ? replaceTime.splice(nums) : ''
             }
+            res.hours.sdOpenLesn.forEach((item) => {
+              item.key + discrepancy > 24 ? item.key = (item.key + discrepancy) - 24 : item.key = item.key + discrepancy
+            })
+            res.hours.sdUpdLesn.forEach((item) => {
+              item.key + discrepancy > 24 ? item.key = (item.key + discrepancy) - 24 : item.key = item.key + discrepancy
+            })
+            //处理HiTeach开课
+            schoolClassInfo.forEach((s) => {
+              for (let c in replaceTime) {
+                replaceTime[c].value === s.key ? (replaceTime[c].schoolClass = s.value) : ''
+              }
+            })
+            //处理HiTeach开课(个人课例)
+            uploadClssInfo.forEach((t) => {
+              for (let e in replaceTime) {
+                replaceTime[e].value === t.key ? (replaceTime[e].teachClass = t.value) : ''
+              }
+            })
+          }
+          console.log(replaceTime, '345345')
+          if (siteValue.value === 'china') {
+            replaceTime.forEach((q) => {
+              nowTimeQuantum.value.classTime.push(q.time)
+              nowTimeQuantum.value.classinToday.push(q.schoolClass)
+              nowTimeQuantum.value.todayUpload.push(q.teachClass)
+              nowTimeQuantum.value.totalattendClass.push(parseInt(q.schoolClass))
+            })
+          } else if (siteValue.value === 'international') {
+            replaceTime.forEach((q) => {
+              worldnowTimeQuantum.value.classTime.push(q.time)
+              worldnowTimeQuantum.value.classinToday.push(q.schoolClass)
+              worldnowTimeQuantum.value.todayUpload.push(q.teachClass)
+              worldnowTimeQuantum.value.totalattendClass.push(parseInt(q.schoolClass))
+            })
+          }
 
-            console.log(nowTimeQuantum.value.todayUpload, '999999999999999999')
-            //处理课例活跃数据(今日)
-            // let classToday = nowTimeQuantum.value.schoolClass.map((index, item) => {
-            //     return index + nowTimeQuantum.value.teachClass[item]
-            // })
-            // classToday.forEach((j) => {
-            //     nowTimeQuantum.value.classinToday.push(j)
-            // })
-            if (siteValue.value === 'china') {
-              res.hours.ydOpenLesn.forEach((y) => {
-                nowTimeQuantum.value.classinYesterday.push(y.value)
-              })
-              res.hours.ydUpdLesn.forEach((y) => {
-                nowTimeQuantum.value.yesterdayUpload.push(y.value)
-              })
-            } else if (siteValue.value === 'international') {
-              res.hours.ydOpenLesn.forEach((w) => {
-                worldnowTimeQuantum.value.classinYesterday.push(w.value)
-              })
-              res.hours.ydUpdLesn.forEach((w) => {
-                worldnowTimeQuantum.value.yesterdayUpload.push(w.value)
-              })
-            }
-            //处理开课及上传  本月 全年 历年数据
-            //本月
-            nowTimeQuantum.value.daysclassdata = res.days.sdOpenLesn
-            nowTimeQuantum.value.daysuploaddata = res.days.sdUpdLesn
-            //今年全月
-            nowTimeQuantum.value.monthclassdata = res.months.sdOpenLesn
-            nowTimeQuantum.value.monthuploaddta = res.months.sdUpdLesn
-            //历年
-            nowTimeQuantum.value.yearclassdata = res.years.sdOpenLesn
-            nowTimeQuantum.value.yearuploaddata = res.years.sdUpdLesn
-            loading.value.basics2 = false
-            loading.value.active = false
+          console.log(nowTimeQuantum.value.todayUpload, '999999999999999999')
+          //处理课例活跃数据(今日)
+          // let classToday = nowTimeQuantum.value.schoolClass.map((index, item) => {
+          //     return index + nowTimeQuantum.value.teachClass[item]
+          // })
+          // classToday.forEach((j) => {
+          //     nowTimeQuantum.value.classinToday.push(j)
+          // })
+          if (siteValue.value === 'china') {
+            res.hours.ydOpenLesn.forEach((y) => {
+              nowTimeQuantum.value.classinYesterday.push(y.value)
+            })
+            res.hours.ydUpdLesn.forEach((y) => {
+              nowTimeQuantum.value.yesterdayUpload.push(y.value)
+            })
+          } else if (siteValue.value === 'international') {
+            res.hours.ydOpenLesn.forEach((w) => {
+              worldnowTimeQuantum.value.classinYesterday.push(w.value)
+            })
+            res.hours.ydUpdLesn.forEach((w) => {
+              worldnowTimeQuantum.value.yesterdayUpload.push(w.value)
+            })
           }
-        })
+          //处理开课及上传  本月 全年 历年数据
+          //本月
+          nowTimeQuantum.value.daysclassdata = res.days.sdOpenLesn
+          nowTimeQuantum.value.daysuploaddata = res.days.sdUpdLesn
+          //今年全月
+          nowTimeQuantum.value.monthclassdata = res.months.sdOpenLesn
+          nowTimeQuantum.value.monthuploaddta = res.months.sdUpdLesn
+          //历年
+          nowTimeQuantum.value.yearclassdata = res.years.sdOpenLesn
+          nowTimeQuantum.value.yearuploaddata = res.years.sdUpdLesn
+          loading.value.basics2 = false
+          loading.value.active = false
+        }
+      })
         .catch((res) => {
           ElMessage.error('课例活跃度获取异常')
         })
@@ -2319,23 +2321,23 @@ export default {
           let basicsData = []; let standardData = []; let majorData = [];
           //基础版
           schoolList.forEach((x) => {
-            x.scale === 0 && x.size <=100 ? basicsData.push(x) : ''
+            x.scale === 0 && x.size <= 100 ? basicsData.push(x) : ''
             // x.scale === 500 && x.hard === 0 && x.serial === 0 && x.service === 0 ? standardData.push(x) : ''
             // x.scale === 500 && (x.hard !== 0 || x.serial !== 0 || x.service !== 0) ? majorData.push(x) : ''
             // ((x.scale ===300 && x.size === 500) || (x.size >100)) && (!x.service.includes('YMPCVCIM') && !x.service.includes('VLY6J6N6') && !x.service.includes('VABAJ6NV') ) ? standardData.push(x):''
             // ((x.scale === 300 && x.size === 500) || (x.size >100)) ? standardData.push(x):''
-            x.scale === 300 && x.size === 500 ? standardData.push(x):x.size > 100 && (!x.service.includes('YMPCVCIM') && !x.service.includes('VLY6J6N6') && !x.service.includes('VABAJ6NV') )  ?  standardData.push(x):''
-            x.service.includes('YMPCVCIM') || x.service.includes('VLY6J6N6') || x.service.includes('VABAJ6NV') ? majorData.push(x):''
+            x.scale === 300 && x.size === 500 ? standardData.push(x) : x.size > 100 && (!x.service.includes('YMPCVCIM') && !x.service.includes('VLY6J6N6') && !x.service.includes('VABAJ6NV')) ? standardData.push(x) : ''
+            x.service.includes('YMPCVCIM') || x.service.includes('VLY6J6N6') || x.service.includes('VABAJ6NV') ? majorData.push(x) : ''
           })
-          console.log(basicsData,standardData,majorData,7777777)
+          console.log(basicsData, standardData, majorData, 7777777)
           if (siteValue.value === 'china') {
             versionsData.value.basics.num = basicsData.length
-            versionsData.value.basics.proportion = ((basicsData.length / schoolList.length).toFixed(2)) * 100
+            versionsData.value.basics.proportion = Math.round(((basicsData.length / schoolList.length) * 100))
             versionsData.value.standard.num = standardData.length
-            versionsData.value.standard.proportion = ((standardData.length / schoolList.length).toFixed(2)) * 100
+            versionsData.value.standard.proportion = Math.round(((standardData.length / schoolList.length) * 100))
             console.log(versionsData.value.standard.proportion, standardData.length, schoolList.length, '占比')
             versionsData.value.major.num = majorData.length
-            versionsData.value.major.proportion = ((majorData.length / schoolList.length).toFixed(2)) * 100
+            versionsData.value.major.proportion = Math.round(((majorData.length / schoolList.length) * 100))
             let dataBasics = {
               title: {
                 text: '{a|' + versionsData.value.basics.proportion + '}{c|%}' + '\n' + '基础版占比',
@@ -3230,7 +3232,8 @@ export default {
       worldtotalArea,
       worldnowTimeQuantum,
       worldversionsData,
-      worldState
+      worldState,
+      dataTime
     }
   },
 }
@@ -3241,7 +3244,7 @@ export default {
   background: url("../../assets/img/bg-index.jpg") no-repeat;
   background-size: 100% 100%;
   position: relative;
-  height:100%;
+  height: 100%;
 }
 .statisticsbox,
 .statisticsbox-all {
@@ -4029,6 +4032,14 @@ export default {
   top: 0%;
   right: 1%;
 }
+.gain-time {
+  float: right;
+  font-size: 16px;
+  padding-right: 1%;
+  padding-top: 2px;
+  line-height: 18px;
+  color: #ecf0f1;
+}
 </style>
 <style>
 .statisticsbox-all .el-loading-spinner .circular {

+ 11 - 6
TEAMModelBI/ClientApp/src/view/login.vue

@@ -123,10 +123,10 @@ export default {
         let datas = { code: res }
         proxy.$api.Dinglogin(datas).then((res) => {
           console.log(res, '接口访问成功')
-          if(res.state === 200){
+          if (res.state === 200) {
             callbackStatus.value.state = res.state
-          callbackStatus.value.partitionKey = res.ddUserInfos[0].partitionKey
-          callbackStatus.value.rowKey = res.ddUserInfos[0].rowKey
+            callbackStatus.value.partitionKey = res.ddUserInfos[0].partitionKey
+            callbackStatus.value.rowKey = res.ddUserInfos[0].rowKey
           }
           // res.state === 201
           //     ? (store.commit('BindStatus', true),
@@ -137,7 +137,7 @@ export default {
           //     : res.state === 200
           //     ? (loading.close(), loginSuccess(res), ElMessage.success(proxy.$t(`login.loginSuccess`)))
           //     : (loading.close(), ElMessage.error(res.message))
-          res.state === 200 ? ElMessage.success(proxy.$t(`login.loginSuccess`), loginSuccess(res)) : res.state === 0 ? ElMessage.error(res.msg) : res.state === 403 ? ElMessage.error('该用户无权或被禁用登录,请联系平台管理人员'):ElMessage.error('登录失败')
+          res.state === 200 ? ElMessage.success(proxy.$t(`login.loginSuccess`), loginSuccess(res)) : res.state === 0 ? ElMessage.error(res.msg) : res.state === 403 ? ElMessage.error('该用户无权或被禁用登录,请联系平台管理人员') : ElMessage.error('登录失败')
           loading.close()
         })
       }
@@ -147,15 +147,20 @@ export default {
     }
     //登录成功后执行的数据保存本地
     function loginSuccess (res) {
+      console.log(res, '内容')
       if (res.state === 200) {
         let blobInfos = { osblob_uri: res.osblob_uri, osblob_sas: res.osblob_sas }
         localStorage.setItem('userData', JSON.stringify(res.ddUserInfos[0]))
         localStorage.setItem('id_token', JSON.stringify(res.id_token))
         localStorage.setItem('blobInfo', JSON.stringify(blobInfos))
-        localStorage.setItem('access_token',JSON.stringify(res.access_token))
+        localStorage.setItem('access_token', JSON.stringify(res.access_token))
         getOrganization()
         Allpermission()
-        router.push('/home/index')
+        if (res.roles.includes('admin') || res.roles.includes('leader')) {
+          router.push('/home/index')
+        } else if (res.roles.includes('assist') || res.roles.includes('sales')) {
+          router.push('/home/campus')
+        }
       }
     }
     //获取组织架构

+ 142 - 42
TEAMModelBI/ClientApp/src/view/participation/index.vue

@@ -41,7 +41,7 @@
             </el-button>
         </div> -->
     <div class="school-list">
-      <el-table :data="tableData" style="width: 100%" height="75vh" v-loading="loading" element-loading-text="加载中..."  empty-text="暂无相关数据">
+      <el-table :data="tableData" id="tableExclusive" style="width: 100%" height="75vh" v-loading="loading" element-loading-text="加载中..." empty-text="暂无相关数据">
         <el-table-column prop="index" :label="$t(`schoolManages.tables.serialnum`)" type="index" sortable align="center" />
         <el-table-column :label="$t(`schoolManages.tables.badge`)" width="150" align="center">
           <template #default="scope">
@@ -228,11 +228,12 @@
   <!--编辑学校页面end-->
 </template>
 <script>
-import { reactive, ref, getCurrentInstance, toRef } from 'vue'
+import { reactive, ref, getCurrentInstance, toRef, onMounted, watch } from 'vue'
 import option from '@/static/region.json'
 import { useStore } from 'vuex'
 import { ElMessage, ElLoading, ElMessageBox } from 'element-plus'
 import { useRouter } from 'vue-router'
+import jwt_decode from 'jwt-decode'
 import SetSchool from './setPhase.vue'
 const optionsData = option
 export default {
@@ -338,10 +339,46 @@ export default {
     ])
     provinceOptions.value.optionInfo = optionsData
     console.log(store.state.point)
+    //下拉加载
+    let scrollHeight = ref()
+    let tableNexttoken = ref()
+    let selectValue = ref({
+      province: '',
+      provinceCode: '',
+      city: '',
+      cityCode: '',
+      dist: '',
+      distCode: '',
+    })
+    let timer = ref()
+    onMounted(() => {
+      //监听表格滚动事件
+      // let table = mutipleTable.value._value.layout.table.refs.bodyWrapper;
+      let table = document.getElementById('tableExclusive')
+      console.log(table, '查看是否获取到')
+      table.addEventListener("scroll", (res) => { loadmore(res) }, true);
+    })
+    const loadmore = (res) => {
+      if (res.target.scrollTop && ((res.target.scrollHeight - 20) <= (res.target.scrollTop + res.target.clientHeight))) {
+        scrollHeight.value = (res.target.scrollHeight - 20) - (res.target.scrollTop + res.target.clientHeight)
+        console.log(scrollHeight.value, '值')
+      }
+    }
     //所有学校列表
     function getAllschool () {
-      let user = JSON.parse(localStorage.getItem('userData'))
-      proxy.$api.getSchooldata({ tmdId: user.tmdId }).then((res) => {
+      let user = jwt_decode(JSON.parse(localStorage.getItem('id_token')))
+      let roleA = ''
+      if (user.roles.length > 1) {
+        user.roles.includes('admin') ? roleA = 'admin' :
+          user.roles.includes('leader') ? roleA = 'leader' :
+            user.roles.includes('assist') ? roleA = 'assist' :
+              user.roles.includes('sales') ? roleA = 'sales' :
+                user.roles.includes('rdc') ? roleA = 'rdc' : ''
+      } else {
+        roleA = user.roles[0]
+      }
+      let data = roleA === 'admin' || roleA === 'leader' ? {} : { tmdId: JSON.parse(localStorage.getItem('userData')).tmdId, role: roleA }
+      proxy.$api.getSchooldata(data).then((res) => {
         console.log(res, '获取学校列表assist')
         //处理关联管家  拼内容
         // res.schoolAssists.splice(3)
@@ -405,40 +442,41 @@ export default {
     }
     //地区选择
     function areaSelctChange (value, model) {
-      loading.value = true
-      console.log(value)
-      let data = option
-      if (model === 'province') {
-        //传输下一级的数据给select
-        let cityData = data.filter((item) => {
-          return item.name === value
-        })
-        cityOptions.value.cityInfo = cityData[0].children
-        //遍历list
-        let schoolData = originalData.value.filter((items) => {
-          return items.province === value
-        })
-        tableData.value = schoolData
-        console.log(schoolData, tableData.value)
-      } else if (model === 'city') {
-        let distData = cityOptions.value.cityInfo.filter((item) => {
-          return item.name === value
-        })
-        distOptions.value.distInfo = distData[0].children
-        let provinceData = provinceOptions.value.provinceValue
-        let schoolData = originalData.value.filter((items) => {
-          return items.city === value && items.province === provinceData
-        })
-        tableData.value = schoolData
-      } else if (model === 'dist') {
-        let provinceData = provinceOptions.value.provinceValue
-        let cityData = cityOptions.value.cityValue
-        let schoolData = originalData.value.filter((items) => {
-          return items.city === cityData && items.province === provinceData && items.dist === value
-        })
-        tableData.value = schoolData
-      }
-      loading.value = false
+      console.log(value, model, '触发')
+      // loading.value = true
+      // console.log(value)
+      // let data = option
+      // if (model === 'province') {
+      //   //传输下一级的数据给select
+      //   let cityData = data.filter((item) => {
+      //     return item.name === value
+      //   })
+      //   cityOptions.value.cityInfo = cityData[0].children
+      //   //遍历list
+      //   let schoolData = originalData.value.filter((items) => {
+      //     return items.province === value
+      //   })
+      //   tableData.value = schoolData
+      //   console.log(schoolData, tableData.value)
+      // } else if (model === 'city') {
+      //   let distData = cityOptions.value.cityInfo.filter((item) => {
+      //     return item.name === value
+      //   })
+      //   distOptions.value.distInfo = distData[0].children
+      //   let provinceData = provinceOptions.value.provinceValue
+      //   let schoolData = originalData.value.filter((items) => {
+      //     return items.city === value && items.province === provinceData
+      //   })
+      //   tableData.value = schoolData
+      // } else if (model === 'dist') {
+      //   let provinceData = provinceOptions.value.provinceValue
+      //   let cityData = cityOptions.value.cityValue
+      //   let schoolData = originalData.value.filter((items) => {
+      //     return items.city === cityData && items.province === provinceData && items.dist === value
+      //   })
+      //   tableData.value = schoolData
+      // }
+      // loading.value = false
     }
     //学校详情 close
     function schoolClose () {
@@ -640,6 +678,57 @@ export default {
         ElMessage.success('删除成功')
       })
     }
+    function debounce (fn, wait) {
+      if (timer.value !== null) {
+        clearTimeout(timer.value)
+      }
+      timer.value = setTimeout(fn, wait)
+    }
+    function datascroll () {
+      console.log('触发了')
+      // loading.value = true
+      // let data = { contToken: tableNexttoken.value, province: selectValue.value.province, city: selectValue.value.city, dist: selectValue.value.dist }
+      // if (tableNexttoken.value === null) {
+      //   ElMessage.success('已经到最底了')
+      //   setTimeout(function () { loading.value = false }, 500);
+      //   return
+      // }
+      // proxy.$api.getSchooldata(data).then((res) => {
+      //   console.log(res, '查询到下一页数据了')
+      //   if (res.state === 200) {
+      //     for (let i in res.schoolAssists) {
+      //       res.schoolAssists[i].serviceData = []
+      //       if (res.schoolAssists[i].assists) {
+      //         res.schoolAssists[i].assisName = ''
+      //         res.schoolAssists[i].location = res.schoolAssists[i].dist !== null ? res.schoolAssists[i].province + res.schoolAssists[i].city + res.schoolAssists[i].dist : res.schoolAssists[i].province + res.schoolAssists[i].city
+      //         let datas = res.schoolAssists[i].assists
+      //         for (let y in datas) {
+      //           res.schoolAssists[i].assisName = res.schoolAssists[i].assisName + datas[y].tmdName + ','
+      //         }
+      //       }
+      //       if (res.schoolAssists[i].service.length > 0) {
+      //         res.schoolAssists[i].service.forEach((x) => {
+      //           for (let m in patternIcon.value) {
+      //             patternIcon.value[m].key === x ? res.schoolAssists[i].serviceData.push(patternIcon.value[m]) : ''
+      //           }
+      //         })
+      //       }
+      //     }
+      //   }
+      //   tableData.value.push(...res.schoolAssists)
+      //   tableData.value.forEach((item) => { item.areaName = ''; areaSelect.value.data.forEach((itema) => { item.areaId === itema.id ? item.areaName = itema.name : '' }) })
+      //   tablesccnt.value = res.scCnt
+      //   tableNexttoken.value = res.continuationToken
+      // }).catch((error) => {
+      //   ElMessage.error('API异常,获取更多学校数据失败')
+      // })
+      // setTimeout(function () { loading.value = false }, 800);
+    }
+    // watch(scrollHeight, (newdata) => {
+    //   if (scrollHeight.value < 0) {
+    //     debounce(datascroll, 500)
+    //   }
+    // })
     getAllschool()
     getAllassists()
     return {
@@ -685,7 +774,12 @@ export default {
       skipAnalyse,
       schooldataSort,
       removeSchool,
-      siteList
+      siteList,
+      scrollHeight,
+      debounce,
+      datascroll,
+      tableNexttoken,
+      selectValue
     }
   },
 }
@@ -965,9 +1059,15 @@ export default {
   color: #ecf0f1;
   margin: 0 auto;
 }
-.basic{color:#409EFF}
-.stands{color:#67C23A}
-.majors{color: #E6A23C}
+.basic {
+  color: #409eff;
+}
+.stands {
+  color: #67c23a;
+}
+.majors {
+  color: #e6a23c;
+}
 </style>
 <style>
 .schoolboxtad .el-cascader {

+ 38 - 3
TEAMModelBI/ClientApp/src/view/schoolServe/school.vue

@@ -170,6 +170,14 @@
         返回
       </el-button>
     </div>
+    <div class="informationbox" v-if="models === 'details'">
+      <div class="informationbox-img">
+        <el-image :src="nSchool.img" fit="fill"></el-image>
+      </div>
+      <div class="informationbox-name">
+        <span>{{nSchool.name}}</span>
+      </div>
+    </div>
     <el-tabs v-if="models === 'details'" @tab-click="changeTabs">
       <!--基础设置-->
       <el-tab-pane :label="$t(`schoolManages.redactSet`)">
@@ -189,7 +197,7 @@
                 <el-input v-model="nowPitchdata.name" :placeholder="nowPitchdata.name" v-else-if="!PowerShow" disabled></el-input>
               </el-form-item>
               <el-form-item label="学校徽章" class="school-form-badge">
-                <el-upload class="upload-demo-redact" :headers="uploadHeader" action="/blob/upload-public" :before-upload="changeBadge" :on-success="success" :on-error="handleUpdErr" v-if="PowerShow">
+                <el-upload class="upload-demo-redact" :headers="uploadHeader" action="/blob/upload-public" :before-upload="changeBadge" :on-success="success" :on-error="handleUpdErr" :show-file-list="false" v-if="PowerShow">
                   <el-image :src="nowPitchdata.picture" fit="contain"></el-image>
                   <div class="changebadge">
                     <el-button>{{ $t(`schoolManages.basicSet.badgeChange`) }}</el-button>
@@ -460,6 +468,11 @@ export default {
       value: 'code',
       checkStrictly: false,
     })
+    //当前学校显示
+    let nSchool = ref({
+      img: '',
+      name: ''
+    })
     provinceOptions.value.optionInfo = optionsData
     placeData.value.dataInfo = optionsData
     onMounted(() => {
@@ -519,6 +532,8 @@ export default {
       nowPitchdata.value.scale = data.scale
       nowPitchdata.value.standard = data.standard
       nowPitchdata.value.areaId = data.areaId
+      nSchool.value.img = data.picture
+      nSchool.value.name = data.name
       data.assists.length ? data.assists.forEach((element) => { adminvalue.value.push(element.tmdId) }) : ''
       //处理现实学校所属位置
       let provinceValues = data.province; let cityValues = data.city; let distValues = data.dist;
@@ -1105,6 +1120,7 @@ export default {
       placeData,
       schoolregionParams,
       originalNum,
+      nSchool
     }
   },
 }
@@ -1134,7 +1150,8 @@ export default {
 /*编辑页面样式*/
 .redactbox {
   width: 100%;
-  height: 87vh;
+  /* height: 87vh; */
+  overflow: auto;
 }
 
 .backbtn {
@@ -1149,7 +1166,7 @@ export default {
 .school-formbox {
   width: 98%;
   padding: 1% 1%;
-  margin: 1%;
+  margin: 1% 1% 0% 1%;
   border: 1px solid #ccc;
   border-radius: 10px;
   background: #fff;
@@ -1460,6 +1477,24 @@ export default {
 .majors {
   color: #e6a23c;
 }
+.informationbox {
+  position: absolute;
+  width: 20%;
+  z-index: 99999;
+  display: flex;
+  height: 46px;
+  line-height: 46px;
+  left: 45%;
+}
+.informationbox-img {
+  width: 37px;
+  padding-top: 2px;
+}
+.informationbox-name {
+  font-size: 16px;
+  color: #00a8ff;
+  margin-left: 2%;
+}
 </style>
 <style>
 .schoolboxea .el-cascader {

+ 69 - 6
TEAMModelBI/ClientApp/src/view/schoolServe/setSchooladmin.vue

@@ -1,6 +1,6 @@
 <template>
-  <div class="setadmin">
-    <div class="admin-List">
+  <div class="setadmin" v-if="userRoles.roles.includes('admin')">
+    <div class=" admin-List">
       <p class="admin-List-title">学校管理员列表</p>
       <div class="list-item" v-for="(item,index) in adminData" :key="index">
         <div class="list-item-photo">
@@ -66,11 +66,27 @@
       </div>
     </div>
   </div>
+  <div class="setadminbox" v-else>
+    <p class="setadmin-concealed-title">管理员列表:</p>
+    <div class="setadmin-concealed">
+      <div class="adminlist-box">
+        <div class="adminList-item" v-for="item in adminData">
+          <div class="headportrait">
+            <el-image style="" :src="item.picture" fit="fill" v-if="item.picture"></el-image>
+            <PersonalPhoto style="cursor: pointer;" :name="item.name" width="103px" height="103px" fontSize="20px" v-else></PersonalPhoto>
+          </div>
+          <div class="adminList-item-name"><span>{{item.name}}</span></div>
+          <div class="adminList-item-id"><span>醍摩豆ID:</span><span>{{item.id}}</span></div>
+        </div>
+      </div>
+    </div>
+  </div>
 </template>
 <script>
 import { ref, getCurrentInstance, watch } from 'vue'
 import { Search } from '@element-plus/icons-vue'
 import { ElMessage, ElLoading, ElMessageBox } from 'element-plus'
+import jwt_decode from 'jwt-decode'
 export default {
   props: {
     schoolinfo: {
@@ -82,6 +98,7 @@ export default {
   },
   setup (props) {
     console.log(props, '设置管理员的值')
+    const userRoles = jwt_decode(JSON.parse(localStorage.getItem('id_token')))
     let adminfilter = ref('')
     let adminData = ref([])
     let { proxy } = getCurrentInstance()
@@ -185,7 +202,8 @@ export default {
       searchState,
       removeadmin,
       addAdmin,
-      searchNodata
+      searchNodata,
+      userRoles
     }
   },
 }
@@ -193,13 +211,18 @@ export default {
 <style scoped>
 .setadmin {
   width: 100%;
-  padding: 0% 1%;
+  /* padding: 0% 1%; */
   display: flex;
   line-height: 20px;
   background-color: #fff;
   margin: 0 auto;
 }
-
+.setadminbox {
+  width: 100%;
+  line-height: 20px;
+  background-color: #fff;
+  margin: 0 auto;
+}
 .admin-List {
   width: 30%;
   text-align: left;
@@ -439,6 +462,43 @@ export default {
   margin-bottom: 5px;
   margin-top: 10px;
 }
+.adminList-item {
+  border: 1px solid #ccc;
+  padding: 1%;
+  width: 12%;
+  text-align: center;
+  margin: 1%;
+  border-radius: 10px;
+}
+.adminList-item-name {
+  font-size: 20px;
+  font-weight: 700;
+  line-height: 35px;
+  color: #303133;
+}
+.adminList-item-id {
+  font-size: 14px;
+  line-height: 35px;
+  color: #909399;
+}
+.setadmin-concealed {
+  width: 100%;
+  line-height: 20px;
+  overflow: auto;
+}
+.adminlist-box {
+  width: 100%;
+  display: flex;
+  flex-wrap: wrap;
+}
+.setadmin-concealed-title {
+  padding-left: 1%;
+  font-size: 14px;
+  margin-bottom: 5px !important;
+  line-height: 20px;
+  text-align: left;
+  color: #95a5a6;
+}
 </style>
 <style>
 .admin-text-box .search-box .el-input__inner {
@@ -459,7 +519,10 @@ export default {
   padding: 9px 62px;
   font-size: 14px;
 }
-
+.headportrait .el-image {
+  width: 55%;
+  border-radius: 50%;
+}
 .headerPhoto .el-image {
   display: block;
 }

+ 154 - 38
TEAMModelBI/ClientApp/src/view/schoolmanage/schoolAnalyse.vue

@@ -81,10 +81,10 @@
             <!-- <el-table-column>
               <div class="column"></div>
             </el-table-column> -->
-            <el-table-column label="课例数" align="center">
+            <el-table-column label="活动数" align="center">
               <template #default="scope">
                 <div class="data-and-state">
-                  <div>{{scope.row.interCnt}}</div>
+                  <div>{{scope.row.actCnt}}</div>
                   <!-- <div v-if="scope.row.state ==='up'" style="color:red">
                     <el-image style="width: 10px; height: 10px" :src="imgDP[0].url" fit="fill"></el-image>
                   </div>
@@ -212,6 +212,29 @@
         <ConventionPie :pieData="echartsData.edition"></ConventionPie>
       </div>
     </div>
+    <div class="schoolList-all">
+      <p class="school-list-title">学校列表:</p>
+      <div class="school-tables">
+        <el-table :data="schoolDefault" height="25vh" style="width: 100%" v-loading="searchText.loading" element-loading-background="rgba(0, 0, 0, 0.5)" empty-text="暂无数据">
+          <el-table-column label="校徽" align="center">
+            <template #default="scope">
+              <el-image style="width: 50px; height: 50px" :src="scope.row.picture" fit="fill" v-if="scope.row.picture"></el-image>
+              <div class="notimage" v-else>暂无图片</div>
+            </template>
+          </el-table-column>
+          <el-table-column prop="name" label="名称" align="center" />
+          <el-table-column prop="id" label="简码" align="center" />
+          <el-table-column align="center">
+            <template #header>
+              <el-input v-model="searchText.values" size="small" placeholder="搜索学校名称/简码" clearable />
+            </template>
+            <template #default="scope">
+              <el-button size="small" type="primary">前往查看</el-button>
+            </template>
+          </el-table-column>
+        </el-table>
+      </div>
+    </div>
   </div>
   <div class="schoolanalysebox" v-else-if="showState ==='particular'">
     <div class="school-analyse-header bgimg" v-loading="detailsLoding.header" element-loading-background="rgba(0, 0, 0, 0.5)">
@@ -306,23 +329,10 @@
         <ConventionPie :pieData="detailsEchart.subject"></ConventionPie>
       </div>
     </div>
-    <!-- <div class="school-list">
-            <p class="school-list-title">学校列表:</p>
-            <div class="schoolListbox">
-                <div class="school-badge">
-                    <el-image style="width: 80px; height: 80px;border-radius:50%" src="" fit="fill"></el-image>
-                </div>
-                <div class="school-data">
-                    <p class="school-data-name">成都锦江区外国语小学</p>
-                    <p><span class="school-data-title">学校教师数:</span><span class="school-data-num">1234</span></p>
-                    <p><span class="school-data-title">学校学生数:</span><span class="school-data-num">1234</span></p>
-                </div>
-            </div>
-        </div> -->
   </div>
 </template>
 <script>
-import { ref, onMounted, getCurrentInstance, watch } from 'vue'
+import { ref, reactive, onMounted, getCurrentInstance, watch } from 'vue'
 import Bar from '@/components/echarts/commonBar.vue'
 import CommonLine from '@/components/echarts/commonLine.vue'
 import CommonBar from '@/components/echarts/commonBar.vue'
@@ -332,6 +342,7 @@ import ConventionPie from '@/components/echarts/conventionPie.vue'
 import { useRouter } from 'vue-router'
 import { useStore } from 'vuex'
 import { ElMessage, ElLoading } from 'element-plus'
+import jwt_decode from 'jwt-decode'
 import * as echarts from 'echarts'
 export default {
   components: {
@@ -442,7 +453,7 @@ export default {
               rotate: 0,
               textStyle: {
                 color: '#A6D3FD',
-                fontSize: 14,
+                fontSize: 10,
               },
             },
             axisPointer: {
@@ -534,7 +545,7 @@ export default {
               rotate: 0,
               textStyle: {
                 color: '#A6D3FD',
-                fontSize: 14,
+                fontSize: 10,
               },
             },
             axisPointer: {
@@ -626,7 +637,7 @@ export default {
               rotate: 0,
               textStyle: {
                 color: '#A6D3FD',
-                fontSize: 14,
+                fontSize: 10,
               },
             },
             axisPointer: {
@@ -1109,7 +1120,7 @@ export default {
               rotate: 0,
               textStyle: {
                 color: '#A6D3FD',
-                fontSize: 14,
+                fontSize: 10,
               },
             },
             axisPointer: {
@@ -1188,7 +1199,7 @@ export default {
         grid: {
           top: '25%',
           bottom: '1.5%',
-          left: '5%',
+          left: '2%',
           right: '5%',
           containLabel: true,
         },
@@ -1201,7 +1212,7 @@ export default {
               rotate: 0,
               textStyle: {
                 color: '#A6D3FD',
-                fontSize: 14,
+                fontSize: 10,
               },
             },
             axisPointer: {
@@ -1293,7 +1304,7 @@ export default {
               rotate: 0,
               textStyle: {
                 color: '#A6D3FD',
-                fontSize: 14,
+                fontSize: 10,
               },
             },
             axisPointer: {
@@ -1490,6 +1501,13 @@ export default {
     })
     let detailsGrade = ref([])
     let isAreaon = ref(false)
+    let schoolDefault = ref([])
+    let searchText = reactive({
+      values: '',
+      original: [],
+      loading: false
+    })
+    let timer = ref()
     function showInit () {
       let urls = window.location.href.indexOf('pattern=area')
       let detailsUrl = window.location.href.indexOf('pattern=details')
@@ -1507,13 +1525,36 @@ export default {
       router.push('/home/district')
     }
     function getbasics () {
+      searchText.loading = true
       let urlState = window.location.href.indexOf('pattern=area') != -1 ? true : false
       console.log(urlState)
-      let data = store.state.areaClickCounselor && urlState ? { tmdId: store.state.areaClickCounselor } : { tmdId: JSON.parse(localStorage.getItem('userData')).tmdId }
+      let user = jwt_decode(JSON.parse(localStorage.getItem('id_token')))
+      console.log(user, '人员')
+      let roleA = ''
+      if (user.roles.length > 1) {
+        user.roles.includes('admin') ? roleA = 'admin' :
+          user.roles.includes('leader') ? roleA = 'leader' :
+            user.roles.includes('assist') ? roleA = 'assist' :
+              user.roles.includes('sales') ? roleA = 'sales' :
+                user.roles.includes('rdc') ? roleA = 'rdc' : ''
+      } else {
+        roleA = user.roles[0]
+      }
+      console.log(roleA, '身份')
+      console.log(store.state.areaClickCounselor, '特殊跳过来的')
+
+      // let data = store.state.areaClickCounselor && urlState ? { tmdId: store.state.areaClickCounselor, role: store.state.areaClickRoles } : { tmdId: JSON.parse(localStorage.getItem('userData')).tmdId, role: roleA }
       let ids = store.state.areaClickCounselor && urlState ? store.state.areaClickCounselor : JSON.parse(localStorage.getItem('userData')).tmdId
+      let data = (roleA === 'admin' || roleA === 'leader') && !store.state.areaClickCounselor ? {} :
+        store.state.areaClickCounselor && urlState ? { tmdId: store.state.areaClickCounselor, role: store.state.areaClickRoles } :
+          (roleA !== 'admin' || roleA !== 'leader') && !store.state.areaClickCounselor ? { tmdId: JSON.parse(localStorage.getItem('userData')).tmdId, role: roleA } : ''
+      allAspects(ids)
+      serviceAndversions(ids)
       proxy.$api.getBasicsData(data).then((res) => {
         console.log(res, '返回掌握')
         if (res.state === 200) {
+          schoolDefault.value = res.scRankCnts
+          searchText.original = res.scRankCnts
           headerbasics.value[0].num = res.scAllCnt
           headerbasics.value[1].num = res.tchAllCnt
           headerbasics.value[2].num = res.stuAllCnt
@@ -1526,7 +1567,7 @@ export default {
           allLoding.value.headerData = false
           let beginsData = res.scRankCnts.sort((a, b) => a.openCnt < b.openCnt ? 1 : a.openCnt > b.openCnt ? -1 : 0)
           let activityDataInfo = res.scRankCnts.sort((a, b) => a.actCnt < b.actCnt ? 1 : a.actCnt > b.actCnt ? -1 : 0)
-          let compositeData = res.scRankCnts.sort((a, b) => parseInt(a.openCnt) + parseInt(a.actCnt) + parseInt(a.lessCnt) < parseInt(b.openCnt) + parseInt(b.actCnt) + parseInt(b.lessCnt) ? 1 : parseInt(a.openCnt) + parseInt(a.actCnt) + parseInt(a.lessCnt) > parseInt(b.openCnt) + parseInt(b.actCnt) + parseInt(b.lessCnt) ? -1 : 0)
+          let compositeData = res.scRankCnts.sort((a, b) => parseInt(a.openCnt) + parseInt(a.actCnt) + parseInt(a.lessCnt) + parseInt(a.interCnt) < parseInt(b.openCnt) + parseInt(b.actCnt) + parseInt(b.lessCnt) + parseInt(a.interCnt) ? 1 : parseInt(a.openCnt) + parseInt(a.actCnt) + parseInt(a.lessCnt) + parseInt(a.interCnt) > parseInt(b.openCnt) + parseInt(b.actCnt) + parseInt(b.lessCnt) + parseInt(a.interCnt) ? -1 : 0)
           console.log(beginsData, activityDataInfo, '369')
           for (let i in beginsData) {
             beginsData[i].sort = parseInt(i) + 1
@@ -1545,9 +1586,8 @@ export default {
           comprehensiveData.value = compositeData
           console.log(comprehensiveData.value, '查看1111')
           allLoding.value.rank = false
+          searchText.loading = false
           store.commit('clickCounselor', '')
-          allAspects(ids)
-          serviceAndversions(ids)
         }
       }).catch((error) => {
         ElMessage.error('数据获取API异常')
@@ -1555,9 +1595,23 @@ export default {
     }
     function allAspects (val, state, behinddata) {
       console.log(val, state, behinddata)
-      let data = state === 'details' ? { schoolId: val } : { tmdId: val }
+      //let data = state === 'details' ? { schoolId: val } : { tmdId: val }
+      let user = jwt_decode(JSON.parse(localStorage.getItem('id_token')))
+      let roleA = ''
+      if (user.roles.length > 1) {
+        user.roles.includes('admin') ? roleA = 'admin' :
+          user.roles.includes('leader') ? roleA = 'leader' :
+            user.roles.includes('assist') ? roleA = 'assist' :
+              user.roles.includes('sales') ? roleA = 'sales' :
+                user.roles.includes('rdc') ? roleA = 'rdc' : ''
+      } else {
+        roleA = user.roles[0]
+      }
       let beData = behinddata
       let nowstate = state
+      let data = state === 'details' ? { schoolId: val } :
+        store.state.areaClickCounselor && urlState ? { tmdId: store.state.areaClickCounselor, role: store.state.areaClickRoles } :
+          (roleA === 'admin' || roleA === 'leader') && !store.state.areaClickCounselor && state !== 'details' ? {} : ''
       proxy.$api.getAllaspects(data).then((res) => {
         console.log(res, '课例等的返回')
         if (res.state === 200) {
@@ -1608,7 +1662,21 @@ export default {
       })
     }
     function aspectsInfo (val) {
-      let data = { tmdId: val }
+      // let data = { tmdId: val }
+      let user = jwt_decode(JSON.parse(localStorage.getItem('id_token')))
+      let roleA = ''
+      if (user.roles.length > 1) {
+        user.roles.includes('admin') ? roleA = 'admin' :
+          user.roles.includes('leader') ? roleA = 'leader' :
+            user.roles.includes('assist') ? roleA = 'assist' :
+              user.roles.includes('sales') ? roleA = 'sales' :
+                user.roles.includes('rdc') ? roleA = 'rdc' : ''
+      } else {
+        roleA = user.roles[0]
+      }
+      let data = (roleA === 'admin' || roleA === 'leader') && !store.state.areaClickCounselor ? {} :
+        store.state.areaClickCounselor && urlState ? { tmdId: store.state.areaClickCounselor, role: store.state.areaClickRoles } :
+          (roleA !== 'admin' || roleA !== 'leader') && !store.state.areaClickCounselor ? { tmdId: JSON.parse(localStorage.getItem('userData')).tmdId, role: roleA } : ''
       proxy.$api.getaspects(data).then((res) => {
         console.log(res, '历史全部返回')
         if (res.state == 200) {
@@ -1678,7 +1746,20 @@ export default {
         ElMessage.error('学校参数异常,无法查询服务及版本')
         return
       }
-      let data = { tmdId: val }
+      let user = jwt_decode(JSON.parse(localStorage.getItem('id_token')))
+      let roleA = ''
+      if (user.roles.length > 1) {
+        user.roles.includes('admin') ? roleA = 'admin' :
+          user.roles.includes('leader') ? roleA = 'leader' :
+            user.roles.includes('assist') ? roleA = 'assist' :
+              user.roles.includes('sales') ? roleA = 'sales' :
+                user.roles.includes('rdc') ? roleA = 'rdc' : ''
+      } else {
+        roleA = user.roles[0]
+      }
+      let data = (roleA === 'admin' || roleA === 'leader') && !store.state.areaClickCounselor ? {} :
+        store.state.areaClickCounselor && urlState ? { tmdId: store.state.areaClickCounselor, role: store.state.areaClickRoles } :
+          (roleA !== 'admin' || roleA !== 'leader') && !store.state.areaClickCounselor ? { tmdId: JSON.parse(localStorage.getItem('userData')).tmdId, role: roleA } : ''
       proxy.$api.getServiceandVersions(data).then((res) => {
         console.log(res, '服务和版本占比')
         let xData = []; let datas = []
@@ -1707,10 +1788,10 @@ export default {
           //处理版本占比
           let basicsV = 0; let standardV = 0; let marjorV = 0
           for (let v in res.scInfos) {
-            res.scInfos[v].scale === 0 && res.scInfos[v].size===100 ? basicsV += 1 : ''
+            res.scInfos[v].scale === 0 && res.scInfos[v].size === 100 ? basicsV += 1 : ''
             // res.scInfos[v].scale === 500 && res.scInfos[v].serial.length === 0 && res.scInfos[v].service.length === 0 ? standardV += 1 : ''
-            res.scInfos[v].scale === 300 && res.scInfos[v].size === 500 ? standardV += 1:res.scInfos[v].size > 100 && (!res.scInfos[v].service.includes('YMPCVCIM') && !res.scInfos[v].service.includes('VLY6J6N6') && !res.scInfos[v].service.includes('VABAJ6NV') )  ? standardV += 1:''
-            res.scInfos[v].service.includes('YMPCVCIM') || res.scInfos[v].service.includes('VLY6J6N6') || res.scInfos[v].service.includes('VABAJ6NV') ? marjorV += 1:''
+            res.scInfos[v].scale === 300 && res.scInfos[v].size === 500 ? standardV += 1 : res.scInfos[v].size > 100 && (!res.scInfos[v].service.includes('YMPCVCIM') && !res.scInfos[v].service.includes('VLY6J6N6') && !res.scInfos[v].service.includes('VABAJ6NV')) ? standardV += 1 : ''
+            res.scInfos[v].service.includes('YMPCVCIM') || res.scInfos[v].service.includes('VLY6J6N6') || res.scInfos[v].service.includes('VABAJ6NV') ? marjorV += 1 : ''
             // res.scInfos[v].serial.length !== 0 && res.scInfos[v].service.length !== 0 && res.scInfos[v].scale === 500 ? marjorV += 1 : ''
           }
           echartsData.value.edition.series[0].data[0].value = basicsV
@@ -1764,10 +1845,10 @@ export default {
         console.log(res, '年级、科目')
         if (res.state === 200) {
           //处理显示学校版本 
-          console.log(schoolDeatils.value,'查看标准')
-          schoolDeatils.value.versions = schoolDeatils.value.scale === 0 && schoolDeatils.value.size <=100 ? '基础版' : 
-          schoolDeatils.value.scale === 300 && schoolDeatils.value.size === 500 ?'标准版':schoolDeatils.value.size > 100 && (!res.school.service.includes('YMPCVCIM') && !res.school.service.includes('VLY6J6N6') && !res.school.service.includes('VABAJ6NV') )  ? '标准版':
-          res.school.service.includes('YMPCVCIM') || res.school.service.includes('VLY6J6N6') || res.school.service.includes('VABAJ6NV') ?'专业版':''
+          console.log(schoolDeatils.value, '查看标准')
+          schoolDeatils.value.versions = schoolDeatils.value.scale === 0 && schoolDeatils.value.size <= 100 ? '基础版' :
+            schoolDeatils.value.scale === 300 && schoolDeatils.value.size === 500 ? '标准版' : schoolDeatils.value.size > 100 && (!res.school.service.includes('YMPCVCIM') && !res.school.service.includes('VLY6J6N6') && !res.school.service.includes('VABAJ6NV')) ? '标准版' :
+              res.school.service.includes('YMPCVCIM') || res.school.service.includes('VLY6J6N6') || res.school.service.includes('VABAJ6NV') ? '专业版' : ''
           //处理年级占比
           let Xdata = [];
           if (res.gradeCnt.length !== 0) {
@@ -1913,11 +1994,31 @@ export default {
       detailsEchart.value.bar3.series[0].data = []
       detailsEchart.value.line.series[0].data = []
     }
+    function debounce (fn, wait) {
+      if (timer.value !== null) {
+        clearTimeout(timer.value)
+      }
+      timer.value = setTimeout(fn, wait)
+    }
+    function personnelSearch () {
+      let arr = schoolDefault.value
+      let newArr = arr.filter((item) => {
+        return item.name.includes(searchText.values) || item.id.includes(searchText.values)
+      })
+      schoolDefault.value = newArr
+    }
     showInit()
     watch(store.state.areaClickschool, (newdata) => {
       console.log(newdata, '是否触发')
       newdata.hasOwnProperty('name') ? (showState.value = 'particular', schoolDeatils.value = newdata, schoolDeatilsInfo(newdata.id), isAreaon.value = true) : ''
     }, { immediate: true, deep: true })
+    watch(() => searchText.values, (newvalues) => {
+      if (newvalues.trim().length !== 0) {
+        debounce(personnelSearch, 500)
+      } else {
+        schoolDefault.value = searchText.original
+      }
+    })
     return {
       headerbasics,
       echartsData,
@@ -1953,7 +2054,12 @@ export default {
       detailsGrade,
       detailsLoding,
       rankMsg,
-      serviceArr
+      serviceArr,
+      schoolDefault,
+      searchText,
+      timer,
+      debounce,
+      personnelSearch
     }
   },
 }
@@ -2089,10 +2195,17 @@ export default {
   display: flex;
   justify-content: space-between;
 }
+.schoolList-all {
+  width: 100%;
+  height: 35vh;
+  padding: 1%;
+  background-color: #fff;
+}
 .school-list-title {
   font-size: 14px;
   color: #b2bec3;
   font-weight: 700;
+  text-align: left;
 }
 .schoolListbox {
   display: flex;
@@ -2314,6 +2427,9 @@ export default {
   color: rgba(112, 103, 245, 0.4);
   margin-top: 1.5%;
 }
+.school-tables {
+  padding: 0% 1%;
+}
 </style>
 <style>
 /* .school-list .el-table .el-table__cell {

+ 10 - 9
TEAMModelBI/ClientApp/src/view/systemConfig/apimanage.vue

@@ -709,29 +709,30 @@ export default ({
           rankBar.value.series[0].data = rankdata
           tableData.value = newArr
 
-          //默认显示最近两个小时的分钟
+          //默认显示最近两个小时的分钟 or 如果只有一个小时的数据 就是最近一小时的
           let minutex = []; let minuteData = []; let ipData = []; let areaDatas = []
-          if (res.days[res.days.length - 2].value.minCnts) {
-            res.days[res.days.length - 2].value.minCnts.forEach((itemM) => {
+          let lengNum = res.days.length > 1 ? 2 : 1
+          if (res.days[res.days.length - lengNum].value.minCnts) {
+            res.days[res.days.length - lengNum].value.minCnts.forEach((itemM) => {
               minutex.push(itemM.minute)
               minuteData.push(itemM.cnt)
             })
-            res.days[res.days.length - 2].value.ipCnt = res.days[res.days.length - 2].value.ipCnt.sort(function (a, b) {
+            res.days[res.days.length - lengNum].value.ipCnt = res.days[res.days.length - lengNum].value.ipCnt.sort(function (a, b) {
               if (a.count - b.count < 0) { return 1 }
               if (a.count - b.count > 0) { return -1 }
             })
-            res.days[res.days.length - 2].value.ipCnt.forEach((ipitem, index) => {
+            res.days[res.days.length - lengNum].value.ipCnt.forEach((ipitem, index) => {
               index < 24 ? ipData.push({ name: ipitem.ip + '(' + ipitem.count + ')', value: ipitem.count }) : ''
             })
             // let ipsDa = res.days[res.days.length - 2].value.ipCnt
             // for (let i in ipsDa) {
             //   i < 25 ? ipData.push({ name: ipsDa[i].ip + '(' + ipsDa[i].count + ')', value: ipsDa[i].count }) : ''
             // }
-            res.days[res.days.length - 2].value.regionCnts = res.days[res.days.length - 2].value.regionCnts.sort(function (x, y) {
+            res.days[res.days.length - lengNum].value.regionCnts = res.days[res.days.length - lengNum].value.regionCnts.sort(function (x, y) {
               if (x.count - y.count < 0) { return 1 }
               if (x.count - y.count > 0) { return -1 }
             })
-            res.days[res.days.length - 2].value.regionCnts.forEach((areaitem) => {
+            res.days[res.days.length - lengNum].value.regionCnts.forEach((areaitem) => {
               areaDatas.push({ name: areaitem.region, value: areaitem.count })
             })
             //数据差距放大规则
@@ -752,8 +753,8 @@ export default ({
             minuteBar.value.series[0].data = minuteData
             ipQie.value.series[0].data = ipData
             areaQie.value.series[0].data = showdata
-            console.log(res.days[res.days.length - 2].key.substr(0, 1), '时间')
-            let time = res.days[res.days.length - 2].key.substr(0, 1) === 0 ? Number(res.days[res.days.length - 2].key.substr(1, 1)) + discrepancy : Number(res.days[res.days.length - 2].key) + discrepancy
+            console.log(res.days[res.days.length - lengNum].key.substr(0, 1), '时间')
+            let time = res.days[res.days.length - lengNum].key.substr(0, 1) === 0 ? Number(res.days[res.days.length - lengNum].key.substr(1, 1)) + discrepancy : Number(res.days[res.days.length - lengNum].key) + discrepancy
             nowatTime.value = time
           } else {
             minuteShow.value = true

+ 95 - 22
TEAMModelBI/ClientApp/src/view/systemConfig/correlation.vue

@@ -16,11 +16,11 @@
         <el-table-column label="校徽" align="center">
           <template #default="scope">
             <el-image style="width: 40px; height: 40px;" :src="scope.row.picture" fit="fill" v-if="scope.row.picture"></el-image>
-            <div class="notimage" v-else>暂无<br/>图片</div>
+            <div class="notimage" v-else>暂无<br />图片</div>
           </template>
         </el-table-column>
-        <el-table-column prop="name" label="名称" align="center"/>
-        <el-table-column prop="id" label="学校简码" align="center"/>
+        <el-table-column prop="name" label="名称" align="center" />
+        <el-table-column prop="id" label="学校简码" align="center" />
         <!-- <el-table-column prop="name" label="版本" />
                 <el-table-column prop="name" label="目前顾问" /> -->
         <el-table-column fixed="right" label="操作" width='80' align="center" v-if="PowerShow">
@@ -50,13 +50,23 @@
           <el-table-column label="校徽" align="center">
             <template #default="scope">
               <el-image style="width: 40px; height: 40px;" :src="scope.row.picture" fit="fill" v-if="scope.row.picture"></el-image>
-              <div class="notimage" v-else>暂无<br/>图片</div>
+              <div class="notimage" v-else>暂无<br />图片</div>
             </template>
           </el-table-column>
-          <el-table-column prop="name" label="名称" align="center"/>
-          <el-table-column prop="id" label="学校简码" align="center"/>
+          <el-table-column prop="name" label="名称" align="center" />
+          <el-table-column prop="id" label="学校简码" align="center" />
           <!-- <el-table-column prop="name" label="版本" /> -->
-          <el-table-column prop="assisName" label="目前顾问" align="center"/>
+          <el-table-column prop="assisName" label="目前顾问" align="center" />
+          <el-table-column align="center">
+            <template #header>
+              <el-select v-model="areaList.value" placeholder="Select" size="small" @change="getAreaschool">
+                <el-option v-for="item in areaList.data" :key="item.value" :label="item.label" :value="item.value" />
+              </el-select>
+            </template>
+            <template #default="scope">
+              <span class="areaitem-name">{{scope.row.areaName}}</span><span class="areaitem-insti">({{scope.row.institution}})</span>
+            </template>
+          </el-table-column>
           <el-table-column fixed="right" label="操作" width='80' align="center" v-if="PowerShow">
             <template #default="scope">
               <el-button type="text" size="small" @click="correlation(scope.row,tableData)">关联学校</el-button>
@@ -93,9 +103,15 @@ export default {
     let timer = ref('')
     let nextpageToken = ref('')
     let scrollHeight = ref('init')
-    let loadingData=ref({
-      possess:true,
-      list:false,
+    let loadingData = ref({
+      possess: true,
+      list: false,
+    })
+    let areaList = ref({
+      value: 'all',
+      data: [
+        { value: 'all', label: '全部', situate: 'all' },
+      ]
     })
     onMounted(() => {
       //监听表格滚动事件
@@ -111,13 +127,13 @@ export default {
         console.log(scrollHeight.value, '值')
       }
     }
-    function getSchoolList (value) {
+    function getSchoolList (value, state) {
       console.log(value, '触发下一页')
-      if (nextpageToken.value == null) {
+      if (nextpageToken.value == null && state !== 'restart') {
         ElMessage.success('已经到最底了')
         return
       }
-      loadingData.value.list=true
+      loadingData.value.list = true
       let data = value ? { contToken: value } : {}
       proxy.$api
         .getSchooldata(data)
@@ -135,6 +151,17 @@ export default {
               }
             }
             nextpageToken.value = res.continuationToken
+            //处理学区呈现
+            console.log(areaList.value.data, '是否有数据')
+            for (let y in res.schoolAssists) {
+              let areaValue = res.schoolAssists[y].areaId
+              res.schoolAssists[y].areaName = ''
+              res.schoolAssists[y].institution = ''
+              for (let i in areaList.value.data) {
+                areaValue === areaList.value.data[i].value ? (res.schoolAssists[y].areaName = areaList.value.data[i].label, res.schoolAssists[y].institution = areaList.value.data[i].situate) : ''
+              }
+            }
+            console.log(res.schoolAssists, '是否有数据')
             if (!value) {
               tableData.value = res.schoolAssists;
               original.value = JSON.parse(JSON.stringify(res.schoolAssists));
@@ -239,8 +266,8 @@ export default {
       })
     }
     function processingSchool (state) {
-      console.log(tableData.value,original.value,'原始数据和变更数据')
-      state ? tableData.value=original.value:''
+      console.log(tableData.value, original.value, '原始数据和变更数据')
+      state ? tableData.value = original.value : ''
       let allSchool = JSON.parse(JSON.stringify(tableData.value))
       let handleSchools = nowUsers.value.handleSchools
       for (let i in handleSchools) {
@@ -250,7 +277,7 @@ export default {
         }
       }
       tableData.value = allSchool
-      loadingData.value.list=false
+      loadingData.value.list = false
     }
     function debounce (fn, wait) {
       if (timer.value !== null) {
@@ -280,8 +307,45 @@ export default {
         ElMessage.error('搜索学校失败,API异常')
       })
     }
-    function loadData () {
-
+    //加载所有学区信息
+    function areaData () {
+      proxy.$api.getCapacity({}).then((res) => {
+        console.log(res, '学区信息')
+        res.state === 200 ? res.areas.forEach((item) => { areaList.value.data.push({ value: item.id, label: item.name, situate: item.institution }) }) : ''
+      }).catch((error) => {
+        ElMessage.error('API异常,获取学区失败')
+      })
+    }
+    //筛选学区数据列表
+    function getAreaschool (value) {
+      console.log(value, 'V')
+      tableData.value = []
+      let data = value !== 'all' ? { areaId: value } : getSchoolList('', 'restart')
+      if (value !== 'all') {
+        loadingData.value.list = true
+        proxy.$api.getForareaSchool(data).then((res) => {
+          console.log(res, '现在区域拥有的学校')
+          if (res.state === 200) {
+            for (let y in res.joinAreaSchools) {
+              let areaValue = res.joinAreaSchools[y].areaId
+              res.joinAreaSchools[y].areaName = ''
+              res.joinAreaSchools[y].institution = ''
+              res.joinAreaSchools[y].assisName = ''
+              for (let i in areaList.value.data) {
+                areaValue === areaList.value.data[i].value ? (res.joinAreaSchools[y].areaName = areaList.value.data[i].label, res.joinAreaSchools[y].institution = areaList.value.data[i].situate) : ''
+              }
+              for (let e in res.joinAreaSchools[y].assists) {
+                res.joinAreaSchools[y].assisName !== '' ? res.joinAreaSchools[y].assisName = res.joinAreaSchools[y].assisName + ',' + res.joinAreaSchools[y].assists[e].tmdName : res.joinAreaSchools[y].assisName = res.joinAreaSchools[y].assists[e].tmdName
+              }
+            }
+            tableData.value = res.joinAreaSchools
+          }
+          loadingData.value.list = false
+        }).catch((error) => {
+          ElMessage.error('获取筛选学校列表失败')
+          loadingData.value.list = false
+        })
+      }
     }
     watch(scrollHeight, (newdata, olddata) => {
       console.log(newdata, olddata, '监听的数据')
@@ -290,7 +354,7 @@ export default {
     watch(
       props,
       (newuser) => {
-        newuser ? (nowUsers.value = newuser.userdata,loadingData.value.possess=false,processingSchool(true)) : ''
+        newuser ? (nowUsers.value = newuser.userdata, loadingData.value.possess = false, processingSchool(true)) : ''
         console.log(nowUsers.value, '触发监听')
       },
       { immediate: true, deep: true }
@@ -303,6 +367,7 @@ export default {
       }
     })
     getSchoolList()
+    areaData()
     return {
       tableData,
       tableDatas,
@@ -319,8 +384,10 @@ export default {
       timer,
       personnelSearch,
       scrollHeight,
-      loadData,
-      loadingData
+      areaData,
+      loadingData,
+      areaList,
+      getAreaschool
     }
   },
 }
@@ -399,7 +466,7 @@ export default {
   width: 30%;
   text-align: right;
 }
-.notimage{
+.notimage {
   width: 40px;
   height: 40px;
   line-height: 20px;
@@ -409,6 +476,12 @@ export default {
   color: #ecf0f1;
   margin: 0 auto;
 }
+.areaitem-name {
+  color: #409eff;
+}
+.areaitem-insti {
+  color: #909399;
+}
 </style>
 <style>
 .school-list-header .el-button--small {

+ 4 - 4
TEAMModelBI/ClientApp/src/view/teachermanage/manage.vue

@@ -51,7 +51,6 @@
       </el-table-column>
       <el-table-column prop="name" :label="$t(`personnelManagement.personnelTable.name`)" sortable align="center" />
       <!-- <el-table-column prop="isexist" label="状态" width="250" align="center" /> -->
-
       <el-table-column prop="mobile" :label="$t(`personnelManagement.personnelTable.phone`)" align="center" />
       <el-table-column prop="tmdId" :label="$t(`personnelManagement.personnelTable.tmdids`)" align="center">
       </el-table-column>
@@ -90,15 +89,15 @@
     </el-table>
   </div>
   <div class="personnel-drawer">
-    <el-drawer v-model="drawer" title="用户相关信息配置" :direction="direction" :modal="true" :before-close="closeDrawer()">
+    <el-drawer v-model="drawer" title="用户相关信息配置" :direction="direction" :modal="true" :before-close="closeDrawer()" size="40%">
       <el-tabs v-model="activeName" class="demo-tabs">
         <el-tab-pane label="权限/身份" name="first">
           <Operates ref="roaming" :userdata="nowUser" @changeShow="changeState"></Operates>
         </el-tab-pane>
-        <el-tab-pane label="关联学校" name="second" v-if="nowUser.handleRoles.includes('assist') || nowUser.handleRoles.includes('admin')">
+        <el-tab-pane label="关联学校" name="second" v-if="(nowUser.handleRoles.includes('assist') || nowUser.handleRoles.includes('admin')) && nowUser.tmdId !==''">
           <Correlation :userdata="nowUser"></Correlation>
         </el-tab-pane>
-        <el-tab-pane label="管理学校" name="manegeschool">
+        <el-tab-pane label="管理学校" name="manegeschool" v-if="nowUser.tmdId !==''">
           <Manageschool :userdata="nowUser"></Manageschool>
         </el-tab-pane>
       </el-tabs>
@@ -313,6 +312,7 @@ export default {
       nowUser.value = data[index]
       console.log(nowUser.value)
       drawer.value = true
+      console.log(nowUser.value)
       // let userData = data.value[index]
       // nowUser.value = data.value[index]
       // let manaPermission = JSON.parse(localStorage.getItem('management'))

+ 116 - 12
TEAMModelBI/ClientApp/src/view/teachermanage/traitmanage.vue

@@ -1,10 +1,10 @@
 <template>
   <div class="abilitysInfo">
     <!--外部呈现-->
-    <div class="site-box" v-show="siteCut.show && treeModel === 'default'">
+    <!-- <div class="site-box" v-show="siteCut.show && treeModel === 'default'">
       <span :class="[siteCut.positionIndex ===1 ? 'site-pitch':'']" @click="cutSite(1,'china')">中国</span>
       <span :class="[siteCut.positionIndex ===2 ? 'site-pitch':'']" @click="cutSite(2,'international')">国际</span>
-    </div>
+    </div> -->
     <div class="allbox" v-if="treeModel === 'default'">
       <!-- <el-tabs type="border-card">
                 <el-tab-pane :label="$t(`abilityManages.titlegather`)">
@@ -14,7 +14,7 @@
             </el-tabs> -->
       <div class="cardlist-card" v-if="showPattern.state">
         <!--预设-->
-        <div class="readybox">
+        <div class="readybox" v-loading="loadingData.preinstall" element-loading-text="数据加载中...">
           <div class="cardlist-card-title">
             <div class="cardlist-card-title-icon">
               <svg class="cardtitleIcon" aria-hidden="true">
@@ -25,11 +25,23 @@
           </div>
           <div class="preinstallbox">
             <div class="cardbox" v-for="(item, index) in abilityProject" :key="item.id">
-              <div class="mask1" @click="pitch(item)">
-                <el-image :src="redactImg" :fit="fit" />
-                <p class="hovertitles">编辑当前微能力点</p>
+              <div class="mask1">
+                <div v-if="item.id !=='bde5c011-2ae4-461a-b46c-5483ba72ae45'" @click="pitch(item)">
+                  <el-image :src="redactImg" :fit="fit" />
+                  <div class="hovertitles" v-if="item.id !=='bde5c011-2ae4-461a-b46c-5483ba72ae45'">编辑当前微能力点</div>
+                </div>
+                <div class="intactbox" v-else>
+                  <div class="intact-left" @click="pitch(item)">
+                    <el-image :src="intactImg.view" :fit="fit" class="intactimg" />
+                    <div class="hovertitles-intact">查看微能力点</div>
+                  </div>
+                  <div class="intact-right" @click="synchronize">
+                    <el-image :src="intactImg.synchronization" :fit="fit" class="intactimg" />
+                    <div class="hovertitles-intact">同步微能力点</div>
+                  </div>
+                </div>
               </div>
-              <el-card class="box-card">
+              <el-card :class="[item.id==='bde5c011-2ae4-461a-b46c-5483ba72ae45' ? 'intact':'','box-card']">
                 <!-- <div class="card-number">{{ index + 1 }}</div> -->
                 <template #header>
                   <div class="card-header">
@@ -44,12 +56,17 @@
                   <p class="source">{{ $t(`abilityManages.affiliation`) }}:<span class="source-title">{{item.institution}}</span></p>
                 </div>
               </el-card>
+              <!-- <div class="signboard" v-show="item.id==='bde5c011-2ae4-461a-b46c-5483ba72ae45'" title="即时同步" @click="synchronize">
+                <svg class="synchronization" aria-hidden="true">
+                  <use xlink:href="#icon-shangpintongbu"></use>
+                </svg>
+              </div> -->
             </div>
           </div>
         </div>
         <!--预设end-->
         <!--引用-->
-        <div class="readybox citeboxs">
+        <div class="readybox citeboxs" v-loading="loadingData.cite" element-loading-text="数据加载中...">
           <div class="cardlist-card-title">
             <div class="cardlist-card-title-icon">
               <svg class="cardtitleIcon" aria-hidden="true">
@@ -355,7 +372,7 @@ import { onMounted, reactive, getCurrentInstance, ref, toRefs, watch } from 'vue
 import { useStore } from 'vuex'
 import AbilityTree from '@/components/AbilityTree.vue'
 import dimension from '@/static/dimension.js'
-import { ElMessage, ElMessageBox } from 'element-plus'
+import { ElMessage, ElMessageBox, ElLoading } from 'element-plus'
 export default {
   components: {
     AbilityTree,
@@ -563,12 +580,20 @@ export default {
     let treeDataInfo = ref([])
     //编辑图片
     const redactImg = require('@/assets/img/redacts.png')
+    const intactImg = {
+      view: require('@/assets/img/view.png'),
+      synchronization: require('@/assets/img/synchronization.png')
+    }
     //站点切换呈现
     let siteCut = ref({
       show: true,
       positionIndex: 1,
       value: 'china',
     })
+    let loadingData = ref({
+      preinstall: false,
+      cite: false,
+    })
     //获取到方案下的能力点,并默认选中第一个
     function pitch (data) {
       console.log(data, '查询册别传入的data')
@@ -850,16 +875,43 @@ export default {
     }
     //进入获取微能力点并区分
     function getAbilitys () {
+      loadingData.value.preinstall = true
+      loadingData.value.cite = true
+      console.log(loadingData, '状态')
       proxy.$api.getCapacity({}).then((res) => {
         console.log(res, '微能力返回')
         if (res.state === 200) {
+          res.areas.map((item, index) => {
+            item.id === 'bde5c011-2ae4-461a-b46c-5483ba72ae45' ? res.areas.unshift(res.areas.splice(index, 1)[0]) : ''
+          })
           for (let i in res.areas) {
-            i < 3 ? abilityProject.push(res.areas[i]) : citeAbility.value.push(res.areas[i])
+            i < 4 ? abilityProject.push(res.areas[i]) : citeAbility.value.push(res.areas[i])
           }
           store.commit('getPoint', res.areas)
+          loadingData.value.preinstall = false
+          loadingData.value.cite = false
         }
       }).catch((error) => {
         ElMessage.error('微能力获取异常')
+        loadingData.value.preinstall = false
+        loadingData.value.cite = false
+      })
+      console.log(loadingData, '状态变化')
+    }
+    //同步完整方案操作
+    function synchronize () {
+      let loading = ElLoading.service({
+        lock: true,
+        text: '正在同步,内容较多,需要一定时间。请稍后...',
+        background: 'rgba(0, 0, 0, 0.7)',
+      })
+      proxy.$api.getSynchronize({}).then((res) => {
+        console.log(res, '同步操作')
+        res.state === 200 ? ElMessage.success('同步成功') : ''
+        loading.close()
+      }).catch((error) => {
+        ElMessage.error('同步操作失败,API异常')
+        loading.close()
       })
     }
     // onMounted(() => {
@@ -932,7 +984,10 @@ export default {
       cutSite,
       siteCut,
       citeAbility,
-      nowPitch
+      nowPitch,
+      synchronize,
+      intactImg,
+      loadingData
     }
   },
 }
@@ -952,11 +1007,12 @@ export default {
   margin-top: 10px;
   position: relative;
   cursor: pointer;
+  margin-left: 1%;
 }
 .cardbox .box-card {
   background: rgba(116, 185, 255, 0.3);
 }
-.cardbox:hover .mask1 {
+.mask1:hover {
   opacity: 1;
 }
 /* .cardbox:hover {
@@ -991,6 +1047,7 @@ export default {
   align-items: center;
   border-bottom: 0px;
   /* background: #bdc3c7; */
+  position: relative;
 }
 
 .text {
@@ -1611,6 +1668,47 @@ export default {
   left: 5px;
   top: 5px;
 }
+.intact {
+  background: rgba(30, 144, 255, 0.5) !important;
+}
+.synchronization {
+  width: 25px;
+  height: 25px;
+  vertical-align: 0em;
+  fill: currentColor;
+  overflow: hidden;
+  /* margin-right: 25px; */
+  margin-left: 0px;
+}
+.intactbox {
+  width: 100%;
+  height: 100%;
+  display: flex;
+}
+.intact-left,
+.intact-right {
+  width: 38%;
+  padding: 1.5%;
+  margin: 7%;
+  border: 1px solid #ecf0f1;
+  border-radius: 10px;
+  box-shadow: 0 1px 1px rgba(99, 110, 114, 1),
+    0 2px 2px rgba(99, 110, 114, 1) 0 4px 4px rgba(99, 110, 114, 1),
+    0 8px 8px rgba(99, 110, 114, 1);
+}
+.intactimg {
+  width: 45% !important;
+  margin-top: 40% !important;
+}
+.hovertitles-intact {
+  font-size: 14px;
+  font-weight: bold;
+  color: rgba(255, 255, 255, 0.9);
+}
+.intact-left:hover,
+.intact-right:hover {
+  background-color: rgba(99, 110, 114, 1) !important;
+}
 @keyframes mymove {
   0% {
     top: -35px;
@@ -1776,5 +1874,11 @@ export default {
   .compulsorydialog .el-dialog {
     height: 80vh;
   }
+  .cardbox {
+    width: 24% !important;
+  }
+  .information-box .source {
+    line-height: 40px !important;
+  }
 }
 </style>

+ 42 - 14
TEAMModelBI/Controllers/BIHome/OnLineController.cs

@@ -6,6 +6,7 @@ using Microsoft.AspNetCore.Mvc;
 using StackExchange.Redis;
 using System;
 using System.Collections.Generic;
+using System.Data.SqlTypes;
 using System.IO;
 using System.Linq;
 using System.Text;
@@ -81,6 +82,7 @@ namespace TEAMModelBI.Controllers.BIHome
             int todayTchCnt = 0; //今日新增教师
             int todayStuCnt = 0;   //今日新增学生数
 
+            long datetime = dateTime.ToUnixTimeMilliseconds();
             string currentSql = "select value(count(c.id)) from c";
 
             areaCnt = await CommonFind.GetSqlValueCount(cosmosClient, "Normal", currentSql, "Base-Area");
@@ -117,8 +119,8 @@ namespace TEAMModelBI.Controllers.BIHome
                 }
             }
             apiCnt = recCnts.Select(x => x.apiCnt.Select(s => s.count).Sum()).Sum();
-
-            return Ok(new { state = 200, areaCnt, scCnt, tchCnt, stuCnt, todayScCnt, todayTchCnt, todayStuCnt, onStuCnt, onTchCnt, apiCnt });
+            string yymmss = TimeHelper.GetDateTime(datetime).ToString();
+            return Ok(new { state = 200, yymmss, datetime, areaCnt, scCnt, tchCnt, stuCnt, todayScCnt, todayTchCnt, todayStuCnt, onStuCnt, onTchCnt, apiCnt });
         }
 
         /// <summary>
@@ -409,9 +411,32 @@ namespace TEAMModelBI.Controllers.BIHome
                 scEdCnt.Add(item);
             }
 
-            scEdCnt.ForEach(async scProCnt =>
+            //scEdCnt.ForEach(async scProCnt =>
+            //{
+            //    var response = await cosmosClient.GetContainer("TEAMModelOS", "School").ReadItemStreamAsync(scProCnt.id, new PartitionKey("ProductSum"));
+            //    if (response.Status == 200)
+            //    {
+            //        using var json = await JsonDocument.ParseAsync(response.ContentStream);
+            //        SchoolProductSum ScProductSum = json.ToObject<SchoolProductSum>();
+            //        //scProCnt.serial = ScProductSum.serial.Count();
+            //        //scProCnt.service = ScProductSum.service.Count();
+            //        //scProCnt.hard = ScProductSum.hard.Count();
+            //        List<string> pSeriCnt = ScProductSum.serial.Select(s=> s.prodCode).ToList();
+            //        List<string> pServCnt = ScProductSum.service.Select(s=>s.prodCode).ToList();
+            //        List<string> pHardCnt = ScProductSum.hard.Select(s=>s.prodCode).ToList();
+            //        scProCnt.serial = pSeriCnt;
+            //        scProCnt.service = pServCnt;
+            //        scProCnt.hard = pHardCnt;
+            //        if (scProCnt.scale >= 500 && (pSeriCnt.Count > 0 || pServCnt.Count > 0 || pHardCnt.Count > 0)) peCnt += 1;
+            //    }
+
+            //    if (scProCnt.scale >= 500 && scProCnt.serial.Count == 0 && scProCnt.service.Count == 0 && scProCnt.hard.Count == 0) seCnt += 1;
+            //    if (scProCnt.scale == 0) beCnt += 1;
+            //});
+
+            foreach (var item in scEdCnt)
             {
-                var response = await cosmosClient.GetContainer("TEAMModelOS", "School").ReadItemStreamAsync(scProCnt.id, new PartitionKey("ProductSum"));
+                var response = await cosmosClient.GetContainer("TEAMModelOS", "School").ReadItemStreamAsync(item.id, new PartitionKey("ProductSum"));
                 if (response.Status == 200)
                 {
                     using var json = await JsonDocument.ParseAsync(response.ContentStream);
@@ -419,18 +444,21 @@ namespace TEAMModelBI.Controllers.BIHome
                     //scProCnt.serial = ScProductSum.serial.Count();
                     //scProCnt.service = ScProductSum.service.Count();
                     //scProCnt.hard = ScProductSum.hard.Count();
-                    List<string> pSeriCnt = ScProductSum.serial.Select(s=> s.prodCode).ToList();
-                    List<string> pServCnt = ScProductSum.service.Select(s=>s.prodCode).ToList();
-                    List<string> pHardCnt = ScProductSum.hard.Select(s=>s.prodCode).ToList();
-                    scProCnt.serial = pSeriCnt;
-                    scProCnt.service = pServCnt;
-                    scProCnt.hard = pHardCnt;
-                    if (scProCnt.scale >= 500 && (pSeriCnt.Count > 0 || pServCnt.Count > 0 || pHardCnt.Count > 0)) peCnt += 1;
+                    List<string> pSeriCnt = ScProductSum.serial.Select(s => s.prodCode).ToList();
+                    List<string> pServCnt = ScProductSum.service.Select(s => s.prodCode).ToList();
+                    List<string> pHardCnt = ScProductSum.hard.Select(s => s.prodCode).ToList();
+                    item.serial = pSeriCnt;
+                    item.service = pServCnt;
+                    item.hard = pHardCnt;
+                    if (item.scale >= 500 && (pSeriCnt.Count > 0 || pServCnt.Count > 0 || pHardCnt.Count > 0)) peCnt += 1;
                 }
 
-                if (scProCnt.scale >= 500 && scProCnt.serial.Count == 0 && scProCnt.service.Count == 0 && scProCnt.hard.Count == 0) seCnt += 1;
-                if (scProCnt.scale == 0) beCnt += 1;
-            });
+                if (item.scale >= 500 && item.serial.Count == 0 && item.service.Count == 0 && item.hard.Count == 0) seCnt += 1;
+                if (item.scale == 0) beCnt += 1;
+            }
+
+
+
 
             return Ok(new { state = 200, beCnt, seCnt, peCnt, scEdCnt });
         }

+ 8 - 0
TEAMModelBI/Controllers/BINormal/AreaRelevantController.cs

@@ -22,6 +22,7 @@ using TEAMModelOS.SDK.Extension;
 using TEAMModelOS.SDK.Models;
 using TEAMModelOS.SDK.Models.Cosmos.BI;
 using Top.Api;
+using static TEAMModelBI.Controllers.BITest.TestController;
 
 namespace TEAMModelBI.Controllers.BINormal
 {
@@ -106,6 +107,12 @@ namespace TEAMModelBI.Controllers.BINormal
                                 joinAreaSchool.areas = obj.GetProperty("areas").ToObject<List<SchoolArea>>();
                             }
                             catch { }
+
+                            if (!string.IsNullOrEmpty($"{joinAreaSchool.id}")) 
+                            {
+                                joinAreaSchool.assists = await CommonFind.FindSchoolRoles(cosmosClient, joinAreaSchool.id, "assist");
+                            }
+
                             joinAreaSchools.Add(joinAreaSchool);
                         }                    
                     }
@@ -604,6 +611,7 @@ namespace TEAMModelBI.Controllers.BINormal
             public string city { get; set; }
             public string dist { get; set; }
             public List<SchoolArea> areas { get; set; } = new List<SchoolArea>();
+            public List<SchoolTeacherRoles> assists { get; set; } = new List<SchoolTeacherRoles>();
         }
 
     }

+ 310 - 24
TEAMModelBI/Controllers/BINormal/BatchAreaController.cs

@@ -32,6 +32,8 @@ using System.Text;
 using DocumentFormat.OpenXml.Bibliography;
 using Microsoft.Extensions.Hosting;
 using Microsoft.AspNetCore.Hosting;
+using static TEAMModelBI.Controllers.RepairApi.InitialAreaController;
+using TEAMModelOS.SDK.Context.Constant;
 
 namespace TEAMModelBI.Controllers.BINormal
 {
@@ -779,19 +781,17 @@ namespace TEAMModelBI.Controllers.BINormal
                     }
                 }
                 StandardFile tempFile = new();
-                try
-                {
-                    tempFile = await cosmosClient.GetContainer(Constant.TEAMModelOS, "Normal").ReadItemAsync<StandardFile>(saveFile.id, new PartitionKey("StandardFile"));
-                }
-                catch
+                if (saveFile.id != null)
                 {
-                }
-                if (tempFile.id != null)
-                {
-                    if (tempFile.id.Equals(saveFile.id))
-                        await cosmosClient.GetContainer(Constant.TEAMModelOS, "Normal").ReplaceItemAsync<StandardFile>(saveFile, saveFile.id, new PartitionKey("StandardFile")); //直接替换以前的数据
-                    else
-                        await cosmosClient.GetContainer(Constant.TEAMModelOS, "Normal").CreateItemAsync(saveFile, new PartitionKey($"StandardFile")); // 需要删除原来的政策文件数据在进行添加
+                    var respFile = await cosmosClient.GetContainer(Constant.TEAMModelOS, "Normal").ReadItemStreamAsync(saveFile.id, new PartitionKey("StandardFile"));
+                    if (respFile.Status == 200)
+                    {
+                        using var json = await JsonDocument.ParseAsync(respFile.ContentStream);
+                        tempFile = json.ToObject<StandardFile>();
+                        await cosmosClient.GetContainer(Constant.TEAMModelOS, "Normal").DeleteItemAsync<StandardFile>(tempFile.id, new PartitionKey("StandardFile"));
+                    }
+
+                    await cosmosClient.GetContainer(Constant.TEAMModelOS, "Normal").CreateItemAsync(saveFile, new PartitionKey($"StandardFile")); // 需要删除原来的政策文件数据在进行添加
                 }
 
                 //新的区域设置
@@ -808,19 +808,17 @@ namespace TEAMModelBI.Controllers.BINormal
                 }
 
                 AreaSetting tempSetting = new();
-                try
-                {
-                    tempSetting = await cosmosClient.GetContainer(Constant.TEAMModelOS, "Normal").ReadItemAsync<AreaSetting>(saveSetting.id, new PartitionKey("StandardFile"));
-                }
-                catch
-                {
-                }
-                if (tempSetting.id != null)
+                if (saveSetting.id != null)
                 {
-                    if (tempSetting.id.Equals(saveSetting.id))
-                        await cosmosClient.GetContainer(Constant.TEAMModelOS, "Normal").ReplaceItemAsync<AreaSetting>(saveSetting, saveSetting.id, new PartitionKey($"AreaSetting"));   //直接替换以前的数据
-                    else
-                        await cosmosClient.GetContainer(Constant.TEAMModelOS, "Normal").CreateItemAsync(saveSetting, new PartitionKey($"AreaSetting"));  //需要删除原来的区域设置数据在进行添加
+                    var respSetting = await cosmosClient.GetContainer(Constant.TEAMModelOS, "Normal").ReadItemStreamAsync(saveSetting.id, new PartitionKey("AreaSetting"));
+                    if (respSetting.Status == 200)
+                    {
+                        using var json = await JsonDocument.ParseAsync(respSetting.ContentStream);
+                        tempSetting = json.ToObject<AreaSetting>();
+                        await cosmosClient.GetContainer(Constant.TEAMModelOS, "Normal").DeleteItemAsync<AreaSetting>(tempFile.id, new PartitionKey("AreaSetting"));
+                    }
+
+                    tempSetting = await cosmosClient.GetContainer(Constant.TEAMModelOS, "Normal").CreateItemAsync(saveSetting, new PartitionKey($"AreaSetting"));  //需要删除原来的区域设置数据在进行添加
                 }
 
                 //发送消息分区键
@@ -1009,6 +1007,294 @@ namespace TEAMModelBI.Controllers.BINormal
             else return Ok(new { state = 404,msg="依据Id未找到该区!" });
         }
 
+
+        /// <summary>
+        /// 同步两个区到新的区中
+        /// </summary>
+        /// <param name="jsonElement"></param>
+        /// <returns></returns>
+        [ProducesDefaultResponseType]
+        [AuthToken(Roles = "admin")]
+        [HttpPost("cut-full-statndard")]
+        public async Task<IActionResult> CutFullStandard(JsonElement jsonElement)
+        {
+            try
+            {
+                string _oldId = "bde5c011-2ae4-461a-b46c-5483ba72ae45";
+                string _oldStandard = "standard27";
+                string standardFileId = "02944f32-f534-3397-ea56-e6f1fc6c3714";
+                string standard = "standard2";
+
+                List<CopyStandard> copyStand = new()
+                {
+                    new CopyStandard() { id = standardFileId, standard = standard },
+                    new CopyStandard() { id = "99a4a33b-e21b-44ac-80a1-b31dc40496e0", standard = "standard3" }
+                };
+
+                var (_tmdId, _tmdName, pic, did, dname, dpic) = HttpJwtAnalysis.JwtXAuthBI(HttpContext.GetXAuth("AuthToken"), _option);
+                var tableClient = _azureStorage.GetCloudTableClient();
+                var blobClient = _azureStorage.GetBlobContainerClient(containerName: "0-public");
+                var cosmosClient = _azureCosmos.GetCosmosClient();
+                var serBusClient = _serviceBus.GetServiceBusClient();
+                var activeTask = _configuration.GetValue<string>("Azure:ServiceBus:ActiveTask");//秘钥地址
+
+                var table = tableClient.GetTableReference("IESLogin");
+
+                var responseSet = await cosmosClient.GetContainer(Constant.TEAMModelOS, "Normal").ReadItemStreamAsync($"{_oldId}", new PartitionKey("AreaSetting"));
+                if (responseSet.Status == 200)
+                {
+                    using var fileJson = await JsonDocument.ParseAsync(responseSet.ContentStream);
+                    AreaSetting delSet = fileJson.ToObject<AreaSetting>();
+                    if (!string.IsNullOrEmpty(delSet.accessConfig))
+                        return Ok(new { state = 401, msg = "区域已经规定了,不能切换能能力" });
+                }
+
+                List<string> abilityIds = new List<string>();  //册别的ID集合
+
+                //查询册别信息
+                await foreach (var tempAbility in cosmosClient.GetContainer(Constant.TEAMModelOS, "Normal").GetItemQueryIterator<Ability>(queryText: $"select value(c) from c", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Ability-{_oldStandard}") }))
+                {
+                    abilityIds.Add(tempAbility.id);  //查询出来册别ID添加册别ID集合
+                }
+                //删除册别
+                if (abilityIds.IsNotEmpty())
+                {
+                    var sresponse = await cosmosClient.GetContainer(Constant.TEAMModelOS, "Normal").DeleteItemsStreamAsync(abilityIds, $"Ability-{_oldStandard}");
+                }
+
+                List<string> abilityTaskIds = new List<string>();  //章节ID集合
+                await foreach (var item in cosmosClient.GetContainer(Constant.TEAMModelOS, "Normal").GetItemQueryIterator<AbilityTask>(queryText: $"select value(c) from c", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"AbilityTask-{_oldStandard}") }))
+                {
+                    abilityTaskIds.Add(item.id);   //查询出来的章节信息ID添加到战绩集合
+                }
+                //删除章节
+                if (abilityTaskIds.IsNotEmpty())
+                {
+                    var sresponse = await cosmosClient.GetContainer(Constant.TEAMModelOS, "Normal").DeleteItemsStreamAsync(abilityTaskIds, $"AbilityTask-{_oldStandard}");
+                }
+                List<Task<ItemResponse<Ability>>> abilities = new();      //存储册别数据
+                List<Task<ItemResponse<AbilityTask>>> abilityTasks = new();  //存储章节
+
+                List<string> repeatAbilityId = new();
+                List<string> repeatAbilityTaskId = new();
+
+                foreach (var newstand in copyStand)
+                {
+                    try
+                    {
+                        //查询要复制区域的能力标准点 
+                        await foreach (var item in cosmosClient.GetContainer(Constant.TEAMModelOS, "Normal").GetItemQueryIterator<Ability>(queryText: $"select value(c) from c", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Ability-{newstand.standard}") }))
+                        {
+                            if (!string.IsNullOrEmpty(item.blob))
+                                item.blob = item.blob.Replace($"/{newstand.standard}/", $"/{_oldStandard}/");
+
+                            item.standard = $"{_oldStandard}";
+                            item.code = $"Ability-{_oldStandard}";
+                            item.school = $"{_oldStandard}";
+
+                            //添加区能力标准点
+                            //abilities.Add(cosmosClient.GetContainer(Constant.TEAMModelOS, "Normal").CreateItemAsync(item, new PartitionKey($"Ability-{_oldStandard}")));
+                            //await cosmosClient.GetContainer(Constant.TEAMModelOS, "Normal").CreateItemAsync(item, new PartitionKey($"Ability-{_oldStandard}"));
+
+                            var respond = await cosmosClient.GetContainer(Constant.TEAMModelOS, "Normal").ReadItemStreamAsync(item.id, new PartitionKey(item.code));
+                            if (respond.Status != 200)
+                                abilities.Add(cosmosClient.GetContainer(Constant.TEAMModelOS, "Normal").CreateItemAsync(item, new PartitionKey($"Ability-{_oldStandard}")));
+                            else
+                                repeatAbilityId.Add(item.id);
+                        }
+                    }
+                    catch
+                    {
+                        return Ok(new { state = 200, msg = "创区成功,能力标准点复制失败,遗留数据影响!" });
+                    }
+
+                    try
+                    {
+                        if (abilities.Count < 256)
+                        {
+                            await Task.WhenAll(abilities);
+                        }
+                        else
+                        {
+                            int pages = (abilities.Count + 255) / 256;
+                            for (int i = 0; i < pages; i++)
+                            {
+                                List<Task<ItemResponse<Ability>>> tempAbility = abilities.Skip((i) * 256).Take(256).ToList();
+                                await Task.WhenAll(tempAbility);
+                            }
+                        }
+                    }
+                    catch
+                    {
+                        return Ok(new { state = 200, msg = "创区成功,能力标准点复制失败,遗留数据影响!" });
+                    }
+
+                    try
+                    {
+                        //微能力点
+                        await foreach (var atask in _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "Normal").GetItemQueryIterator<AbilityTask>(queryText: $"select value(c) from c ", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"AbilityTask-{newstand.standard}") }))
+                        {
+                            List<Tnode> tnodes = new();
+                            foreach (Tnode tnode in atask.children)
+                            {
+                                if (tnode.rnodes != null)
+                                {
+                                    List<Rnode> rnodes = new();
+                                    foreach (Rnode rnode in tnode.rnodes)
+                                    {
+                                        if (!string.IsNullOrEmpty($"{rnode.link}"))
+                                        {
+                                            rnode.link = rnode.link.Replace($"/{newstand.standard}/", $"/{_oldStandard}/");
+                                        }
+                                        rnodes.Add(rnode);
+                                    }
+                                    tnode.rnodes = rnodes;
+                                }
+                                tnodes.Add(tnode);
+                            }
+
+                            atask.children = tnodes;
+                            atask.code = $"AbilityTask-{_oldStandard}";
+                            atask.standard = $"{_oldStandard}";
+                            atask.codeval = $"{_oldStandard}";
+
+                            var respond = await cosmosClient.GetContainer(Constant.TEAMModelOS, "Normal").ReadItemStreamAsync(atask.id, new PartitionKey(atask.code));
+                            if (respond.Status != 200)
+                                ////添加区能力标准点中的节点 
+                                //abilityTasks.Add(cosmosClient.GetContainer(Constant.TEAMModelOS, "Normal").CreateItemAsync(atask, new PartitionKey($"AbilityTask-{_oldStandard}")));
+                                await cosmosClient.GetContainer(Constant.TEAMModelOS, "Normal").CreateItemAsync(atask, new PartitionKey($"AbilityTask-{_oldStandard}"));
+                            else
+                                repeatAbilityTaskId.Add(atask.id);
+                        }
+                    }
+                    catch
+                    {
+                        return Ok(new { state = 200, msg = "创区成功,能力标准创建成功,微能力点复制失败,遗留数据影响!" });
+                    }
+
+                    //发送消息分区键
+                    string partitionCode = "DelBeforeCopyAbility-mark";
+
+                    //执行复制操作
+                    BatchCopyFile batchCopyFile = new();
+                    batchCopyFile.blobCntr = "teammodelos";
+                    batchCopyFile.oldFileName = $"{newstand.standard}";
+                    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 = "能力点复制成功,复制能力点的文件失败," });
+                    }
+
+                    //发送消息实体
+                    Notification notification = new()
+                    {
+                        hubName = "hita",
+                        type = "msg",
+                        from = $"BI:{_option.Location}:private",
+                        to = new List<string> { $"{_tmdId}" },
+                        label = $"{partitionCode}_start",
+                        body = new { location = _option.Location, biz = partitionCode, tmdid = $"{_tmdId}", tmdname = $"{_tmdName}", status = 1, time = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() }.ToJsonString(),
+                        expires = DateTimeOffset.UtcNow.AddDays(7).ToUnixTimeSeconds()
+                    };
+
+                    var url = _configuration.GetValue<string>("HaBookAuth:CoreService:sendnotification");
+                    var clientID = _configuration.GetValue<string>("HaBookAuth:CoreService:clientID");
+                    var clientSecret = _configuration.GetValue<string>("HaBookAuth:CoreService:clientSecret");
+                    var location = _option.Location;
+                    await _notificationService.SendNotification(clientID, clientSecret, location, url, notification); //发送站内发送消息
+                }
+
+                StandardFile saveFile = new();
+                //新政策文件
+                await foreach (StandardFile standardFile in cosmosClient.GetContainer(Constant.TEAMModelOS, "Normal").GetItemQueryIterator<StandardFile>(queryText: $"select value(c) from c where  c.id='{standardFileId}'", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"StandardFile") }))
+                {
+                    if (standardFile != null)
+                    {
+                        standardFile.standard = $"{_oldStandard}";
+                        standardFile.id = $"{_oldId}";
+
+                        saveFile = standardFile;
+                    }
+                }
+
+                StandardFile tempFile = new();
+                if (saveFile.id != null)
+                {
+                    var respFile = await cosmosClient.GetContainer(Constant.TEAMModelOS, "Normal").ReadItemStreamAsync(saveFile.id, new PartitionKey("StandardFile"));
+                    if (respFile.Status == 200)
+                    {
+                        using var json = await JsonDocument.ParseAsync(respFile.ContentStream);
+                        tempFile = json.ToObject<StandardFile>();
+                        await cosmosClient.GetContainer(Constant.TEAMModelOS, "Normal").DeleteItemAsync<StandardFile>(tempFile.id, new PartitionKey("StandardFile"));
+                    }
+
+                    tempFile = await cosmosClient.GetContainer(Constant.TEAMModelOS, "Normal").CreateItemAsync(saveFile, new PartitionKey($"StandardFile")); // 需要删除原来的政策文件数据在进行添加
+                }
+
+                //if (tempFile.id != null)
+                //{
+                //    if (tempFile.id.Equals(saveFile.id))
+                //        await cosmosClient.GetContainer(Constant.TEAMModelOS, "Normal").ReplaceItemAsync<StandardFile>(saveFile, saveFile.id, new PartitionKey("StandardFile")); //直接替换以前的数据
+                //    else
+                //        await cosmosClient.GetContainer(Constant.TEAMModelOS, "Normal").CreateItemAsync(saveFile, new PartitionKey($"StandardFile")); // 需要删除原来的政策文件数据在进行添加
+                //}
+
+                //新的区域设置
+                AreaSetting saveSetting = new();
+                await foreach (AreaSetting areaSetting in cosmosClient.GetContainer(Constant.TEAMModelOS, "Normal").GetItemQueryIterator<AreaSetting>(queryText: $"select value(c) from c where c.id='{standardFileId}'", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("AreaSetting") }))
+                {
+                    if (areaSetting != null)
+                    {
+                        areaSetting.accessConfig = null;
+                        areaSetting.id = $"{_oldId}";
+
+                        saveSetting = areaSetting;
+                    }
+                }
+
+                AreaSetting tempSetting = new();
+                if (saveSetting.id != null)
+                {
+                    var respSetting = await cosmosClient.GetContainer(Constant.TEAMModelOS, "Normal").ReadItemStreamAsync(saveSetting.id, new PartitionKey("AreaSetting"));
+                    if (respSetting.Status == 200)
+                    {
+                        using var json = await JsonDocument.ParseAsync(respSetting.ContentStream);
+                        tempSetting = json.ToObject<AreaSetting>();
+                        await cosmosClient.GetContainer(Constant.TEAMModelOS, "Normal").DeleteItemAsync<AreaSetting>(saveSetting.id, new PartitionKey("AreaSetting"));
+                    }
+
+                    tempSetting = await cosmosClient.GetContainer(Constant.TEAMModelOS, "Normal").CreateItemAsync(saveSetting, new PartitionKey($"AreaSetting"));  //需要删除原来的区域设置数据在进行添加
+                }
+
+                //if (tempSetting.id != null)
+                //{
+                //    if (tempSetting.id.Equals(saveSetting.id))
+                //        await cosmosClient.GetContainer(Constant.TEAMModelOS, "Normal").ReplaceItemAsync<AreaSetting>(saveSetting, saveSetting.id, new PartitionKey($"AreaSetting"));   //直接替换以前的数据
+                //    else
+                //        await cosmosClient.GetContainer(Constant.TEAMModelOS, "Normal").CreateItemAsync(saveSetting, new PartitionKey($"AreaSetting"));  //需要删除原来的区域设置数据在进行添加
+                //}
+
+                return Ok(new { state = RespondCode.Ok, repeatAbilityId, repeatAbilityTaskId });
+
+            }
+            catch (Exception ex)
+            {
+                await _dingDing.SendBotMsg($"BI,{_option.Location} batcharea/cut-full-statndard \n {ex.Message}\n{ex.StackTrace}", GroupNames.成都开发測試群組);
+                return BadRequest();
+            }
+        }
+
         /// <summary>
         /// 区域列表
         /// </summary>

+ 21 - 19
TEAMModelBI/Controllers/BISchool/BatchSchoolController.cs

@@ -37,7 +37,6 @@ using DocumentFormat.OpenXml.Wordprocessing;
 using DocumentFormat.OpenXml.Bibliography;
 using HTEXLib;
 using TEAMModelOS.SDK.Models.Service.BI;
-using static TEAMModelBI.Controllers.BISchool.BatchSchoolController;
 
 namespace TEAMModelBI.Controllers.BISchool
 {
@@ -473,9 +472,9 @@ namespace TEAMModelBI.Controllers.BISchool
                 else if(upSc.Count == 1)
                     await _dingDing.SendBotMsg($"BI,{_option.Location} \n 单个建校信息:{noticeDD}", GroupNames.成都开发測試群組);
 
-                //v2通知
-                Teacher targetTeacher = await cosmosClient.GetContainer(Constant.TEAMModelOS, Constant.Teacher).ReadItemAsync<Teacher>($"{_tmdId}", new PartitionKey($"Base"));
-                _coreAPIHttpService.PushNotify(new List<IdNameCode> { new IdNameCode { id = targetTeacher.id, name = targetTeacher.name, code = targetTeacher.lang } }, "create-school", Constant.NotifyType_IES5_Management, new Dictionary<string, object> { { "tmdname", $"{_tmdName}" }, { "schooName", $"{vsSql}" } }, _option.Location, _configuration, _dingDing, _environment.ContentRootPath);
+                ////v2通知
+                //Teacher targetTeacher = await cosmosClient.GetContainer(Constant.TEAMModelOS, Constant.Teacher).ReadItemAsync<Teacher>($"{_tmdId}", new PartitionKey($"Base"));
+                //_coreAPIHttpService.PushNotify(new List<IdNameCode> { new IdNameCode { id = targetTeacher.id, name = targetTeacher.name, code = targetTeacher.lang } }, "create-school", Constant.NotifyType_IES5_Management, new Dictionary<string, object> { { "tmdname", $"{_tmdName}" }, { "schooName", $"{vsSql}" } }, _option.Location, _configuration, _dingDing, _environment.ContentRootPath);
 
                 //保存操作记录
                 await AzureStorageBlobExtensions.SaveBILog(blobClient, tableClient, "school-batchAdd", stringBuilder?.ToString(), _dingDing, httpContext: HttpContext);
@@ -550,8 +549,8 @@ namespace TEAMModelBI.Controllers.BISchool
                 if (!string.IsNullOrEmpty($"{tmdId}"))
                 {
                     schoolIds = await CommonFind.FindSchoolIds(cosmosClient, $"{tmdId}");
-                    string scsSql = BICommonWay.ManyScSql("c.school", schoolIds);
-                    scCntSql.Append(scsSql);
+                    string scsSql = BICommonWay.ManyScSql("c.id", schoolIds);
+                    scCntSql.Append($" where {scsSql}");
                 }
 
                 if (schoolIds.Count > 0)
@@ -1275,21 +1274,24 @@ namespace TEAMModelBI.Controllers.BISchool
                         noAccounts.Add(bISchool);
                 });
 
-                //查询区是否存在
-                var respAreaId = await cosmosClient.GetContainer(Constant.TEAMModelOS, "Normal").ReadItemStreamAsync($"{bISchool.areaId}", new PartitionKey("Base-Area"));
-                if (respAreaId.Status != 200)
+                if (!string.IsNullOrEmpty(bISchool.areaId))
                 {
-                    noAreaIds.Add(bISchool);
-                }
+                    //查询区是否存在
+                    var respAreaId = await cosmosClient.GetContainer(Constant.TEAMModelOS, "Normal").ReadItemStreamAsync($"{bISchool.areaId}", new PartitionKey("Base-Area"));
+                    if (respAreaId.Status != 200)
+                    {
+                        noAreaIds.Add(bISchool);
+                    }
 
-                //查询去是否同步省平台
-                var responseSet = await cosmosClient.GetContainer(Constant.TEAMModelOS, "Normal").ReadItemStreamAsync($"{bISchool.areaId}", new PartitionKey("AreaSetting"));
-                if (responseSet.Status == 200)
-                {
-                    using var fileJson = await JsonDocument.ParseAsync(responseSet.ContentStream);
-                    AreaSetting delSet = fileJson.ToObject<AreaSetting>();
-                    if (!string.IsNullOrEmpty(delSet.accessConfig))
-                        synPro.Add(bISchool);
+                    //查询去是否同步省平台
+                    var responseSet = await cosmosClient.GetContainer(Constant.TEAMModelOS, "Normal").ReadItemStreamAsync($"{bISchool.areaId}", new PartitionKey("AreaSetting"));
+                    if (responseSet.Status == 200)
+                    {
+                        using var fileJson = await JsonDocument.ParseAsync(responseSet.ContentStream);
+                        AreaSetting delSet = fileJson.ToObject<AreaSetting>();
+                        if (!string.IsNullOrEmpty(delSet.accessConfig))
+                            synPro.Add(bISchool);
+                    }
                 }
 
                 CreateSchoolInfo createSchoolInfo = new CreateSchoolInfo()

+ 15 - 13
TEAMModelBI/Controllers/BISchool/SchoolController.cs

@@ -1485,11 +1485,11 @@ namespace TEAMModelBI.Controllers.BISchool
                     string tchAllSql = $"select value(count(c.id)) from c where ARRAY_CONTAINS(c.roles,'teacher',true) and c.status = 'join'";
                     tchAllCnt += await CommonFind.GetSqlValueCount(cosmosClient, "School", tchAllSql, $"Teacher-{item.id}");
 
-                    string lessALLSql = "select value(count(c.id)) from c where c.pk='LessonRecord'";
+                    string lessALLSql = $"select value(count(c.id)) from c where c.pk='LessonRecord'";
                     lessAllCnt += await CommonFind.GetSqlValueCount(cosmosClient, "School", lessALLSql, $"LessonRecord-{item.id}");
 
                     string lessUpSql = $"select value(count(c.id)) from c where c.pk='LessonRecord' and c.upload = 0";
-                    lessRankCnt.openCnt = await CommonFind.GetSqlValueCount(cosmosClient, "School", lessMoSql, $"LessonRecord-{item.id}");
+                    lessRankCnt.openCnt = await CommonFind.GetSqlValueCount(cosmosClient, "School", lessUpSql, $"LessonRecord-{item.id}");
 
                     string lessSql = $"select value(count(c.id)) from c where c.pk='LessonRecord' and c.upload = 1";
                     lessRankCnt.lessCnt = await CommonFind.GetSqlValueCount(cosmosClient, "School", lessSql, $"LessonRecord-{item.id}");
@@ -1529,13 +1529,13 @@ namespace TEAMModelBI.Controllers.BISchool
             //    cosmosClient = _azureCosmos.GetCosmosClient(name: BIConst.Global);
 
             List<YearCnt> yearCnts = new();  //当前的课例,活动,互动统计
-
+            List<string> schoolIds = new();
             string strSql = "SELECT value(count(c.id)) FROM c where c.pk='LessonRecord'";
             string scSql = null;
             if (!string.IsNullOrEmpty($"{tmdId}") && string.IsNullOrEmpty($"{schooId}"))
             {
                 //List<string> schoolIds = await CommonFind.FindSchoolIds(cosmosClient, $"{tmdId}");
-                List<string> schoolIds = new();
+
                 if (!string.IsNullOrEmpty($"{tmdId}"))
                 {
                     switch ($"{role}")
@@ -1548,19 +1548,21 @@ namespace TEAMModelBI.Controllers.BISchool
                             break;
                     }
                 }
-                else
-                    schoolIds = await CommonFind.GetValueSingle(cosmosClient, "School", "select value(c.id) from c", "Base");
-
-                if (schoolIds.Count == 0) return Ok(new { state = RespondCode.Ok, yearCnts, });
-                scSql = BICommonWay.ManyScSql("c.school", schoolIds);
             }
-
-            if (!string.IsNullOrEmpty($"{schooId}") && string.IsNullOrEmpty($"{tmdId}"))
+            else if (!string.IsNullOrEmpty($"{schooId}") && string.IsNullOrEmpty($"{tmdId}"))
                 scSql = $"c.school = '{schooId}'";
+            else 
+            {
+                schoolIds = await CommonFind.GetValueSingle(cosmosClient, "School", "select value(c.id) from c", "Base");
 
-            if ((string.IsNullOrEmpty($"{tmdId}") && string.IsNullOrEmpty($"{schooId}")) || (!string.IsNullOrEmpty($"{tmdId}") && !string.IsNullOrEmpty($"{schooId}")))
-                return Ok(new { state = RespondCode.ParamsError, msg = "参数错误" });
+                if (schoolIds.Count == 0)
+                    return Ok(new { state = RespondCode.Ok, yearCnts, });
+                else
+                    scSql = BICommonWay.ManyScSql("c.school", schoolIds);
+            }
 
+            //if ((string.IsNullOrEmpty($"{tmdId}") && string.IsNullOrEmpty($"{schooId}")) || (!string.IsNullOrEmpty($"{tmdId}") && !string.IsNullOrEmpty($"{schooId}")))
+                
             List<MonthStartEnd> mthStartEnds = await TimeHelper.GetYearSataMthCtMth(DateTimeOffset.UtcNow);
             if (mthStartEnds.Count > 0)
             {

+ 6 - 8
TEAMModelBI/Controllers/Core/BlobController.cs

@@ -14,7 +14,7 @@ using TEAMModelOS.SDK.Context.BI;
 using TEAMModelOS.SDK.DI;
 using TEAMModelOS.SDK.Extension;
 
-namespace TEAMModelBI.Controllers.Core
+namespace TEAMModelBI.Controllers.BISystem
 {
     [Route("blob")]
     [ApiController]
@@ -52,19 +52,20 @@ namespace TEAMModelBI.Controllers.Core
         [HttpPost("upload-public")]
         [AuthToken(Roles = "admin,rdc,assist,sales")]
         [RequestSizeLimit(102_400_000_00)]//最大10000m左右
-        public async Task<IActionResult> UploadPublic([FromForm] IFormFile file,[FromHeader]string site)
+        public async Task<IActionResult> UploadPublic([FromForm] IFormFile file, [FromHeader] string upType)
         {
             var (id, _, _, school) = HttpContext.GetAuthTokenInfo();
             string fileExt = FileType.GetExtention(file.FileName).ToLower();
             var blobClient = _azureStorage.GetBlobContainerClient(containerName: "0-public");
-            if ($"{site}".Equals(BIConst.Global))
-                blobClient = _azureStorage.GetBlobContainerClient(containerName: "0-public", BIConst.Global);
+            string typeName = "school";
+            if ($"{upType}".Equals("biz"))
+                typeName = "business";
 
             if (ContentTypeDict.dict.ContainsKey($".{fileExt}"))
             {
                 //var url = await _azureStorage.GetBlobContainerClient(containerName: "0-public", BIConst.Global).UploadFileByContainer("0-public", file.OpenReadStream(), "school", $"{Guid.NewGuid()}.{fileExt}", false);
                 //var url = await BIAzureStorageBlobExtensions.UploadFileByContainer(blobClient, file.OpenReadStream(), "school", $"{Guid.NewGuid()}.{fileExt}");
-                var url = await AzureStorageBlobExtensions.UploadFileByContainer(blobClient, file.OpenReadStream(), "school", $"{Guid.NewGuid()}.{fileExt}");
+                var url = await blobClient.UploadFileByContainer(file.OpenReadStream(), typeName, $"{Guid.NewGuid()}.{fileExt}");
                 return Ok(new { url });
             }
             else
@@ -73,8 +74,5 @@ namespace TEAMModelBI.Controllers.Core
             }
         }
 
-
-
-
     }
 }

+ 36 - 0
TEAMModelBI/Controllers/BITest/TestController.cs

@@ -52,6 +52,7 @@ using Azure.Storage.Blobs.Models;
 using Azure.Storage.Blobs;
 using Azure.Storage.Blobs.Specialized;
 using System.Web;
+using Azure.Storage.Sas;
 
 namespace TEAMModelBI.Controllers.BITest
 {
@@ -1472,6 +1473,41 @@ namespace TEAMModelBI.Controllers.BITest
             return Ok(new { state = RespondCode.Ok });
         }
 
+        [HttpPost("set-copyfile")]
+        public async Task<IActionResult> SetCopyFile() 
+        {
+
+            List<Task<CopyFromUriOperation>> filelist = new List<Task<CopyFromUriOperation>>();
+            var azureClient = _azureStorage.GetBlobContainerClient($"0-public");//获取容器连接地址
+
+            //查询目录下所有容器路径
+            await foreach (BlobItem blobItem in azureClient.GetBlobsAsync(BlobTraits.None, BlobStates.None, $"TestMP4Max/"))
+            {
+                string newurl = $"{blobItem.Name}".Replace($"/SourceFile/", $"/SourceFiles/");//替换成新的容器路径
+                var urlSas = _azureStorage.GetBlobSAS($"0-public", blobItem.Name, BlobSasPermissions.Read | BlobSasPermissions.List);   //获取容器sas和有效期
+                //await azureClient.GetBlobClient(newurl).StartCopyFromUriAsync(new Uri(urlSas));
+                filelist.Add(azureClient.GetBlobClient(newurl).StartCopyFromUriAsync(new Uri(urlSas)));
+                //await azureClient.GetBlobClient(newurl).SyncCopyFromUriAsync(new Uri(urlSas));  //添加复制文件到集合执行复制操作
+
+            }
+            if (filelist.Count <= 256)
+            {
+                await Task.WhenAll(filelist);
+            }
+            else
+            {
+                int pages = (filelist.Count + 255) / 256;
+                for (int i = 0; i < pages; i++)
+                {
+                    List<Task<CopyFromUriOperation>> rspBlobCopyInfos = filelist.Skip((i) * 256).Take(256).ToList();
+                    await Task.WhenAll(rspBlobCopyInfos);
+                }
+            }
+
+
+            return Ok(new { state = 200 , filelist });
+        }
+
         public class linqTest
         {
             public string id { get; set; }

+ 349 - 0
TEAMModelBI/Controllers/RepairApi/InitialAreaController.cs

@@ -0,0 +1,349 @@
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.Options;
+using System.Net.Http;
+using TEAMModelOS.SDK.DI;
+using TEAMModelOS.SDK;
+using TEAMModelOS.Models;
+using System.Text.Json;
+using System.Threading.Tasks;
+using TEAMModelOS.SDK.Context.Constant;
+using Azure.Messaging.ServiceBus;
+using TEAMModelBI.Tool.Extension;
+using TEAMModelOS.SDK.Extension;
+using Microsoft.AspNetCore.Hosting;
+using TEAMModelOS.SDK.DI.CoreAPI;
+using TEAMModelOS.SDK.Models;
+using Azure.Cosmos;
+using System;
+using System.Collections.Generic;
+using TEAMModelOS.SDK.Models.Cosmos.Common;
+using System.Linq;
+using Pipelines.Sockets.Unofficial.Arenas;
+using TEAMModelOS.SDK.Models.Cosmos.BI;
+using TEAMModelBI.Filter;
+using HTEXLib.COMM.Helpers;
+
+namespace TEAMModelBI.Controllers.RepairApi
+{
+    [Route("initialarea")]
+    [ApiController]
+    public class InitialAreaController : ControllerBase
+    {
+        private readonly AzureCosmosFactory _azureCosmos;
+        private readonly DingDing _dingDing;
+        private readonly Option _option;
+        private readonly AzureStorageFactory _azureStorage;
+        private readonly IConfiguration _configuration;
+        private readonly NotificationService _notificationService;
+        private readonly AzureServiceBusFactory _serviceBus;
+        private readonly IHttpClientFactory _http;
+        private readonly CoreAPIHttpService _coreAPIHttpService;
+        private readonly IWebHostEnvironment _environment; //读取文件
+
+        public InitialAreaController(AzureCosmosFactory azureCosmos, DingDing dingDing, AzureStorageFactory azureStorage, IOptionsSnapshot<Option> option, IConfiguration configuration, NotificationService notificationService, AzureServiceBusFactory serviceBus, IHttpClientFactory http, CoreAPIHttpService coreAPIHttpService, IWebHostEnvironment hostingEnvironment)
+        {
+            _azureCosmos = azureCosmos;
+            _dingDing = dingDing;
+            _azureStorage = azureStorage;
+            _option = option?.Value;
+            _configuration = configuration;
+            _notificationService = notificationService;
+            _serviceBus = serviceBus;
+            _http = http;
+            _coreAPIHttpService = coreAPIHttpService;
+            _environment = hostingEnvironment;
+        }
+
+        /// <summary>
+        /// 创建完整版的学区初始化
+        /// </summary>
+        /// <param name="jsonElement"></param>
+        /// <returns></returns>
+        [ProducesDefaultResponseType]
+        [HttpPost("create-full-version")]
+        public async Task<IActionResult> CreateFullVersion(JsonElement jsonElement)
+        {
+            if (!jsonElement.TryGetProperty("name", out JsonElement name)) return BadRequest();
+            jsonElement.TryGetProperty("provCode", out JsonElement provCode);
+            jsonElement.TryGetProperty("provName", out JsonElement provName);
+            jsonElement.TryGetProperty("cityCode", out JsonElement cityCode);
+            jsonElement.TryGetProperty("cityName", out JsonElement cityName);
+            jsonElement.TryGetProperty("areaAdmin", out JsonElement areadAdmin);
+            if (!jsonElement.TryGetProperty("standard", out JsonElement standard)) return BadRequest();
+            if (!jsonElement.TryGetProperty("standardName", out JsonElement standardName)) return BadRequest();
+            jsonElement.TryGetProperty("institution", out JsonElement institution);
+            jsonElement.TryGetProperty("oldStandard", out JsonElement oldStandard);
+
+            var (_tmdId, _tmdName, pic, did, dname, dpic) = HttpJwtAnalysis.JwtXAuthBI(HttpContext.GetXAuth("AuthToken"), _option);
+            List<CopyStandard> copyStand = new();
+            if (!string.IsNullOrEmpty($"{oldStandard}"))
+                copyStand = oldStandard.ToObject<List<CopyStandard>>();
+
+            var cosmosClient = _azureCosmos.GetCosmosClient();  //数据库连接
+            var tableClient = _azureStorage.GetCloudTableClient();
+            var blobClient = _azureStorage.GetBlobContainerClient(containerName: "0-public");
+
+            ServiceBusClient serBusClient;
+            try
+            {
+                serBusClient = _serviceBus.GetServiceBusClient();
+            }
+            catch
+            {
+                return Ok(new { state = 403, msg = "Functionn未启动,请联系管理员" });
+            }
+
+            var activeTask = _configuration.GetValue<string>("Azure:ServiceBus:ActiveTask");
+            var table = tableClient.GetTableReference("IESLogin");
+
+            //查询新的是否存在
+            await foreach (var item in cosmosClient.GetContainer(Constant.TEAMModelOS, "Normal").GetItemQueryIterator<Area>(queryText: $"select value(c) from c where c.standard='{standard}'", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Base-Area") }))
+            {
+                if (item.standard.Equals($"{standard}"))
+                    return Ok(new { state = 1, message = "新创区的standard已存在请检查" });
+            }
+
+            //区级的ID
+            string areaId = Guid.NewGuid().ToString();
+            Area addArea = new()
+            {
+                id = areaId,
+                code = $"Base-Area",
+                name = $"{name}",
+                provCode = $"{provCode}",
+                provName = $"{provName}",
+                cityCode = $"{cityCode}",
+                cityName = $"{cityName}",
+                standard = $"{standard}",
+                standardName = $"{standardName}",
+                institution = $"{institution}"
+            };
+
+            #region  区级管理员
+
+            var coreUser = await _coreAPIHttpService.GetUserInfo(new Dictionary<string, string> { { "key", $"{areadAdmin}" } }, _option.Location, _configuration);
+            if (coreUser == null || coreUser.id == null)
+                return Ok(new { state = 404, msg = "未找到改账户的管理员" });
+
+            //string tmdId = !string.IsNullOrEmpty(tempTmdId) ? tempTmdId : $"{areadAdmin}";
+            Teacher teacher = null;
+            try
+            {
+                //查询该教师是否存在
+                teacher = await cosmosClient.GetContainer(Constant.TEAMModelOS, "Teacher").ReadItemAsync<Teacher>($"{coreUser.id}", new PartitionKey("Base"));
+            }
+            catch
+            {
+            }
+
+            if (teacher != null)
+            {
+                //教师存在,在该教师信息中添加要管理的学校信息
+                teacher.areas.Add(new Teacher.TeacherArea { areaId = addArea.id, status = "join", name = addArea.name });
+                await cosmosClient.GetContainer(Constant.TEAMModelOS, "Teacher").ReplaceItemAsync<Teacher>(teacher, teacher.id, new PartitionKey("Base"));
+            }
+            else
+            {
+                teacher.id = coreUser.id;
+                teacher.name = coreUser.name;
+                teacher.picture = coreUser.picture;
+                teacher.pk = "Base";
+                teacher.code = "Base";
+                teacher.size = 1;
+                teacher.createTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
+                //教师存在,在该教师信息中添加要管理的学校信息
+                teacher.areas.Add(new Teacher.TeacherArea { areaId = addArea.id, status = "join", name = addArea.name });
+                await cosmosClient.GetContainer(Constant.TEAMModelOS, "Teacher").CreateItemAsync<Teacher>(teacher, new PartitionKey("Base"));
+            }
+
+            #endregion
+
+            //创建区域
+            await cosmosClient.GetContainer(Constant.TEAMModelOS, "Normal").CreateItemAsync<Area>(addArea, new PartitionKey("Base-Area"));
+
+            //消息分区键
+            string partitionCode = "copyAbility-mark";
+
+            #region 复制微能力点到新区
+
+            List<Task<ItemResponse<Ability>>> abilities = new();      //存储区域数据
+            List<Task<ItemResponse<AbilityTask>>> abilityTasks = new();  //存储章节
+
+            Area AreaS2 = null;
+            List<Area> areas = new();
+
+            await foreach (var itemArea in cosmosClient.GetContainer(Constant.TEAMModelOS, "Normal").GetItemQueryIterator<Area>(queryText: $"select value(c) from c where c.standard='standard2'", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Base-Area") }))
+            {
+                areas.Add(itemArea);
+                AreaS2 = itemArea;
+            }
+
+            await foreach (var itemArea in cosmosClient.GetContainer(Constant.TEAMModelOS, "Normal").GetItemQueryIterator<Area>(queryText: $"select value(c) from c where c.standard='standard3'", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Base-Area") }))
+            {
+                areas.Add(itemArea);
+            }
+
+            foreach (var area in areas)
+            {
+                //查询要复制区域的能力标准点 
+                await foreach (var item in cosmosClient.GetContainer(Constant.TEAMModelOS, "Normal").GetItemQueryIterator<Ability>(queryText: $"select value(c) from c", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Ability-{area.standard}") }))
+                {
+                    if (!string.IsNullOrEmpty(item.blob))
+                    {
+                        item.blob = item.blob.Replace($"/{area.standard}/", $"/{standard}/");
+                    };
+
+                    item.standard = $"{standard}";
+                    item.code = $"Ability-{standard}";
+                    item.school = $"{standard}";
+
+                    //添加区能力标准点
+                    abilities.Add(cosmosClient.GetContainer(Constant.TEAMModelOS, "Normal").CreateItemAsync(item, new PartitionKey($"Ability-{standard}")));
+                }
+
+                try
+                {
+                    if (abilities.Count < 256)
+                    {
+                        await Task.WhenAll(abilities);
+                    }
+                    else
+                    {
+                        int pages = (abilities.Count + 255) / 256;
+                        for (int i = 0; i < pages; i++)
+                        {
+                            List<Task<ItemResponse<Ability>>> tempAbility = abilities.Skip((i) * 256).Take(256).ToList();
+                            await Task.WhenAll(tempAbility);
+                        }
+                    }
+                }
+                catch
+                {
+                    return Ok(new { state = 200, msg = "创区成功,能力标准点复制失败,遗留数据影响!" });
+                }
+
+                try
+                {
+                    //微能力点
+                    await foreach (var atask in _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "Normal").GetItemQueryIterator<AbilityTask>(queryText: $"select value(c) from c ", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"AbilityTask-{area.standard}") }))
+                    {
+                        List<Tnode> tnodes = new();
+                        foreach (Tnode tnode in atask.children)
+                        {
+                            if (tnode.rnodes != null)
+                            {
+                                List<Rnode> rnodes = new List<Rnode>();
+                                foreach (Rnode rnode in tnode.rnodes)
+                                {
+                                    if (!string.IsNullOrEmpty($"{rnode.link}"))
+                                    {
+                                        rnode.link = rnode.link.Replace($"/{area.standard}/", $"/{standard}/");
+                                    }
+                                    rnodes.Add(rnode);
+                                }
+                                tnode.rnodes = rnodes;
+                            }
+                            tnodes.Add(tnode);
+                        }
+
+                        atask.children = tnodes;
+                        atask.code = $"AbilityTask-{standard}";
+                        atask.standard = $"{standard}";
+                        atask.codeval = $"{standard}";
+
+                        //添加区能力标准点中的节点 
+                        //abilityTasks.Add(cosmosClient.GetContainer(Constant.TEAMModelOS, "Normal").CreateItemAsync(atask, new PartitionKey($"AbilityTask-{standard}")));
+                        await cosmosClient.GetContainer(Constant.TEAMModelOS, "Normal").CreateItemAsync(atask, new PartitionKey($"AbilityTask-{standard}"));
+                    }
+                }
+                catch
+                {
+                    return Ok(new { state = 200, 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");
+                //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 = "能力点复制成功,复制能力点的文件失败," });
+                }
+
+                //发送消息实体
+                Notification notification = new()
+                {
+                    hubName = "hita",
+                    type = "msg",
+                    from = $"BI:{_option.Location}:private",
+                    to = new List<string> { $"{_tmdId}" },
+                    label = $"{partitionCode}_start",
+                    body = new { location = _option.Location, biz = partitionCode, tmdid = $"{_tmdId}", tmdname = $"{_tmdName}", status = 1, time = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() }.ToJsonString(),
+                    expires = DateTimeOffset.UtcNow.AddDays(7).ToUnixTimeSeconds()
+                };
+
+                var notiUrl = _configuration.GetValue<string>("HaBookAuth:CoreService:sendnotification");
+                var clientID = _configuration.GetValue<string>("HaBookAuth:CoreService:clientID");
+                var clientSecret = _configuration.GetValue<string>("HaBookAuth:CoreService:clientSecret");
+                var location = _option.Location;
+                await _notificationService.SendNotification(clientID, clientSecret, location, notiUrl, notification); //站内发送消息
+            }
+
+            if (AreaS2 != null)
+            {
+                //新政策文件
+                await foreach (StandardFile standardFile in cosmosClient.GetContainer(Constant.TEAMModelOS, "Normal").GetItemQueryIterator<StandardFile>(queryText: $"select value(c) from c where  c.id='{AreaS2.id}'", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"StandardFile") }))
+                {
+                    if (standardFile != null)
+                    {
+                        standardFile.standard = $"{standard}";
+                        standardFile.id = areaId;
+                        await cosmosClient.GetContainer(Constant.TEAMModelOS, "Normal").CreateItemAsync(standardFile, new PartitionKey($"StandardFile"));
+                    }
+                }
+
+                //新的区域设置
+                await foreach (AreaSetting areaSetting in cosmosClient.GetContainer(Constant.TEAMModelOS, "Normal").GetItemQueryIterator<AreaSetting>(queryText: $"select value(c) from c where c.id='{AreaS2.id}'", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("AreaSetting") }))
+                {
+                    if (areaSetting != null)
+                    {
+                        areaSetting.accessConfig = null;
+                        areaSetting.id = areaId;
+                        await cosmosClient.GetContainer(Constant.TEAMModelOS, "Normal").CreateItemAsync(areaSetting, new PartitionKey($"AreaSetting"));
+                    }
+                }
+            }
+            else return Ok(new { state = 201, message = "未找到默认能力点!" });
+
+            #endregion
+
+            return Ok(new { sate = RespondCode.Ok });
+        }
+
+
+        public record CopyStandard
+        {
+            public string id { get; set; }
+            public string standard { get; set; }
+            public string name { get; set; }
+        }
+
+
+    }
+}

+ 3 - 3
TEAMModelBI/TEAMModelBI.csproj

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

+ 1 - 1
TEAMModelBI/Tool/CommonFind.cs

@@ -189,7 +189,7 @@ namespace TEAMModelBI.Tool
         public static async Task<int> GetSqlValueCount(CosmosClient cosmosClient, string container, string SqlTxt,string code = null) 
         {
             int totals = 0;
-            await foreach (var item in cosmosClient.GetContainer("TEAMModelOS", container).GetItemQueryIterator<int>(queryText: SqlTxt,requestOptions: string.IsNullOrEmpty(code) ? new QueryRequestOptions() { } : new QueryRequestOptions() { PartitionKey = new PartitionKey(code) }))
+            await foreach (var item in cosmosClient.GetContainer("TEAMModelOS", container).GetItemQueryIterator<int>(queryText: SqlTxt, requestOptions: string.IsNullOrEmpty(code) ? new QueryRequestOptions() { } : new QueryRequestOptions() { PartitionKey = new PartitionKey($"{code}") }))
             {
                 totals = item;
             }

+ 11 - 9
TEAMModelOS.FunctionV4/CosmosDB/TriggerArt.cs

@@ -86,15 +86,17 @@ namespace TEAMModelOS.FunctionV4.CosmosDB
                             }
                             else
                             {
-                                long start = await _serviceBus.GetServiceBusClient().SendScheduleMessageAsync(Environment.GetEnvironmentVariable("Azure:ServiceBus:ActiveTask"), messageVote, DateTimeOffset.FromUnixTimeMilliseconds(tdata.startTime));
-                                ChangeRecord changeRecord = new ChangeRecord
-                                {
-                                    RowKey = tdata.id,
-                                    PartitionKey = PartitionKey,
-                                    sequenceNumber = start,
-                                    msgId = messageVote.MessageId
-                                };
-                                await table.Save<ChangeRecord>(changeRecord);
+                                if (art.classes.Count > 0) {
+                                    long start = await _serviceBus.GetServiceBusClient().SendScheduleMessageAsync(Environment.GetEnvironmentVariable("Azure:ServiceBus:ActiveTask"), messageVote, DateTimeOffset.FromUnixTimeMilliseconds(tdata.startTime));
+                                    ChangeRecord changeRecord = new ChangeRecord
+                                    {
+                                        RowKey = tdata.id,
+                                        PartitionKey = PartitionKey,
+                                        sequenceNumber = start,
+                                        msgId = messageVote.MessageId
+                                    };
+                                    await table.Save<ChangeRecord>(changeRecord);
+                                }                               
                             }
                             break;
                         case "going":

+ 12 - 6
TEAMModelOS.FunctionV4/CosmosDB/TriggerExam.cs

@@ -576,17 +576,23 @@ namespace TEAMModelOS.FunctionV4
                 losStu = losStu.Union(examResults[0].lostStus).ToList();
                 foreach (ExamResult examResult in examResults)
                 {
+                    //取交集
+                    losStu = losStu.Intersect(examResult.lostStus).ToList();
                     if (info.id == examResult.examId)
                     {
-                        foreach (List<double> sc in examResult.studentScores)
+                        //处理缺考的学生
+                        List<string> stus =  examResult.studentIds.Except(losStu).ToList();
+                        foreach (var id in stus) {
+                            int index = examResult.studentIds.IndexOf(id);
+                            score += examResult.studentScores[index].Sum();
+                        }
+                        /*foreach (List<double> sc in examResult.studentScores)
                         {
                             score += sc.Sum();
-                        }
-                        stuCount = examResult.studentIds.Count;
+                        }*/
+                        stuCount = examResult.studentIds.Count - losStu.Count;
                     }
-                    //powSum += Math.Pow(score - examResult.studentIds.Count > 0 ? Math.Round(score * 1.0 / examResult.studentIds.Count, 2) : 0, 2);
-                    //取交集
-                    losStu = losStu.Intersect(examResult.lostStus).ToList();
+                    //powSum += Math.Pow(score - examResult.studentIds.Count > 0 ? Math.Round(score * 1.0 / examResult.studentIds.Count, 2) : 0, 2);                   
                 }
                 double NewsRateScore = stuCount > 0 ? Math.Round(score * 1.0 / stuCount, 2) : 0;
                 foreach (PaperSimple simple in info.papers)

+ 74 - 59
TEAMModelOS.FunctionV4/HttpTrigger/ScsYxptApis.cs

@@ -152,20 +152,22 @@ namespace TEAMModelOS.FunctionV4.HttpTrigger
                 {
                     foreach (var ps in config.p)
                     {
-                        parameterMap["ProjectID"] = ps.pd;
-                        parameterMap["ProjectItemID"] = ps.pid;
-                        ScsResult result = new ScsResult { bizcode = Code, title = " 5.3.1.18根据机构ID、项目ID、子项目ID返回学校列表" };
-                        result = await _thirdApisService.Post(config.url, Code, config.passKey, config.privateKey, parameterMap);
-                        if (result.result)
-                        {
-                            List<ScSchool> schools = result.content.ToObject<List<ScSchool>>();
-                            if (schools.IsNotEmpty())
+                        if (ps.status == 1) {
+                            parameterMap["ProjectID"] = ps.pd;
+                            parameterMap["ProjectItemID"] = ps.pid;
+                            ScsResult result = new ScsResult { bizcode = Code, title = " 5.3.1.18根据机构ID、项目ID、子项目ID返回学校列表" };
+                            result = await _thirdApisService.Post(config.url, Code, config.passKey, config.privateKey, parameterMap);
+                            if (result.result)
                             {
-                                schools.ForEach(x => { x.ProjectID = int.Parse(ps.pd); x.ProjectItemID = int.Parse(ps.pid); });
-                                scSchools.AddRange(schools);
+                                List<ScSchool> schools = result.content.ToObject<List<ScSchool>>();
+                                if (schools.IsNotEmpty())
+                                {
+                                    //schools.ForEach(x => { x.ProjectID = int.Parse(ps.pd); x.ProjectItemID = int.Parse(ps.pid); });
+                                    scSchools.AddRange(schools);
+                                }
                             }
+                            results.Add(result);
                         }
-                        results.Add(result);
                     }
                 }
                 await response.WriteAsJsonAsync(new { data = scSchools.ToJsonString() });
@@ -207,19 +209,22 @@ namespace TEAMModelOS.FunctionV4.HttpTrigger
                 {
                     foreach (var ps in config.p)
                     {
-                        parameterMap["ProjectID"] = ps.pd;
-                        parameterMap["ProjectItemID"] = ps.pid;
-                        ScsResult result = new ScsResult { bizcode = Code, title = "5.3.1.2获取学员名单" };
-                        result = await _thirdApisService.Post(config.url, Code, config.passKey, config.privateKey, parameterMap);
-                        if (result.result)
+                        if (ps.status == 1)
                         {
-                            List<ScTeacher> teachers = result.content.ToObject<List<ScTeacher>>();
-                            if (teachers.IsNotEmpty())
+                            parameterMap["ProjectID"] = ps.pd;
+                            parameterMap["ProjectItemID"] = ps.pid;
+                            ScsResult result = new ScsResult { bizcode = Code, title = "5.3.1.2获取学员名单" };
+                            result = await _thirdApisService.Post(config.url, Code, config.passKey, config.privateKey, parameterMap);
+                            if (result.result)
                             {
-                                scTeachers.AddRange(teachers);
+                                List<ScTeacher> teachers = result.content.ToObject<List<ScTeacher>>();
+                                if (teachers.IsNotEmpty())
+                                {
+                                    scTeachers.AddRange(teachers);
+                                }
                             }
+                            results.Add(result);
                         }
-                        results.Add(result);
                     }
                 }
                 await response.WriteAsJsonAsync(new { data = scTeachers.ToJsonString() });
@@ -264,34 +269,42 @@ namespace TEAMModelOS.FunctionV4.HttpTrigger
                 {
                     foreach (var ps in config.p)
                     {
-                        parameterMap["ProjectID"] = ps.pd;
-                        parameterMap["ProjectItemID"] = ps.pid;
-                        parameterMap["SchoolID"] = $"{schoolCode}";
-                        parameterMap["School"] = $"{schoolCode}";
-                        ScsResult result = new ScsResult { bizcode = Code, title = " 5.3.1.20获取学校设置的可选能力点" };
-                        result = await _thirdApisService.Post(config.url, Code, config.passKey, config.privateKey, parameterMap);
-                        if (result.result)
-                        {
-                            List<ScPDiagnosis> diagnoses = result.content.ToObject<List<ScPDiagnosis>>();
-                            if (diagnoses.IsNotEmpty())
+                        if (ps.status == 1) {
+                            parameterMap["ProjectID"] = ps.pd;
+                            parameterMap["ProjectItemID"] = ps.pid;
+                            parameterMap["SchoolID"] = $"{schoolCode}";
+                            parameterMap["School"] = $"{schoolCode}";
+                            ScsResult result = new ScsResult { bizcode = Code, title = " 5.3.1.20获取学校设置的可选能力点" };
+                            result = await _thirdApisService.Post(config.url, Code, config.passKey, config.privateKey, parameterMap);
+                            if (result.result)
                             {
-                                projects.Add(new ScsProjectDiagnosis { project = ps, diagnoses = diagnoses, });
-                                await table.SaveOrUpdate<ScProjectDiagnosis>(new ScProjectDiagnosis {schoolCode=$"{schoolCode}",areaId= areaId,
-                                    ProjectID=int.Parse(ps.pd),
-                                    ProjectItemID=int.Parse(ps.pid),
-                                    RowKey = $"{ps.pid}-{schoolCode}", PartitionKey = "ScSchoolDiagnosis", abilityNos = diagnoses.ToJsonString() });
+                                List<ScPDiagnosis> diagnoses = result.content.ToObject<List<ScPDiagnosis>>();
+                                if (diagnoses.IsNotEmpty())
+                                {
+                                    projects.Add(new ScsProjectDiagnosis { project = ps, diagnoses = diagnoses, });
+                                    await table.SaveOrUpdate<ScProjectDiagnosis>(new ScProjectDiagnosis
+                                    {
+                                        schoolCode = $"{schoolCode}",
+                                        areaId = areaId,
+                                        ProjectID = int.Parse(ps.pd),
+                                        ProjectItemID = int.Parse(ps.pid),
+                                        RowKey = $"{ps.pid}-{schoolCode}",
+                                        PartitionKey = "ScSchoolDiagnosis",
+                                        abilityNos = diagnoses.ToJsonString()
+                                    });
+                                }
                             }
-                        }
-                        else
-                        {
-                            List<ScProjectDiagnosis> teacherDiagnoses = await table.FindListByDict<ScProjectDiagnosis>(new Dictionary<string, object> { { "PartitionKey", "ScSchoolDiagnosis" }, { "RowKey", $"{ps.pid}-{schoolCode}" } });
-                            if (teacherDiagnoses.IsNotEmpty())
+                            else
                             {
-                                projects.Add(new ScsProjectDiagnosis { project = ps, diagnoses = teacherDiagnoses[0].abilityNos.ToObject<List<ScPDiagnosis>>() });
+                                List<ScProjectDiagnosis> teacherDiagnoses = await table.FindListByDict<ScProjectDiagnosis>(new Dictionary<string, object> { { "PartitionKey", "ScSchoolDiagnosis" }, { "RowKey", $"{ps.pid}-{schoolCode}" } });
+                                if (teacherDiagnoses.IsNotEmpty())
+                                {
+                                    projects.Add(new ScsProjectDiagnosis { project = ps, diagnoses = teacherDiagnoses[0].abilityNos.ToObject<List<ScPDiagnosis>>() });
 
+                                }
                             }
+                            results.Add(result);
                         }
-                        results.Add(result);
                     }
                 }
                 await response.WriteAsJsonAsync(new { data = projects.ToJsonString() });
@@ -347,29 +360,31 @@ namespace TEAMModelOS.FunctionV4.HttpTrigger
                 {
                     foreach (var ps in config.p)
                     {
-                        parameterMap["ProjectID"] = ps.pd;
-                        parameterMap["ProjectItemID"] = ps.pid;
-                        ScsResult result = new ScsResult { bizcode = Code, title = "5.3.1.19获取项目设置的可选能力点" };
-                        result = await _thirdApisService.Post(config.url, Code, config.passKey, config.privateKey, parameterMap);
-                        if (result.result)
-                        {
-                            List<ScPDiagnosis> diagnoses = result.content.ToObject<List<ScPDiagnosis>>();
-                            if (diagnoses.IsNotEmpty())
+                        if (ps.status==1) {
+                            parameterMap["ProjectID"] = ps.pd;
+                            parameterMap["ProjectItemID"] = ps.pid;
+                            ScsResult result = new ScsResult { bizcode = Code, title = "5.3.1.19获取项目设置的可选能力点" };
+                            result = await _thirdApisService.Post(config.url, Code, config.passKey, config.privateKey, parameterMap);
+                            if (result.result)
                             {
-                                projects.Add(new ScsProjectDiagnosis { project = ps, diagnoses = diagnoses });
-                                await table.SaveOrUpdate<ScProjectDiagnosis>(new ScProjectDiagnosis { areaId=areaId, RowKey = $"{ps.pid}", PartitionKey = "ScProjectDiagnosis", abilityNos = diagnoses.ToJsonString() });
+                                List<ScPDiagnosis> diagnoses = result.content.ToObject<List<ScPDiagnosis>>();
+                                if (diagnoses.IsNotEmpty())
+                                {
+                                    projects.Add(new ScsProjectDiagnosis { project = ps, diagnoses = diagnoses });
+                                    await table.SaveOrUpdate<ScProjectDiagnosis>(new ScProjectDiagnosis { areaId = areaId, RowKey = $"{ps.pid}", PartitionKey = "ScProjectDiagnosis", abilityNos = diagnoses.ToJsonString() });
+                                }
                             }
-                        }
-                        else
-                        {
-                            List<ScProjectDiagnosis> teacherDiagnoses = await table.FindListByDict<ScProjectDiagnosis>(new Dictionary<string, object> { { "PartitionKey", "ScProjectDiagnosis" }, { "RowKey", $"{ps.pid}" } });
-                            if (teacherDiagnoses.IsNotEmpty())
+                            else
                             {
-                                projects.Add(new ScsProjectDiagnosis { project = ps, diagnoses = teacherDiagnoses[0].abilityNos.ToObject<List<ScPDiagnosis>>() });
+                                List<ScProjectDiagnosis> teacherDiagnoses = await table.FindListByDict<ScProjectDiagnosis>(new Dictionary<string, object> { { "PartitionKey", "ScProjectDiagnosis" }, { "RowKey", $"{ps.pid}" } });
+                                if (teacherDiagnoses.IsNotEmpty())
+                                {
+                                    projects.Add(new ScsProjectDiagnosis { project = ps, diagnoses = teacherDiagnoses[0].abilityNos.ToObject<List<ScPDiagnosis>>() });
 
+                                }
                             }
+                            results.Add(result);
                         }
-                        results.Add(result);
                     }
                 }
                 await response.WriteAsJsonAsync(new { data = projects.ToJsonString() });

+ 4 - 1
TEAMModelOS.FunctionV4/Lang/en-us.json

@@ -18,5 +18,8 @@
   "expire-school_lessonRecord": [ "Lesson record expiration notice", "Your lesson record, {lessonName}, on {schoolName} will expire at {expireTime}" ],
   "expire-private_lessonRecord": [ "Lesson record expiration notice", "Your lesson record, {lessonName} will expire at {expireTime}" ],
   "create-school": [ "Create schools in batches", "{tmdname}You successfully created schools in batch with Bi" ],
-  "copy-file_area": [ "Batch copy file start", "{tmdname}您用BI创区成功开始复制区域文件" ]
+  "copy-file_area": [ "Batch copy file start", "{tmdname}您用BI创区成功开始复制区域文件" ],
+  "art-template-comment1": "{studentName}同学,你在本次艺术评测活动中整体{level}。",
+  "art-template-comment2": "你在本次活动中{quotasHigh}等指标取得很好的成绩,希望继续保持。",
+  "art-template-comment3": "你在本次活动中{quotasLow}等指标有待提高,希望进一步加强。"
 }

+ 4 - 1
TEAMModelOS.FunctionV4/Lang/zh-cn.json

@@ -18,5 +18,8 @@
   "expire-school_lessonRecord": [ "课例到期通知", "您在{schoolName}的课例将在{expireTime}到期,课例名称:{lessonName}" ],
   "expire-private_lessonRecord": [ "课例到期通知", "您的课例将在{expireTime}到期,课例名称:{lessonName}" ],
   "create-school": [ "批量创建学校", "{tmdname}您用BI批量创建学校成功" ],
-  "copy-file_area": [ "批复制文件开始", "{tmdname}您用BI创区成功开始复制区域文件" ]
+  "copy-file_area": [ "批复制文件开始", "{tmdname}您用BI创区成功开始复制区域文件" ],
+  "art-template-comment1": "{studentName}同学,你在本次艺术评测活动中整体{level}。",
+  "art-template-comment2": "你在本次活动中{quotasHigh}等指标取得很好的成绩,希望继续保持。",
+  "art-template-comment3": "你在本次活动中{quotasLow}等指标有待提高,希望进一步加强。"
 }

+ 4 - 1
TEAMModelOS.FunctionV4/Lang/zh-tw.json

@@ -18,5 +18,8 @@
   "expire-school_lessonRecord": [ "課例到期通知", "您在{schoolName}的課例將在{expireTime}到期,課例名稱:{lessonName}" ],
   "expire-private_lessonRecord": [ "課例到期通知", "您的課例將在{expireTime}到期,課例名稱:{lessonName}" ],
   "create-school": [ "批量創建學校", "{tmdname}您用BI批量創建學校成功" ],
-  "copy-file_area": [ "批復制文件開始", "{tmdname}您用BI创区成功开始复制区域文件" ]
+  "copy-file_area": [ "批復制文件開始", "{tmdname}您用BI创区成功开始复制区域文件" ],
+  "art-template-comment1": "{studentName}同学,你在本次艺术评测活动中整体{level}。",
+  "art-template-comment2": "你在本次活动中{quotasHigh}等指标取得很好的成绩,希望继续保持。",
+  "art-template-comment3": "你在本次活动中{quotasLow}等指标有待提高,希望进一步加强。"
 }

+ 18 - 4
TEAMModelOS.SDK/DI/CoreAPI/CoreAPIHttpService.cs

@@ -167,7 +167,7 @@ namespace TEAMModelOS.SDK
         /// <param name="_configuration"></param>
         /// <param name="_dingDing"></param>
         /// <returns></returns>
-        public     void PushNotify(List<IdNameCode> toTeachers ,  string notifyCode,string notifyType,Dictionary<string, object> replaceData, 
+        public       void PushNotify(List<IdNameCode> toTeachers ,  string notifyCode,string notifyType,Dictionary<string, object> replaceData, 
             string location, IConfiguration _configuration, DI.DingDing _dingDing,string rootPath) {
             /*
              * IES5_Management  shift-assist_school  DelBeforeCopyAbility-mark_start  copyAbility-mark_finish  copyAbility-mark_start 
@@ -232,7 +232,13 @@ namespace TEAMModelOS.SDK
                 }
                 else { replaceData.Add("scope", "private"); }
                 var token = CoreTokenExtensions.CreateAccessToken(clientID, clientSecret, location).Result;
-                if (_httpClient.DefaultRequestHeaders.Contains("Authorization")) {
+                if (_httpClient.DefaultRequestHeaders.Contains("Authorization"))
+                {
+                    _httpClient.DefaultRequestHeaders.Remove("Authorization");
+                    _httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {token.AccessToken}");
+                }
+                else
+                {
                     _httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {token.AccessToken}");
                 }
                 foreach (var group in groups)
@@ -308,12 +314,20 @@ namespace TEAMModelOS.SDK
                                 });
                                 notifyData.body = msgs[1];
                             }
-                           
+                            string result = "";
                             HttpResponseMessage responseMessage = _httpClient.PostAsJsonAsync($"{url}/service/PushNotify", notifyData).Result;
                             if (responseMessage.StatusCode == HttpStatusCode.OK)
                             {
-                                string content =   responseMessage.Content.ReadAsStringAsync().Result;
+                                string content = responseMessage.Content.ReadAsStringAsync().Result;
+                                result = content;
+                            }
+                            else
+                            {
+                                result = $"{responseMessage.StatusCode},推送返回的状态码。";
+
                             }
+                          //  _dingDing.SendBotMsg($"{location}站点发送消息:\n{url}/service/PushNotify \nheader:  {token.AccessToken} \nresult:{result}\n params:{notifyData.ToJsonString()}", GroupNames.成都开发測試群組).GetAwaiter().GetResult();
+
                         }
                     }
                 }

+ 1 - 1
TEAMModelOS.SDK/DI/HttpTrigger/HttpTrigger.cs

@@ -38,7 +38,7 @@ namespace TEAMModelOS.SDK.DI
             string domain = "";
             if (location.Equals("China-Dep"))
             {
-                domain = keys[1];
+                domain = keys[0];
             }
             else if (location.Equals("China-Test"))
             {

+ 5 - 0
TEAMModelOS.SDK/Models/Cosmos/Common/ArtEvaluation.cs

@@ -4,6 +4,7 @@ using System.Collections.Generic;
 using System.Linq;
 using System.Text;
 using System.Text.Json;
+using System.Text.Json.Serialization;
 using System.Threading.Tasks;
 
 namespace TEAMModelOS.SDK.Models.Cosmos.Common
@@ -64,6 +65,7 @@ namespace TEAMModelOS.SDK.Models.Cosmos.Common
         //发布层级 0校级,1区级
         public int? publish { get; set; } = 0;
         public List<ArtSubject> subjects { get; set; } = new List<ArtSubject>();
+        public PeriodSimple period { get; set; }
     }
     public class Tasks {
         public string id { get; set; }
@@ -76,6 +78,8 @@ namespace TEAMModelOS.SDK.Models.Cosmos.Common
         public string name { get; set; }
     }
     public class Acs {
+        [JsonPropertyName("infoId")]
+        public string infoId { get; set; }
         public string acId { get; set; }
         public string name { get; set; }
         public string subject { get; set; }
@@ -83,5 +87,6 @@ namespace TEAMModelOS.SDK.Models.Cosmos.Common
         public int? type { get; set; } = 0;
         public string workDesc { get; set; }
         public long workEnd { get; set; }
+      
     }
 }

+ 21 - 0
TEAMModelOS.SDK/Models/Cosmos/Common/ArtExam.cs

@@ -0,0 +1,21 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Text.Json;
+using System.Threading.Tasks;
+
+namespace TEAMModelOS.SDK.Models.Cosmos.Common
+{
+    public  class ArtExam : CosmosEntity
+    {
+        public ArtExam()
+        {
+            pk = "ArtExam";
+        }
+        public string activityId { get; set; }
+        public JsonElement JsonElement { get; set; }
+
+
+    }
+}

+ 2 - 0
TEAMModelOS.SDK/Models/Cosmos/Common/GroupList.cs

@@ -207,6 +207,8 @@ namespace TEAMModelOS.SDK.Models
         //补充毕业
         //0在校,1毕业 
         public int graduate { get; set; } = 0;
+        //所在名单集合
+        public List<string> groupListIds { get; set; } = new List<string>();
 
     }
     public class GroupListGrp

+ 1 - 0
TEAMModelOS.SDK/Models/Cosmos/Common/Inner/Attachment.cs

@@ -54,5 +54,6 @@ namespace TEAMModelOS.SDK.Models
         /// 创建者
         /// </summary>
         public long creator { get; set; }
+        public List<string> tag { get; set; }= new List<string>();
     }
 }

+ 2 - 0
TEAMModelOS.SDK/Models/Cosmos/Normal/ArtSetting.cs

@@ -27,6 +27,8 @@ namespace TEAMModelOS.SDK.Models
     }
     public class ArtQuota
     { 
+        public int level { get; set; }
+        public string pid { get; set; }
         public string id { get; set; }
         public string name { get; set; }
         public double percent { get; set; }

+ 1 - 1
TEAMModelOS.SDK/Models/Cosmos/School/Knowledge.cs

@@ -52,7 +52,7 @@ namespace TEAMModelOS.SDK.Models
         /// 默认学校添加的 0,  1,区级统一设置的(学校不能删除,不能编辑)。
         /// </summary>
         public int source { get; set; } = 0;
-        public List<string> points { get; set; }
+        public List<string> points { get; set; } = new List<string>();
     }
     /*
     {

+ 6 - 1
TEAMModelOS.SDK/Models/Cosmos/Student/StudentArtResult.cs

@@ -16,11 +16,16 @@ namespace TEAMModelOS.SDK.Models
         public string picture { get; set; }
         public int userType { get; set; }
         public string school { get; set; }
-        public List<string> classIds { get; set; }
+        public List<string> classIds { get; set; } = new List<string>();
         public string artId { get; set; }
         public double totalScore { get; set; } = 0;
+        public List<ArtSubjectScore> subjectScores { get; set; } = new List<ArtSubjectScore>();
         public List<ArtQuotaResult> results { get; set; } = new List<ArtQuotaResult>();
     }
+    public class ArtSubjectScore {
+        public string subjectId { get; set; }
+        public double score { get; set; } = 0;
+    }
     public class ArtQuotaResult
     {
         /// <summary>

+ 72 - 0
TEAMModelOS.SDK/Models/Service/ArtService.cs

@@ -0,0 +1,72 @@
+using HTEXLib.COMM.Helpers;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace TEAMModelOS.SDK.Models.Service
+{
+    public static class ArtService
+    {
+        public static List<ArtQuota> GetParentByChildId(List<ArtQuota> quotas, string childId) {
+            List<ArtQuota> list_quotas = new List<ArtQuota>();
+            list_quotas = TreeToList(quotas, list_quotas, null);
+            List<ArtQuota> parents = new List<ArtQuota>();
+            parents = GetParents(list_quotas, parents, $"{childId}");
+            if (parents.IsNotEmpty()) {
+                int len = parents.Count;
+                parents.ForEach(x => {
+                    x.level= len-x.level+1;
+                });
+            }
+            return parents;
+        }
+
+        private static List<ArtQuota> GetParents(List<ArtQuota> list, List<ArtQuota> parents, string cid, int level = 1)
+        {
+            var child = list.Find(x => x.id.Equals(cid));
+            if (child != null)
+            {
+                child.level = level;
+                parents.Add(child);
+                if (!child.pid.Equals(child.id))
+                {
+                    level++;
+                    return GetParents(list, parents, child.pid, level);
+                }
+                else
+                {
+                    return parents;
+                }
+            }
+            else { return parents; }
+
+        }
+        private static List<ArtQuota> TreeToList(List<ArtQuota> trees, List<ArtQuota> nodes, string pid)
+        {
+            List<ArtQuota> list = new List<ArtQuota>();
+            trees.ForEach(x => {
+                var node = new ArtQuota
+                {
+                    pid = string.IsNullOrWhiteSpace(pid) ? x.id : pid,
+                    id = x.id,
+                    name = x.name,
+                    percent = x.percent,
+                    type = x.type,
+                };
+
+                list.Add(node);
+            });
+            nodes.AddRange(list);
+            foreach (ArtQuota tree in trees)
+            {
+                if (tree.children.IsNotEmpty())
+                {
+                    TreeToList(tree.children, nodes, tree.id);
+                }
+            }
+            return nodes;
+        }
+    }
+}

+ 7 - 4
TEAMModelOS.SDK/Models/Service/BatchCopyFileService.cs

@@ -26,11 +26,12 @@ namespace TEAMModelOS.SDK.Models.Service
 
             try
             {
-                List<Task<Response<BlobCopyInfo>>> filelist = new List<Task<Response<BlobCopyInfo>>>();
+                List<Task<CopyFromUriOperation>> filelist = new();   //可复制256M以上文件
+                //List<Task<Response<BlobCopyInfo>>> filelist = new();  //复制256M以下文件
                 var azureClient = _azureStorage.GetBlobContainerClient($"{batchCopyFile.blobCntr}");//获取容器连接地址
 
                 //先删除原有的文件
-                List<Task<Response<bool>>> DelList = new List<Task<Response<bool>>>();
+                List<Task<Response<bool>>> DelList = new();
                 await foreach (BlobItem blobItem in azureClient.GetBlobsAsync(BlobTraits.None, BlobStates.None, $"yxpt/{batchCopyFile.newFileName}/"))
                 {
                     DelList.Add(azureClient.GetBlobBaseClient(blobItem.Name).DeleteIfExistsAsync());
@@ -55,7 +56,8 @@ namespace TEAMModelOS.SDK.Models.Service
                 {
                     string newurl = $"{blobItem.Name}".Replace($"/{batchCopyFile.oldFileName}/", $"/{batchCopyFile.newFileName}/");//替换成新的容器路径
                     var urlSas = _azureStorage.GetBlobSAS($"{batchCopyFile.blobCntr}", blobItem.Name, BlobSasPermissions.Read | BlobSasPermissions.List);   //获取容器sas和有效期
-                    filelist.Add(azureClient.GetBlobClient(newurl).SyncCopyFromUriAsync(new Uri(urlSas)));  //添加复制文件到集合执行复制操作
+                    filelist.Add(azureClient.GetBlobClient(newurl).StartCopyFromUriAsync(new Uri(urlSas)));    //可复制256M以上文件
+                    //filelist.Add(azureClient.GetBlobClient(newurl).SyncCopyFromUriAsync(new Uri(urlSas)));  //添加复制文件到集合执行复制操作  复制256M以下文件
                 }
                 if (filelist.Count <= 256)
                 {
@@ -66,7 +68,8 @@ namespace TEAMModelOS.SDK.Models.Service
                     int pages = (filelist.Count + 255) / 256;
                     for (int i = 0; i < pages; i++)
                     {
-                        List<Task<Response<BlobCopyInfo>>> rspBlobCopyInfos = filelist.Skip((i) * 256).Take(256).ToList();
+                        List<Task<CopyFromUriOperation>> rspBlobCopyInfos = filelist.Skip((i) * 256).Take(256).ToList(); //可复制256M以上文件
+                        //List<Task<Response<BlobCopyInfo>>> rspBlobCopyInfos = filelist.Skip((i) * 256).Take(256).ToList(); //复制256M以下文件
                         await Task.WhenAll(rspBlobCopyInfos);
                     }
                 }

+ 3 - 2
TEAMModelOS.SDK/Models/Service/Third/Sc/ScYxptModel.cs

@@ -119,8 +119,8 @@ namespace TEAMModelOS.SDK.Models
         public string dist { get; set; }
         public string city { get; set; }
         public string areaId { get; set; }
-        public int ProjectID { get; set; }
-        public int ProjectItemID { get; set; }
+        //public int ProjectID { get; set; }
+        //public int ProjectItemID { get; set; }
     }
     
 
@@ -222,6 +222,7 @@ namespace TEAMModelOS.SDK.Models
         /// 子项目名称
         /// </summary>
         public string pit { get; set; }
+        public int status { get; set; }=1;
     }
     public class ScsResult
     {

+ 4 - 1
TEAMModelOS.SDK/Models/Service/Third/ThirdService.cs

@@ -122,7 +122,10 @@ namespace TEAMModelOS.SDK.Models
                                 string groupId = Guid.NewGuid().ToString();
                                 if (scTeachers.IsNotEmpty())
                                 {
-                                    groupName = scTeachers[0].TeacherXK;
+                                    if (scBind.pid.Equals("1249"))
+                                    {
+                                        groupName =$"第三批教师培训组";
+                                    }
                                     nickname = scTeachers[0].TeacherName;
                                     if (!string.IsNullOrEmpty(groupName))
                                     {

+ 2 - 1
TEAMModelOS/ClientApp/babel.config.js

@@ -10,6 +10,7 @@ module.exports = {
                 'styleLibraryName': 'theme-chalk'
             }
         ],
-        '@babel/plugin-proposal-optional-chaining'
+        '@babel/plugin-proposal-optional-chaining',
+        "@babel/plugin-syntax-import-assertions",
     ]
 }

+ 4 - 2
TEAMModelOS/ClientApp/package.json

@@ -38,7 +38,6 @@
     "js-md5": "^0.7.3",
     "js-sha1": "^0.6.0",
     "json-markup": "^1.1.3",
-    "jsonpath": "^1.1.1",
     "jspdf": "^2.3.1",
     "jspdf-autotable": "^3.5.23",
     "jszip": "^3.4.0",
@@ -60,7 +59,7 @@
     "vue-apexcharts": "^1.6.0",
     "vue-audio-native": "^0.1.41",
     "vue-b2wordcloud": "^1.0.5",
-    "vue-clipboard2": "^0.3.1",
+    "vue-clipboard2": "^0.3.3",
     "vue-count-to": "^1.0.13",
     "vue-lazyload": "^1.3.3",
     "vue-loading-overlay": "^3.3.3",
@@ -78,6 +77,7 @@
     "vuex-oidc": "^3.3.0",
     "vuex-router-sync": "^5.0.0",
     "wangeditor": "4.5.2",
+    "webpack": "^4.46.0",
     "xlsx": "^0.17.1"
   },
   "devDependencies": {
@@ -90,6 +90,7 @@
     "@babel/plugin-proposal-numeric-separator": "^7.0.0",
     "@babel/plugin-proposal-throw-expressions": "^7.0.0",
     "@babel/plugin-syntax-dynamic-import": "^7.0.0",
+    "@babel/plugin-syntax-import-assertions": "^7.18.6",
     "@babel/plugin-syntax-import-meta": "^7.0.0",
     "@babel/plugin-transform-async-to-generator": "^7.5.0",
     "@babel/plugin-transform-runtime": "^7.5.5",
@@ -121,6 +122,7 @@
     "event-source-polyfill": "^1.0.16",
     "file-loader": "^6.0.0",
     "font-awesome": "^4.7.0",
+    "html-webpack-plugin": "^4.4.1",
     "less-loader": "^5.0.0",
     "mini-css-extract-plugin": "^0.5.0",
     "optimize-css-assets-webpack-plugin": "^5.0.3",

+ 17 - 13
TEAMModelOS/ClientApp/public/lang/en-US.js

@@ -431,7 +431,8 @@ const LANG_EN_US = {
             studyAll: 'Student Academics Quality Monitoring',
             studyMenu: 'Learning Status Analysis',
             art1: 'Art Board',
-            art2: 'Art Setting'
+            art2: 'Art Setting',
+            art3: 'Assessment Activity',
         },
         artDimensionSetting: 'Art Assessment Index Setting',
         addDimension: 'Add Index',
@@ -466,9 +467,9 @@ const LANG_EN_US = {
         records: 'Lesson Record',
         over: 'Remaining',
         syllabus: 'Syllabus',
-        itemAndPaper: 'Question & Exam File',
+        itemAndPaper: 'Question Bank',
         appData: 'Application Data',
-        content: 'Content Module',
+        content: 'Content',
         used: 'Used:',
         expired: 'Service Due Date:',
         prodCont: 'Service Content:',
@@ -1108,6 +1109,7 @@ const LANG_EN_US = {
         classStuErr: 'Failure to obtain class list',
         unGroup: 'Ungrouped student',
         setGroupName: 'Please set the group name',
+        filterExpList:'Expiry List',
 
         //NewCusMgt.vue
         schdTable: 'Schedule Mode',
@@ -1199,7 +1201,6 @@ const LANG_EN_US = {
         cusImp28: 'Imported successfully',
         cusImp29: 'The imported fields are incomplete, please check the imported data',
 
-
         //MgtStuList.vue
         nameList: 'Name list',
         remvStu: 'Remove Student',
@@ -1356,7 +1357,7 @@ const LANG_EN_US = {
         tip3: 'Not an administrator',
         tip4: 'Operate successfully',
         tip5: 'Service Abnormal',
-        tip6: 'Have joined the school, please do not repeat the operation'
+        tip6: 'Already joined the school, please do not repeat the operation'
     },
     // ElementUI相关
     elui: {
@@ -1603,7 +1604,7 @@ const LANG_EN_US = {
             itemCount: 'Question No.',
             paperAnalysis: 'Exam File Analysis',
             totalScore: 'Total Points',
-            score: '',
+            score: ' point(s)',
             paperErrorTip: 'Failed to get exam file data!',
             confirmDelete: 'Are you sure you want to delete the file?',
             editPaper: 'Edit Exam File',
@@ -3682,7 +3683,7 @@ const LANG_EN_US = {
         irsRep: 'Duplicate IRS number',
         noRep: 'Duplicate seat number',
         getTeachersErr: 'Failed to get teacher list',
-        gdFreeze:'已毕业的班级不能被删除和修改',
+        gdFreeze:'Classes that have graduated cannot be deleted or modified',
 
         //ClassMgt.vue
         className: 'Name',
@@ -3707,7 +3708,8 @@ const LANG_EN_US = {
         noStuContent: 'The school has not imported any student account, do you want to go to Student Management to import student accounts?',
         setIrsWarning: 'Please set IRS number',
         setNoWarning: 'Please set seat number',
-        findGradClass: 'Graduated Classes',
+        findGradClass: 'Search for graduation class',
+        findGradClass1: 'Graduated Classes',
         findGradStu: 'Graduated Students',
         listExp: 'Valid Period',
         listExpHolder: 'Please set the valid period',
@@ -4192,7 +4194,7 @@ const LANG_EN_US = {
             title: {
                 classRecord: "Latest Lesson Records",
                 activity: "Latest Tasks",
-                notice1: "Announces",
+                notice1: "Announcement",
                 notice2: "Course Announcement",
                 exam: "Assessment Grade Analysis",
                 homework: "Homework Rating",
@@ -4411,7 +4413,7 @@ const LANG_EN_US = {
         addCourse: "Enter the course invitation code to join it",
         courseType: {
             success: "Join the course successfully",
-            warning: "Join the course repeatedly!",
+            warning: "You have already joined this course!",
             error: "Failed to join the course",
             api: "API Error",
             noAgree: "The course is not open to join",
@@ -4629,7 +4631,7 @@ const LANG_EN_US = {
             uploadTime: "Submit Time",
             upload: "Download",
             fileSize: "File Size",
-            maxSize: "Less than 50MB",
+            maxSize: "Less than ",
             mustFile: "Must upload a homework",
             fileType: "Format Restriction",
             allowComment: "Submit homework and you can evaluate other classmates after the homework ends",
@@ -5073,7 +5075,9 @@ const LANG_EN_US = {
         },
         art: {
             relatedFile: "Uploading related files",
-            noExam: "There is no assessment under the current subject, so there is no need for answering."
+            noExam: "There is no assessment under the current subject, so there is no need for answering.",
+            tip1: "If the uploaded data is a certificate, please select the certificate level after selecting the file to provide a reference for teacher evaluation",
+            tip2: "Is this upload a certificate?",
         },
     },
     // 问卷调查
@@ -5665,7 +5669,7 @@ const LANG_EN_US = {
         applyGrade: 'Grade',
         public: 'Public Resources',
         startDown: 'Start Downloading',
-        videoTips: 'Friendly reminder: Only MP4 format is supported for online video playing! ',
+        videoTips: 'Note: Only MP4 format is supported for online video playing! ',
         spaceTips: 'School Available Space = Total School Space - Space Allocated To Teachers',
         common: 'General (unassociated school sysyem resources)',
         renameTitle: 'Rename',

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

@@ -431,7 +431,8 @@ const LANG_ZH_CN = {
             studyAll: '学生学业质量监测',
             studyMenu: '学情分析',
             art1: '艺术看板',
-            art2: '艺术设置'
+            art2: '评测设置',
+            art3: '评测活动',
         },
         artDimensionSetting: '艺术测评指标设置',
         addDimension: '新增指标',
@@ -1108,6 +1109,7 @@ const LANG_ZH_CN = {
         classStuErr: '获取班级名单失败',
         unGroup: '未分组学生',
         setGroupName: '请设置组名',
+        filterExpList:'到期名单',
 
         //NewCusMgt.vue
         schdTable: '课表模式',
@@ -3710,6 +3712,7 @@ const LANG_ZH_CN = {
         setIrsWarning: '请设置IRS编号',
         setNoWarning: '请设置座号',
         findGradClass: '查询毕业班级',
+        findGradClass1: '毕业班级',
         findGradStu: '查询毕业学生',
         listExp: '有效期',
         listExpHolder: '请设置名单有效期',
@@ -4632,7 +4635,7 @@ const LANG_ZH_CN = {
             uploadTime: "提交时间",
             upload: "下载",
             fileSize: "文件大小",
-            maxSize: "不超过50MB",
+            maxSize: "不超过",
             mustFile: "必须上传文件",
             fileType: "格式限制",
             allowComment: "提交作业,即可在作业结束后进行互评",
@@ -5076,7 +5079,9 @@ const LANG_ZH_CN = {
         },
         art: {
             relatedFile: "上传相关文件",
-            noExam: "当前科目下未发布评测,无需作答。"
+            noExam: "当前科目下未发布评测,无需作答。",
+            tip1: "若上传资料为证书,请在选择文件后勾选证书等级,为老师提供考核参考",
+            tip2: "此次上传资料是否为证书?",
         },
     },
     // 问卷调查
@@ -6931,7 +6936,7 @@ const LANG_ZH_CN = {
     // 艺术评价模块
     ae:{
         ae0:'评测',
-        ae1:'作业',
+        ae1:'材料',
         ae2:'名称',
         ae3:'请输入艺术评测活动名称',
         ae4:'学科',
@@ -6967,8 +6972,8 @@ const LANG_ZH_CN = {
         ae34:'删除失败',
         ae35:'获取班级列表失败',
         ae36:'提交时间',
-        ae37:'作业描述:',
-        ae38:'请输入作业描述...',
+        ae37:'材料描述:',
+        ae38:'请输入材料描述...',
         ae39:'请完成评价指标详细设置',
         ae40:'请完成',
         ae41:'详细设置',

+ 20 - 15
TEAMModelOS/ClientApp/public/lang/zh-TW.js

@@ -431,7 +431,8 @@ const LANG_ZH_TW = {
             studyAll: '學生學業品質監測',
             studyMenu: '學情分析',
             art1: '藝術看板',
-            art2: '藝術設定'
+            art2: '評量設定',
+            art3: '評量活動',
         },
         artDimensionSetting: '藝術評量指標設定',
         addDimension: '新增指標',
@@ -440,7 +441,7 @@ const LANG_ZH_TW = {
         level: "級",
         rate: '比例',
         editQuota: '編輯藝術向度',
-        addQuota: '添加藝術向度',
+        addQuota: '新增藝術向度',
         rateSetting: '設定指標分數比例',
         noFullTip: '請確保100%比例合理分配! ',
         delDimenison: '刪除指標',
@@ -1108,6 +1109,7 @@ const LANG_ZH_TW = {
         classStuErr: '獲取班級名單失敗',
         unGroup: '未分組學生',
         setGroupName: '請設定組名',
+        filterExpList:'到期名單',
 
         //NewCusMgt.vue
         schdTable: '課表模式',
@@ -1356,7 +1358,7 @@ const LANG_ZH_TW = {
         tip3: '不是管理員',
         tip4: '操作成功',
         tip5: '服務端異常',
-        tip6: '已經加入學校,請勿重操作'
+        tip6: '已經加入學校,請勿重操作'
     },
     // ElementUI相关
     elui: {
@@ -1451,7 +1453,7 @@ const LANG_ZH_TW = {
         index: {
             item: '試題',
             paper: '試卷',
-            addExercise: '添加題目',
+            addExercise: '新增題目',
             openAll: '全部展開',
             collapseAll: '全部折疊',
             autoCreate: '智慧組卷',
@@ -3637,8 +3639,8 @@ const LANG_ZH_TW = {
         related: '已被關聯',
         noEnable: '此序號尚未啟用',
         onClassStu: '專科教室沒有固定學生名單!',
-        classNoErr: '班不能為空!',
-        classNoErr1: '班只能是數字!',
+        classNoErr: '班不能為空!',
+        classNoErr1: '班只能是數字!',
         roomNoErr: '教室編碼不能為空!',
         roomNoErr1: '教室編碼只能是數字或字母!',
         roomNoErr2: '教室編碼重複',
@@ -3685,7 +3687,7 @@ const LANG_ZH_TW = {
         irsRep: 'IRS號碼重複',
         noRep: '座號重複',
         getTeachersErr: '獲取教師列表失敗',
-        gdFreeze:'已毕业的班级不能被删除和修改',
+        gdFreeze:'已畢業的班級不能被刪除和修改',
 
         //ClassMgt.vue
         className: '名稱',
@@ -3711,6 +3713,7 @@ const LANG_ZH_TW = {
         setIrsWarning: '請設定IRS編號',
         setNoWarning: '請設定座號',
         findGradClass: '查詢畢業班級',
+        findGradClass1: '畢業班級',
         findGradStu: '查詢畢業學生',
         listExp: '效期',
         listExpHolder: '請設置名單效期',
@@ -4142,7 +4145,7 @@ const LANG_ZH_TW = {
         sit2: 'pw: 選填,密碼(若不輸入,預設和學生學號相同)',
         sit3: 'name: 必填,學生姓名',
         sit4: 'stuYear: 必填,學生入學年 ex:2020',
-        sit5: 'className: 必填,學生班級ex:20201班',
+        sit5: 'className: 必填,學生班級ex:20201班',
         sit6: 'classYear: 必填,班級年,通常與學生入學年相同 ex:2020',
         sit7: 'classId: 必填,班級編號ex:20200101',
         sit8: 'id格式錯誤',
@@ -4633,7 +4636,7 @@ const LANG_ZH_TW = {
             uploadTime: "提交時間",
             upload: "下載",
             fileSize: "文件大小",
-            maxSize: "不超過50MB",
+            maxSize: "不超過",
             mustFile: "必須上傳文件",
             fileType: "格式限制",
             allowComment: "提交作業,即可在作業結束後進行互評",
@@ -5077,7 +5080,9 @@ const LANG_ZH_TW = {
         },
         art: {
             relatedFile: "上傳相關文件",
-            noExam: "當前科目下未發佈評量,無需作答。"
+            noExam: "當前科目下未發佈評量,無需作答。",
+            tip1: "若上傳資料為證書,請在選擇檔案後勾選證書等級,提供老師考核參考",
+            tip2: "此次上傳資料是否為證書?",
         },
     },
     // 问卷调查
@@ -5968,7 +5973,7 @@ const LANG_ZH_TW = {
         importTips5: '3. email: 教師電子信箱,選填;',
         importTips6: '4. tmdid: 教師用戶編號,選填;',
         importTips7: '5. note: 教師備註內容,選填;',
-        importTips8: '溫馨提示:如果匯入的教師用戶編號,或者匯入的手機或信箱已註冊醍摩豆帳號,系統會自動邀請教師加入學校。',
+        importTips8: '溫馨提示:匯入的教師用戶編號,或者匯入的手機或信箱已註冊醍摩豆帳號(注意:必須先完成註冊,才可以使用註冊對應的手機或信箱進行匯入),系統會自動邀請教師加入學校。',
         importTips9: '點擊或者拖移文件匯入',
         impText: '無效數據(沒有名字):',
         impText1: '匯入總人數:',
@@ -6007,7 +6012,7 @@ const LANG_ZH_TW = {
         cusType: '學校課程:學校分配的課程,不允許自行新增、修改、刪除。 \n個人課程:您自己建立的課程,可以根據需要進行修改和調整。',
         semester: '您可自行設定學校的學期和入學期,如果沒有設定入學期系統會預設第一個學期為入學期。',
         time: '可設定學校作息時間,以利課程課表的產出。 若未設定則無法產出課表運用。',
-        stuModel: 'Id:學生學號(4-12位數字)\npw:密碼(若不輸入,預設值等於學生學號)\nname:學生姓名\nstuYear:學生入學年 ex:2020\nclassName:學生班級 ex:20201班\nclassYear:班級年,通常與學生入學年相同 ex:2020\nclassId:班級編碼 ex:20200101',
+        stuModel: 'Id:學生學號(4-12位數字)\npw:密碼(若不輸入,預設值等於學生學號)\nname:學生姓名\nstuYear:學生入學年 ex:2020\nclassName:學生班級 ex:20201班\nclassYear:班級年,通常與學生入學年相同 ex:2020\nclassId:班級編碼 ex:20200101',
         cusInfo: '依據學校定義的學科,建立所屬歸類的課程 Ex:”語文”學科下的 “閱讀理解”',
         cusTea: '加入負責該類課程授課的所有授課教師',
         sCusClass: '加入教師於該課程所負責的學生班級',
@@ -6931,7 +6936,7 @@ const LANG_ZH_TW = {
     // 艺术评价模块
     ae:{
         ae0:'評量',
-        ae1:'作業',
+        ae1:'材料',
         ae2:'名稱',
         ae3:'請輸入藝術評量活動名稱',
         ae4:'學科',
@@ -6967,8 +6972,8 @@ const LANG_ZH_TW = {
         ae34:'刪除失敗',
         ae35:'獲取班級列表失敗',
         ae36:'繳交時間',
-        ae37:'作業描述:',
-        ae38:'請輸入作業描述...',
+        ae37:'資料描述:',
+        ae38:'請輸入資料描述...',
         ae39:'請完成評量指標詳細設定',
         ae40:'請完成',
         ae41:'詳細設定',

+ 2 - 1
TEAMModelOS/ClientApp/src/.babelrc

@@ -18,7 +18,8 @@
     "@babel/plugin-proposal-function-sent",
     "@babel/plugin-proposal-export-namespace-from",
     "@babel/plugin-proposal-numeric-separator",
-    "@babel/plugin-proposal-throw-expressions"
+    "@babel/plugin-proposal-throw-expressions",
+    "@babel/plugin-syntax-import-assertions"
   ],
   "comments": false
 }

+ 14 - 0
TEAMModelOS/ClientApp/src/api/areaArt.js

@@ -12,9 +12,15 @@ export default {
     saveArt: function (data) {
         return post('/common/art/save', data)
     },
+    saveAreaArt: function (data) {
+        return post('/school/area/save-art', data)
+    },
     findArtList: function (data) {
         return post('/common/art/find', data)
     },
+    findAreaArtList: function (data) {
+        return post('/school/area/find-all-art', data)
+    },
     findArtSummary: function (data) {
         return post('/common/art/find-summary', data)
     },
@@ -29,4 +35,12 @@ export default {
     findArtWork: function (data) {
         return post('/common/art/find-summary-by-work', data)
     },
+    /* 教师端批量提交作业 */
+    batchUploadWork: function (data) {
+        return post('/common/art/upload-all', data)
+    },
+    /* 获取艺术评测看板数据 */
+    findArtDashAnalysis: function (data) {
+        return post('/analysis/art/statistics', data)
+    }
 }

+ 6 - 0
TEAMModelOS/ClientApp/src/api/http.js

@@ -20,6 +20,12 @@ const NO_ACCESS_API = [
     '/core/get-long-url',
     'restapi.amap.com', //新增的2个,获取城市地址及天气的地址
     'devapi.qweather.com', //新增的2个,获取城市地址及天气的地址
+    '/biz/get-loginuser',  //第三方登录 
+    '/biz/get-info',  //第三方获取企业详情
+    '/biz/set-bizuser', //第三方注册
+    '/biz/reset-secretkey', //第三方 重置
+    '/biz/get-openapi', //第三方 获取列表
+    '/biz/set-info', //第三方创建或者修改
 ]
 // 需要携带access_token 不需要携带auth-token
 const NO_AUTH_API = [

+ 5 - 0
TEAMModelOS/ClientApp/src/api/thirdparty.js

@@ -21,6 +21,11 @@ export default {
     getApilists: function(data) {
         return post('/biz/get-openapi', data)
     },
+    //创建企业或修改企业信息
+    updateThirdparty:function(data) {
+        return post('/biz/set-info', data)
+    },
+
     //获取地址location
     getlocation: function(key, location) {
         return fetch('https://restapi.amap.com/v3/ip?key=' + key + '&ip=' + location)

BIN
TEAMModelOS/ClientApp/src/assets/image/cus-import-cn.png


BIN
TEAMModelOS/ClientApp/src/assets/image/cus-import-en.png


BIN
TEAMModelOS/ClientApp/src/assets/image/cus-import-tw.png


+ 3 - 7
TEAMModelOS/ClientApp/src/boot-app.js

@@ -18,8 +18,6 @@ import loginTools from '@/access/login'
 import axios from 'axios'
 import { fetch, post } from '@/api/http'
 import jwtDecode from 'jwt-decode'
-import FileSaver from "file-saver";
-import JSONPath from 'jsonpath'
 import GLOBAL from '@/static/Global.js'
 import echarts from 'echarts'
 import vuescroll from 'vuescroll/dist/vuescroll-native'
@@ -30,7 +28,8 @@ import animated from 'animate.css'
 import VueLogger from 'vuejs-logger'
 import access from './access/index'
 import _ from 'lodash'
-import Icon from './icons/index.js'
+// import Icon from './icons/index.js'
+import './icons/index.js'
 import commonComponents from '@/common/index.js'
 import evaluationComponents from '@/view/evaluation/components/index.js'
 import '@/assets/iconfont/iconfont.css'
@@ -51,7 +50,6 @@ import NewChooseContent from '@/components/selflearn/NewChooseContent'
 import VueWordcloud from 'vue-b2wordcloud'
 Vue.use(VueWordcloud)
 
-
 jsFn.setLocalLang()
 require('video.js/dist/video-js.css')
 require('vue-video-player/src/custom-theme.css')
@@ -109,9 +107,7 @@ Vue.use(GLOBAL, {
     i18n: (key, value) => i18n.t(key, value)
 })
 // 各个插件
-Vue.prototype.$FileSaver = FileSaver
 Vue.prototype.$jwtDecode = jwtDecode
-Vue.prototype.$JSONPath = JSONPath
 Vue.prototype.$echarts = echarts
 Vue.prototype.$tools = tools
 Vue.prototype.$evTools = evTools
@@ -132,7 +128,7 @@ const app = new Vue({
     store,
     router,
     i18n,
-    Icon,
+    // Icon,
     //利用LocalStorage保存設定
     created() {
         if (localStorage.getItem("local")) {

+ 2 - 1
TEAMModelOS/ClientApp/src/common/TipsInfo.vue

@@ -40,8 +40,9 @@ export default {
     margin: auto;
 }
 .tips-text {
-    font-size: 16px;
+    font-weight: 800;
     max-width: 400px;
+    font-size: 20px;
 }
 </style>
 <style>

+ 321 - 0
TEAMModelOS/ClientApp/src/components/dashboard/art/BaseArtScatter.vue

@@ -0,0 +1,321 @@
+<template>
+  <div id="ArtScatter" class="art-echart"></div>
+</template>
+<script>
+export default {
+  data() {
+    return {
+      option: null,
+    }
+  },
+  methods: {
+    doRender(data, stus) {
+      let myChart = this.$echarts.init(document.getElementById('ArtScatter'))
+      // 散点数据
+      let marksData_ = data;
+      let marksData = []
+      marksData = marksData_.map((item, index) => {
+        let point = {
+          name: stus[index].name,
+          value: item
+        }
+        return point
+      })
+      // 中心线
+      let centerLine = [
+        {
+          name: 'y',
+          xAxis: 0.5,
+        },
+        {
+          name: 'x',
+          yAxis: 50,
+        },
+        {
+          name: 'x',
+          yAxis: 75,
+        },
+      ];
+      let _this = this
+      this.option = {
+        tooltip: {
+          trigger: 'item',
+          showDelay: 0,
+          axisPointer: {
+            show: true,
+            lineStyle: {
+              type: 'dashed',
+              width: 1
+            }
+          },
+
+          formatter: function (params) {
+            const item = params
+            return `${_this.$t('totalAnalysis.base_name')}:${item.name}
+                        <br/>${_this.$t('totalAnalysis.sca_chart_text1')}:${item.value[1]}%
+                        <br/>${_this.$t('totalAnalysis.sca_chart_text2')}:${item.value[0]}
+             `
+          }
+        },
+        grid: {
+          left: 20,
+          right: 20,
+          bottom: '10%',
+          top: '6%',
+          containLabel: true,
+        },
+        xAxis: {
+          scale: true,
+          type: 'value',
+          splitNumber: 2,
+          min: 0,
+          max: 1,
+          axisLine: {
+            lineStyle: {
+              color: '#ccc',
+            },
+          },
+          axisLabel: {
+            color: '#ccc',
+          },
+          splitLine: {
+            show: false,
+            lineStyle: {
+              color: '#ccc',
+            },
+          },
+        },
+        yAxis: {
+          scale: true,
+          axisLine: {
+            lineStyle: {
+              color: '#ccc',
+            },
+          },
+          interval: 25,
+          max: 100,
+          min: 0,
+          axisLabel: {
+            color: '#ccc',
+            formatter: function (value) {
+              let val = []
+              if (value !== 25) {
+                val.push(value + '%')
+              }
+              return val
+            }
+          },
+          splitLine: {
+            show: false,
+            lineStyle: {
+              color: '#eee',
+            },
+          },
+        },
+        dataZoom: [{
+          show: true,
+          height: 10,
+          start: 0,
+          end: 100,
+          xAxisIndex: [0],
+          bottom: 10,
+          handleIcon: 'path://M306.1,413c0,2.2-1.8,4-4,4h-59.8c-2.2,0-4-1.8-4-4V200.8c0-2.2,1.8-4,4-4h59.8c2.2,0,4,1.8,4,4V413z',
+          handleSize: '110%',
+          handleStyle: {
+            color: "#5B3AAE",
+          },
+          textStyle: {
+            color: "rgba(204,187,225,0.5)",
+          },
+          fillerColor: "rgba(67,55,160,0.4)",
+          borderColor: "rgba(204,187,225,0.5)",
+
+        }, {
+          type: "inside",
+          show: true,
+          height: 15,
+          start: 1,
+          end: 35
+        }],
+        series: [
+          {
+            type: 'scatter',
+            data: marksData,
+            label: {
+              show: true,
+              position: 'bottom',
+              formatter: '{b}',
+            },
+            itemStyle: {
+              shadowBlur: 2,
+              shadowColor: 'rgba(120, 36, 50, 0.5)',
+              shadowOffsetY: 1,
+              color: '#2d88f7',
+            },
+            // 各象限区域
+            markArea: {
+              silent: true,
+              data: [
+                [{
+                  xAxis: '0.5',
+                  yAxis: '100',
+                  itemStyle: {
+                    color: 'rgba(90,90,90,.1)'
+                  },
+                  label: {
+                    show: true,
+                    position: ['90%', '10%'],
+                    color: '#008955',
+                    fontSize: 20,
+                    formatter: 'A'
+                  }
+                }, {
+                  xAxis: '0',
+                  yAxis: '75',
+                  itemStyle: {
+                    color: ''
+                  }
+                }],
+                [{
+                  xAxis: '1',
+                  yAxis: '100',
+                  itemStyle: {
+                    color: 'rgba(255,255,255,0.1)'
+
+                  },
+                  label: {
+                    show: true,
+                    position: ['5%', '10%'],
+                    color: '#008955',
+                    fontSize: 20,
+                    formatter: "A'"
+                  }
+                }, {
+                  xAxis: '0.5',
+                  yAxis: '75'
+                }],
+                [{
+                  xAxis: '0.5',
+                  yAxis: '75',
+                  itemStyle: {
+                    color: 'rgba(255,255,255,0.1)'
+                  },
+                  label: {
+                    show: true,
+                    position: ['90%', '10%'],
+                    color: '#008955',
+                    fontSize: 20,
+                    formatter: 'B'
+                  }
+                }, {
+                  xAxis: '0',
+                  yAxis: '50'
+                }],
+                [{
+                  xAxis: '1',
+                  yAxis: '75',
+                  itemStyle: {
+                    color: 'rgba(90,90,90,.1)'
+                  },
+                  label: {
+                    show: true,
+                    position: ['5%', '10%'],
+                    color: '#008955',
+                    fontSize: 20,
+                    zIndex: 99999,
+                    formatter: "B'"
+                  }
+
+                }, {
+                  xAxis: '0.5',
+                  yAxis: '50'
+                }],
+                [{
+                  xAxis: '0.5',
+                  yAxis: '50',
+                  itemStyle: {
+                    color: 'rgba(90,90,90,.1)'
+
+                  },
+                  label: {
+                    show: true,
+                    position: ['90%', '10%'],
+                    color: '#008955',
+                    fontSize: 20,
+                    formatter: 'C'
+                  }
+                }, {
+                  xAxis: '0',
+                  yAxis: '0'
+                }],
+                [{
+                  xAxis: '1',
+                  yAxis: '50',
+                  itemStyle: {
+                    color: 'rgba(255,255,255,0.1)'
+
+                  },
+                  label: {
+                    show: true,
+                    position: ['5%', '10%'],
+                    color: '#008955',
+                    fontSize: 20,
+                    formatter: "C'"
+                  }
+                }, {
+                  xAxis: '0.5',
+                  yAxis: '0'
+                }]
+              ],
+            },
+            // 中心点交集象限轴
+            markLine: {
+              silent: true, // 是否不响应鼠标事件
+              precision: 2, // 精度
+              lineStyle: {
+                type: 'solid',
+                color: 'rgba(255,255,255,.2)',
+              },
+              label: {
+                color: 'rgba(255,255,255,.2)',
+                position: 'end',
+                formatter: '{b}',
+              },
+              data: centerLine,
+            },
+          },
+        ],
+      };
+      myChart.clear()
+      myChart.setOption(this.option)
+      window.addEventListener('resize', function () {
+        myChart.resize()
+      })
+    }
+  },
+  watch: {
+    '$store.state.artDashboard.scatterStuArr': {
+      deep: true,
+      immediate: true,
+      handler(n, o) {
+        if (n) {
+          this.$nextTick(() => {
+            let analysisJson = this.$store.state.artDashboard.artAnalysisJson
+            let scatterArr = n.map(student => [student.sta, (student.pass * 100).toFixed(2)])
+            this.doRender(scatterArr, analysisJson.students)
+          })
+        }
+      }
+    }
+  }
+}
+</script>
+
+<style>
+.art-echart {
+  width: 100%;
+  height: 100%;
+  margin: 0 auto;
+  display: block;
+}
+</style>

+ 15 - 6
TEAMModelOS/ClientApp/src/components/dashboard/art/BaseClassLineBar.vue

@@ -134,7 +134,7 @@ export default {
             name: '班级平均分',
             type: 'bar',
             yAxis: 0,
-            barWidth: '10%',
+            barWidth: 20,
             itemStyle: {
               normal: {
                 color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
@@ -153,7 +153,7 @@ export default {
             name: '班级最高分',
             type: 'bar',
             yAxis: 0,
-            barWidth: '10%',
+            barWidth: 20,
             itemStyle: {
               normal: {
                 color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
@@ -236,14 +236,23 @@ export default {
     }
   },
   watch: {
-    '$store.state.dashboard.artDashboard': {
-      deep: true,
+    '$store.state.artDashboard.curGradeId': {
       immediate: true,
       handler(n, o) {
         if (n) {
+          let artAnalysisJson = this.$store.state.artDashboard.artAnalysisJson
           this.$nextTick(() => {
-            console.log('*********',n.classData)
-            this.doRender(n.classData)
+            let analysisJson = n
+            let classDatas = artAnalysisJson.cInfo.map(classInfo => {
+              return {
+                className: classInfo.name,
+                average: classInfo.score,
+                hScore: classInfo.max,
+                goodRate: classInfo.excellent,
+                normalRate: Number((classInfo.pass * 100).toFixed(2))
+              }
+            })
+            this.doRender(classDatas)
           })
         }
       }

+ 14 - 3
TEAMModelOS/ClientApp/src/components/dashboard/art/BaseGradeLineBar.vue

@@ -63,7 +63,7 @@ export default {
         },
         xAxis: {
           type: 'category',
-          data: data.map(i => i.className),
+          data: data.map(i => i.gradeName),
           axisLine: {
             lineStyle: {
               color: '#eee'
@@ -236,13 +236,24 @@ export default {
     }
   },
   watch: {
-    '$store.state.dashboard.artDashboard': {
+    '$store.state.artDashboard.artAnalysisJson': {
       deep: true,
       immediate: true,
       handler(n, o) {
         if (n) {
           this.$nextTick(() => {
-            this.doRender(n.classData)
+            let analysisJson = n
+            let curPeriod = JSON.parse(localStorage.getItem('curPeriod'))
+            let gradeDatas = analysisJson.gscore.map(grade => {
+              return {
+                gradeName: curPeriod.grades[+grade.id],
+                average: grade.score,
+                hScore: grade.max,
+                goodRate: grade.excellent,
+                normalRate: Number((grade.pass * 100).toFixed(2))
+              }
+            })
+            this.doRender(gradeDatas)
           })
         }
       }

+ 21 - 11
TEAMModelOS/ClientApp/src/components/dashboard/art/BaseKnowPie.vue

@@ -8,7 +8,8 @@ export default {
       option: null,
       level1RenderArr: [],
       myChart: null,
-      curActiveIndex: 0
+      curActiveIndex: 0,
+      level1PieData: []
     }
   },
   methods: {
@@ -36,7 +37,7 @@ export default {
       this.option = {
         tooltip: {
           trigger: 'item',
-          formatter: '{a} <br/>{b}: {c} ({d}%)'
+          formatter: '{a} <br/>{b} 配分: {c} ({d}%)'
         },
         series: [
           {
@@ -52,7 +53,7 @@ export default {
               show: true,
               smooth: 0.2,
             },
-            data: this.level1RenderArr
+            data: that.level1PieData
           },
           {
             name: '二级知识点',
@@ -72,7 +73,7 @@ export default {
       };
       myChart.setOption(this.option)
       myChart.off('click')
-      myChart.on('click',{seriesIndex: 0}, function (param) {
+      myChart.on('click', { seriesIndex: 0 }, function (param) {
         if (that.curActiveIndex !== param.dataIndex) {
           param.data.selected = true
           that.curActiveIndex = param.dataIndex
@@ -88,31 +89,32 @@ export default {
       that.myChart = myChart
     },
     onLevel1Click(name, index, isAll) {
-      let allPoints = this.$store.state.dashboard.artDashboard.knowledges
+      let allPoints = this.$store.state.artDashboard.knowledges
       let level2NameArr = isAll ? [...new Set(allPoints.map(j => j.level2))] : [...new Set(allPoints.filter(i => i.level1 === name).map(j => j.level2))]
       let level2RenderArr = level2NameArr.map(level2Name => {
         return {
           name: level2Name,
-          value: allPoints.filter(j => j.level2 === level2Name).length
+          value: this.$store.state.artDashboard.artAnalysisJson.blk.find(i => i.name === level2Name).score
         }
       })
       this.level1RenderArr.forEach(i => i.selected = false)
       !isAll && (this.level1RenderArr[index].selected = true)
       this.$EventBus.$emit('onLevel1Click', [...arguments])
-      this.doRender(this.level1RenderArr, level2RenderArr)
+      this.doRender(this.level1PieData, level2RenderArr)
     }
   },
   watch: {
-    '$store.state.dashboard.artDashboard': {
+    '$store.state.artDashboard.knowledges': {
       deep: true,
       immediate: true,
       handler(n, o) {
         if (n) {
+          console.error(n)
           this.$nextTick(() => {
-            let level1Arr = [...new Set(n.knowledges.map(i => i.level1))]
-            let level2Arr = [...new Set(n.knowledges.map(i => i.level2))]
+            let level1Arr = [...new Set(n.map(i => i.level1))]
+            let level2Arr = [...new Set(n.map(i => i.level2))]
             let level1RenderArr = level1Arr.map((i, index) => {
-              let matchLevel3 = n.knowledges.filter(j => j.level1 === i)
+              let matchLevel3 = n.filter(j => j.level1 === i)
               return {
                 name: i,
                 value: ((matchLevel3.reduce((a, b) => a + b.val, 0)) / matchLevel3.length).toFixed(2),
@@ -120,6 +122,14 @@ export default {
               }
             })
             this.level1RenderArr = level1RenderArr
+            let blks = this.$store.state.artDashboard.artAnalysisJson.blk
+            this.level1PieData = level1Arr.map((i, index) => {
+              return {
+                name: i,
+                value: Number(blks.filter(j => j.dimension.includes(i)).reduce((a, b) => a + b.score, 0).toFixed(2)),
+                selected: index === 0
+              }
+            })
             this.onLevel1Click(level1RenderArr[0].name, 0)
           })
         }

+ 3 - 4
TEAMModelOS/ClientApp/src/components/dashboard/art/BasePointLineBar.vue

@@ -78,8 +78,8 @@ export default {
         dataZoom: [{
           show: true,
           height: 10,
-          start:data.length > 10 ? 20 : 0,
-          end:data.length > 10 ? 80 : 100,
+          start: data.length > 10 ? 20 : 0,
+          end: data.length > 10 ? 80 : 100,
           xAxisIndex: [0],
           bottom: 10,
           handleIcon: 'path://M306.1,413c0,2.2-1.8,4-4,4h-59.8c-2.2,0-4-1.8-4-4V200.8c0-2.2,1.8-4,4-4h59.8c2.2,0,4,1.8,4,4V413z',
@@ -128,8 +128,7 @@ export default {
   mounted() {
     this.$EventBus.$off('onLevel1Click')
     this.$EventBus.$on('onLevel1Click', val => {
-      let allPoints = this.$store.state.dashboard.artDashboard.knowledges
-      console.log('***',allPoints)
+      let allPoints = this.$store.state.artDashboard.knowledges
       this.doRender(val[2] ? allPoints : allPoints.filter(i => i.level1 === val[0]))
     })
   }

+ 55 - 25
TEAMModelOS/ClientApp/src/components/dashboard/art/BaseStuLineBar.vue

@@ -7,7 +7,8 @@ export default {
     return {
       option: null,
       isFromBarClick: false,
-      activeIndex: 3
+      activeIndex: 3,
+      classAverage: 0
     }
   },
   methods: {
@@ -56,8 +57,8 @@ export default {
         dataZoom: [{
           show: true,
           height: 10,
-          start: data.length > 10 ? 40 : 0,
-          end: data.length > 10 ? 60 : 100,
+          start: 0,
+          end: 100,
           xAxisIndex: [0],
           bottom: 10,
           handleIcon: 'path://M306.1,413c0,2.2-1.8,4-4,4h-59.8c-2.2,0-4-1.8-4-4V200.8c0-2.2,1.8-4,4-4h59.8c2.2,0,4,1.8,4,4V413z',
@@ -80,7 +81,7 @@ export default {
         }],
         xAxis: {
           type: 'category',
-          data: data.map(i => i.className),
+          data: data.map(i => i.stuName),
           axisLine: {
             lineStyle: {
               color: '#eee'
@@ -151,7 +152,7 @@ export default {
             name: '得分',
             type: 'bar',
             yAxis: 0,
-            barWidth: '10%',
+            barWidth: 20,
             itemStyle: {
               normal: {
                 color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
@@ -187,26 +188,36 @@ export default {
               }
               ]
             },
-            data: data.map(i => i.hScore),
+            data: data.map(i => i.score),
           },
           {
             name: '班级平均分',
-            type: 'bar',
-            yAxis: 0,
-            barWidth: '10%',
+            type: 'line',
+            symbol: 'none',
+            lineStyle: {
+              type: 'dashed',
+              width: 0
+            },
+            yAxisIndex: 1,
+            zlevel: 3,
+            markLine: {
+              data: [
+                { type: 'average' }
+              ],
+              lineStyle: {
+                color: '#66ff99',
+                type: 'dashed',
+                width: 2
+              }
+            },
             itemStyle: {
               normal: {
-                color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
-                  offset: 0,
-                  color: '#8bd46e'
-                }, {
-                  offset: 1,
-                  color: '#09bcb7'
-                }]),
-                barBorderRadius: 11,
+                color: 'rgb(255,188,10)',
+                borderColor: 'rgba(255,188,10,0.17)',
+                borderWidth: 12
               }
             },
-            data: data.map(i => i.average)
+            data: data.map(i => that.classAverage),
           }]
       };
       myChart.clear()
@@ -233,17 +244,36 @@ export default {
     }
   },
   watch: {
-    '$store.state.dashboard.artDashboard': {
-      deep: true,
+    // '$store.state.dashboard.artDashboard': {
+    //   deep: true,
+    //   immediate: true,
+    //   handler(n, o) {
+    //     if (n) {
+    //       this.$nextTick(() => {
+    //         if (this.isFromBarClick) {
+    //           this.isFromBarClick = false
+    //         } else {
+    //           this.doRender(n.classData)
+    //         }
+    //       })
+    //     }
+    //   }
+    // }
+    '$store.state.artDashboard.curClassId': {
       immediate: true,
       handler(n, o) {
         if (n) {
+          console.error(n)
+          let artAnalysisJson = this.$store.state.artDashboard.artAnalysisJson
           this.$nextTick(() => {
-            if (this.isFromBarClick) {
-              this.isFromBarClick = false
-            } else {
-              this.doRender(n.classData)
-            }
+            this.classAverage = artAnalysisJson.cInfo.find(i => i.id === n).score
+            let stuDatas = artAnalysisJson.students.filter(i => i.classId === n).map(stu => {
+              return {
+                stuName: stu.name,
+                score: stu.score,
+              }
+            })
+            this.doRender(stuDatas)
           })
         }
       }

+ 5 - 4
TEAMModelOS/ClientApp/src/components/dashboard/art/LeftBottom.vue

@@ -2,20 +2,21 @@
   <div id="bottomLeft">
     <p class="dashboard-block-title">
       <Icon type="md-pulse" />
-      <span>试卷整体质量分析</span>
+      <span>学生落点分析图</span>
       <dv-decoration-1 class="dv-dec-3" />
     </p>
     <div class="bg-color-black">
-      <dv-scroll-board :config="config" style="width:96%;margin-left:2%" />
+      <BaseArtScatter></BaseArtScatter>
+      <!-- <dv-scroll-board :config="config" style="width:96%;margin-left:2%" /> -->
     </div>
   </div>
 </template>
 
 <script>
-import BasePointLineBar from '@/components/dashboard/art/BasePointLineBar.vue'
+import BaseArtScatter from '@/components/dashboard/art/BaseArtScatter.vue'
 export default {
   components: {
-    BasePointLineBar
+    BaseArtScatter
   },
   data() {
     return {

+ 16 - 0
TEAMModelOS/ClientApp/src/components/dashboard/art/LeftCenter.vue

@@ -73,6 +73,22 @@ export default {
     isMusic() {
       return this.$store.state.dashboard.subject === 'music'
     }
+  },
+  watch: {
+    '$store.state.artDashboard.knowledges': {
+      deep: true,
+      immediate: true,
+      handler(n, o) {
+        if (n) {
+          console.error(n)
+          // this.$nextTick(() => {
+          //   let analysisJson = n
+          //   let scatterArr = analysisJson.students.map(student => [student.sta, student.pass])
+          //   this.doRender(scatterArr, analysisJson.students)
+          // })
+        }
+      }
+    }
   }
 }
 </script>

+ 9 - 13
TEAMModelOS/ClientApp/src/components/dashboard/art/LeftTop.vue

@@ -1,9 +1,5 @@
 <template>
   <div id="left-top-box">
-    <!-- <p class="dashboard-block-title">
-			<Icon type="md-cube" />
-			<span>累计数据</span>
-		</p> -->
     <div class="up">
       <div class="bg-color-black item" v-for="(item,index) in titleItem" :key="item.title">
         <p class="count-num">{{ item.number }} <span v-if="index === 5 || index === 6" style="font-size:14px">%</span> </p>
@@ -72,21 +68,21 @@ export default {
     // }
   },
   watch: {
-    '$store.state.dashboard.artDashboard': {
+    '$store.state.artDashboard.artAnalysisJson': {
       deep: true,
       immediate: true,
       handler(n, o) {
         if (n) {
           this.$nextTick(() => {
-            let staticNums = n.staticData
-            this.titleItem[0].number = staticNums.total
-            this.titleItem[1].number = staticNums.join
-            this.titleItem[2].number = staticNums.hScore
-            this.titleItem[3].number = staticNums.lScore
+            let staticNums = n
+            this.titleItem[0].number = staticNums.count
+            this.titleItem[1].number = staticNums.scount
+            this.titleItem[2].number = staticNums.max
+            this.titleItem[3].number = staticNums.min
             this.titleItem[4].number = staticNums.average
-            this.titleItem[5].number = staticNums.goodRate
-            this.titleItem[6].number = staticNums.normalRate
-            this.titleItem[7].number = staticNums.standard
+            this.titleItem[5].number = staticNums.excellent
+            this.titleItem[6].number = staticNums.pass
+            this.titleItem[7].number = staticNums.pow
           })
         }
       }

+ 41 - 8
TEAMModelOS/ClientApp/src/components/dashboard/art/RightBotL.vue

@@ -1,12 +1,13 @@
 <template>
   <div id="bottomLeft">
-	  <p class="dashboard-block-title">
-	  	<Icon type="md-pulse" />
-	  	<span>课程被挤占情况统计</span>
-		<dv-decoration-1 class="dv-dec-3" />
-	  </p>
-    <div class="bg-color-black">
-        <BaseLessonLineBar/>
+    <p class="dashboard-block-title">
+      <Icon type="md-pulse" />
+      <span>班级平均分排行榜</span>
+      <dv-decoration-1 class="dv-dec-3" />
+    </p>
+    <div class="bg-color-black" style="display:flex;justify-content:center;padding-top:30px;">
+      <!-- <BaseLessonLineBar/> -->
+      <dv-scroll-ranking-board :config="config" style="width:90%;height:100%" />
     </div>
   </div>
 </template>
@@ -16,6 +17,38 @@ import BaseLessonLineBar from '@/components/dashboard/art/BaseLessonLineBar.vue'
 export default {
   components: {
     BaseLessonLineBar
+  },
+  data() {
+    return {
+      config: {
+        rowNum: 8,
+        unit: '分',
+        waitTime: 3000,
+        data: []
+      }
+    }
+  },
+  watch: {
+    '$store.state.artDashboard.artAnalysisJson': {
+      deep: true,
+      immediate: true,
+      handler(n, o) {
+        if (n) {
+          this.$nextTick(() => {
+            let analysisJson = n
+            if (analysisJson.cInfo.length) {
+              this.config.data = analysisJson.cInfo.map(i => {
+                return {
+                  name: i.name,
+                  value: i.score
+                }
+              })
+            }
+            this.config = { ...this.config }
+          })
+        }
+      }
+    }
   }
 }
 </script>
@@ -31,7 +64,7 @@ export default {
   flex-direction: column;
   .bg-color-black {
     height: 91%;
-	padding-top: 10px;
+    padding-top: 10px;
   }
   .text {
     color: #c3cbde;

+ 91 - 5
TEAMModelOS/ClientApp/src/components/dashboard/art/RightBotR.vue

@@ -5,8 +5,10 @@
       <span>获奖情况统计</span>
       <dv-decoration-3 class="dv-dec-3" />
     </p>
-    <div class="bg-color-black">
-      <BaseAwardPie></BaseAwardPie>
+    <div class="bg-color-black" style="display:flex;align-items:center;padding-top:40px;flex-direction:column;">
+      <!-- <BaseAwardPie></BaseAwardPie> -->
+      <dv-capsule-chart :config="config2" style="width:300px;height:300px" />
+      <dv-active-ring-chart :config="config" style="width:300px;height:300px" />
     </div>
   </div>
 </template>
@@ -20,11 +22,95 @@ export default {
   },
   data() {
     return {
-
+      config: {
+        digitalFlopStyle: {
+          fontSize: 20,
+          fill: '#fff'
+        },
+        data: [
+          {
+            name: '国家证书',
+            key: '国家级',
+            value: 0
+          },
+          {
+            name: '省级证书',
+            key: '省级',
+            value: 0
+          },
+          {
+            name: '市级证书',
+            key: '市级',
+            value: 0
+          },
+          {
+            name: '区级证书',
+            key: '区级',
+            value: 0
+          },
+          {
+            name: '校级证书',
+            key: '校级',
+            value: 0
+          }
+        ]
+      },
+      config2: {
+        data: [
+          {
+            name: '国家证书',
+            key: '国家级',
+            value: 0
+          },
+          {
+            name: '省级证书',
+            key: '省级',
+            value: 0
+          },
+          {
+            name: '市级证书',
+            key: '市级',
+            value: 0
+          },
+          {
+            name: '区级证书',
+            key: '区级',
+            value: 0
+          },
+          {
+            name: '校级证书',
+            key: '校级',
+            value: 0
+          }
+        ],
+        colors: ['#e062ae', '#fb7293', '#e690d1', '#32c5e9', '#96bfff'],
+        unit: '个',
+        showValue: true
+      }
     }
   },
-  mounted() { },
-  methods: {}
+  watch: {
+    '$store.state.artDashboard.artAnalysisJson': {
+      deep: true,
+      immediate: true,
+      handler(n, o) {
+        if (n) {
+          this.$nextTick(() => {
+            let analysisJson = n
+            let awardKeys = Object.keys(analysisJson.optCount)
+            this.config.data.forEach(i => {
+              i.value = awardKeys.includes(i.key) ? analysisJson.optCount[i.key] : 0
+            })
+            this.config2.data.forEach(i => {
+              i.value = awardKeys.includes(i.key) ? analysisJson.optCount[i.key] : 0
+            })
+            this.config = { ...this.config }
+            this.config2 = { ...this.config2 }
+          })
+        }
+      },
+    }
+  }
 }
 </script>
 

+ 72 - 17
TEAMModelOS/ClientApp/src/components/dashboard/art/RightTop.vue

@@ -6,7 +6,7 @@
       <dv-decoration-1 class="dv-dec-3" />
     </p>
     <div class="select-wrap">
-      <Cascader v-model="cascaderVal" :data="classData" change-on-select @on-change="onSelect" :clearable="false"></Cascader>
+      <Cascader v-model="cascaderVal" :data="cascaderConfig" change-on-select @on-change="onSelect" :clearable="false"></Cascader>
     </div>
     <div class="bg-color-black">
       <BaseGradeLineBar v-if="classType === 'all'" />
@@ -39,7 +39,9 @@ export default {
   },
   data() {
     return {
-      cascaderVal: ['all']
+      cascaderConfig: [],
+      cascaderVal: ['all'],
+      classType: 'all'
     }
   },
   created() {
@@ -49,20 +51,32 @@ export default {
     onSelect(val) {
       console.log(val);
       if (val.length === 1) {
-        this.$store.commit('setRandomArtData', this.isYdzt ? 'grade' : 'all')
-
+        this.classType = 'all'
+        this.$store.commit('artDashboard/setAllStus')
       } else if (val.length === 2) {
-        // this.$store.commit('setRandomArtData', this.isYdzt ? 'single' : 'grade')
-        if (!this.isYdzt) {
-          this.$store.commit('setRandomArtData', 'grade')
-        } else {
-          this.$store.commit('setYdztStuData', val)
-        }
+        this.classType = 'grade'
+        console.error('年级切换', val[1])
+        this.$store.commit('artDashboard/setGradeId', val[1])
       } else {
-        if (!this.isYdzt) {
-          this.$store.commit('setRandomArtData', 'single')
-        }
+        this.classType = 'single'
+        console.error('班级切换', val[2])
+        this.$store.commit('artDashboard/setClassId', val[2])
       }
+      // if (val.length === 1) {
+      //   this.$store.commit('setRandomArtData', this.isYdzt ? 'grade' : 'all')
+
+      // } else if (val.length === 2) {
+      //   // this.$store.commit('setRandomArtData', this.isYdzt ? 'single' : 'grade')
+      //   if (!this.isYdzt) {
+      //     this.$store.commit('setRandomArtData', 'grade')
+      //   } else {
+      //     this.$store.commit('setYdztStuData', val)
+      //   }
+      // } else {
+      //   if (!this.isYdzt) {
+      //     this.$store.commit('setRandomArtData', 'single')
+      //   }
+      // }
     }
   },
   mounted() {
@@ -72,10 +86,10 @@ export default {
     }
   },
   computed: {
-    classType() {
-      console.log(this.$store.state.dashboard.classType);
-      return this.$store.state.dashboard.classType
-    },
+    // classType() {
+    //   console.log(this.$store.state.dashboard.classType);
+    //   return this.$store.state.dashboard.classType
+    // },
     isYdzt() {
       return localStorage.getItem('login_schoolCode') === 'ydzt'
     },
@@ -285,6 +299,47 @@ export default {
       }
     }
   },
+  watch: {
+    '$store.state.artDashboard.artAnalysisJson': {
+      deep: true,
+      immediate: true,
+      handler(n, o) {
+        if (n) {
+          this.$nextTick(() => {
+            let analysisJson = n
+            let curPeriod = JSON.parse(localStorage.getItem('curPeriod'))
+
+            // 全校
+            let cascaderConfig = [
+              {
+                value: 'all',
+                label: '全校',
+                children: []
+              }
+            ]
+            // 放入年级
+            analysisJson.gscore.forEach(grade => {
+              cascaderConfig[0].children.push({
+                value: grade.id,
+                label: curPeriod.grades[+grade.id],
+                children: []
+              })
+            })
+            // 放入班级
+            analysisJson.cInfo.forEach(classInfo => {
+              cascaderConfig[0].children.find(grade => grade.value === classInfo.gradeId).children.push({
+                value: classInfo.id,
+                label: classInfo.name,
+                children: []
+              })
+            })
+
+            this.cascaderConfig = cascaderConfig
+          })
+        }
+      }
+    }
+  }
 }
 </script>
 

+ 3 - 1
TEAMModelOS/ClientApp/src/components/mark/MarkCanvas.vue

@@ -762,6 +762,8 @@ export default {
         clear() {
             this.markLayer.removeChildren()
             this.stage.add(this.markLayer)
+            let img = this.saveAsImg()
+            this.$emit('getImg', img)
         },
 
         /* 保存批注 */
@@ -809,7 +811,7 @@ export default {
                 if (this.mouseStatus == 'move' && nodes.length == 0) {
                     if (this.moveBefore != 'eraser') {
                         this.mouseStatus = this.moveBefore
-                    }else{
+                    } else {
                         this.moveBefore = 'move'
                     }
                 }

+ 6 - 10
TEAMModelOS/ClientApp/src/components/selflearn/NewChooseContent.vue

@@ -130,8 +130,6 @@
 </template>
 <script>
 import { mapGetters } from 'vuex'
-import JSONPath from 'jsonpath'
-import BlobTool from '@/utils/blobTool.js'
 import ExerciseList from '@/components/selflearn/ExerciseList.vue'
 import BasePaperList from '@/view/evaluation/bank/TestPaperList.vue'
 export default {
@@ -372,10 +370,9 @@ export default {
     },
     //题库学科筛选条件
     getQSubjectList() {
-      let result = JSONPath.query(this.$store.state.user.schoolProfile.school_base.period, "$..[?(@.id=='" + this
-        .questionFilter.periodId + "')]")
-      if (result.length > 0) {
-        this.subjectList = result[0].subjects
+      let curPeriod = this.$store.state.user.schoolProfile.school_base.period?.find(item=>item.id === this.questionFilter.periodId)
+      if (curPeriod) {
+        this.subjectList = curPeriod.subjects
         if (this.subjectList.length > 0) {
           this.questionFilter.subjectId = this.subjectList[0].id
           this.refreshQuestionList()
@@ -625,10 +622,9 @@ export default {
     },
     //课纲学科筛选条件
     getSubjectList() {
-      let result = JSONPath.query(this.$store.state.user.schoolProfile.school_base.period, "$..[?(@.id=='" + this
-        .syllabusFilter.periodId + "')]")
-      if (result.length > 0) {
-        this.subjectList = result[0].subjects
+      let curPeriod = this.$store.state.user.schoolProfile.school_base.period?.find(item=>item.id === this.syllabusFilter.periodId)
+      if (curPeriod) {
+        this.subjectList = curPeriod.subjects
         if (this.subjectList.length > 0) {
           this.syllabusFilter.subjectId = this.subjectList[0].id
           this.getVolumes()

+ 1 - 1
TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContentTypeTemplate/Homework.vue

@@ -138,7 +138,7 @@
                                         <p>
                                             <Icon type="md-alert" color="#64ae16" />
                                             {{ $t('studentWeb.homework.fileSize') }}:
-                                            <span style="margin-right: 10px">{{ $t('studentWeb.homework.maxSize') }}</span>
+                                            <span style="margin-right: 10px">{{ $t('studentWeb.homework.maxSize') }}50MB</span>
                                         </p>
                                     </div>
                                     <div>

+ 110 - 24
TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContentTypeTemplate/PaperViewBox/ArtView.vue

@@ -8,6 +8,11 @@
                 <div>
                     <p class="head-box">{{ $t("homework.form.description") }}:{{ hwInfo.workDesc }}</p>
                     <p class="head-box">{{ $t("learnActivity.createEv.endTime") }}:{{ hwInfo.workEnd }}</p>
+                    <!-- <p class="head-box">文件类型:文档、音频、视频</p> -->
+                    <p class="head-box">{{ $t("studentWeb.homework.fileSize") }}:{{ $t('studentWeb.homework.maxSize') }}100MB</p>
+                    <p class="head-box" v-if="hwInfo.quotaId === 'quota_321'">
+                        {{ $t("answerSheet.dp.tip1") }}:{{ $t('studentWeb.art.tip1') }}
+                    </p>
                 </div>
                 <div style="margin-bottom: 20px" v-if="hwInfo.answer && hwInfo.answer.attachments.length">
                     <p>{{ $t('jyzx.offline.submitFile') }}:
@@ -20,7 +25,12 @@
                                     <img :src="$tools.getFileThum(item.type, item.name)"/>
                                 </div>
                                 <div class="file-info">
-                                    <p class="file-name">{{ item.name }}</p>
+                                    <p class="file-name">
+                                        {{ item.name }}
+                                        <template v-if="item.tag">
+                                            <span class="tag-style tag-subject" v-for="(tag, tIndex) in item.tag" :key="tIndex">{{ tag }}</span>
+                                        </template>
+                                    </p>
                                     <div>
                                         <span @click="onPreview(item)" v-if="item.type !== 'other'">{{ $t('ability.review.preview')}}</span>
                                         <span @click="onDownload(item)" v-if="item.type !== 'link'">{{ $t('ability.review.download')}}</span>
@@ -30,23 +40,28 @@
                         </div>
                     </div>
                 </div>
-                <Upload
-                    multiple
-                    type="drag"
-                    action=""
-                    :disabled="isUpload"
-                    :before-upload="customUpload"
-                    v-if="!hwInfo.overTime"
-                >
-                    <div style="padding: 20px 0">
-                        <Icon type="ios-cloud-upload" size="52" :style="{color: hwInfo.answer ? '#b4b4b4' : '#2d8cf0' }"></Icon>
-                        <p>
-                            <span>
-                                {{ hwInfo.answer ? $t('jyzx.offline.againLoad') : $t('studentWeb.art.relatedFile')}}
-                            </span>
-                        </p>
-                    </div>
-                </Upload>
+                <div style="position: relative;" class="upload-box">
+                    <Upload
+                        multiple
+                        type="drag"
+                        action=""
+                        :disabled="isUpload"
+                        :before-upload="customUpload"
+                        v-if="!hwInfo.overTime"
+                        
+                    >
+                        <div style="padding: 20px 0" ref="upload1">
+                            <Icon type="ios-cloud-upload" size="52" :style="{color: hwInfo.answer ? '#b4b4b4' : '#2d8cf0' }"></Icon>
+                            <p>
+                                <span>
+                                    {{ hwInfo.answer ? $t('jyzx.offline.againLoad') : $t('studentWeb.art.relatedFile')}}
+                                </span>
+                            </p>
+                        </div>
+                    </Upload>
+                    <!-- 为打开弹窗的一个按钮,隐藏样式 -->
+                    <Button class="cert-botton" @click="batchImport" v-if="hwInfo.quotaId === 'quota_321'">Primary</Button>
+                </div>
                 <div>
                     <div style="margin-top: 15px" v-if="uploadList.length">
                         <div style="margin-bottom: 20px;">
@@ -56,6 +71,14 @@
                                 </span>
                                 <div style="width: 100%;">
                                     <span style="font-size: 16px;">{{ item.name }}</span>
+                                    <!-- 艺术特长才会上传证书 -->
+                                    <span v-if="hwInfo.quotaId === 'quota_321' && item.tag" class="cert-level">
+                                        <span class="tag-style" :class="{'tag-subject': item.tag.includes('校级')}" @click="setCertLevel(index, '校级')">校级</span>
+                                        <span class="tag-style" :class="{'tag-subject': item.tag.includes('区级')}" @click="setCertLevel(index, '区级')">区级</span>
+                                        <span class="tag-style" :class="{'tag-subject': item.tag.includes('市级')}" @click="setCertLevel(index, '市级')">市级</span>
+                                        <span class="tag-style" :class="{'tag-subject': item.tag.includes('省级')}" @click="setCertLevel(index, '省级')">省级</span>
+                                        <span class="tag-style" :class="{'tag-subject': item.tag.includes('国家级')}" @click="setCertLevel(index, '国家级')">国家级</span>
+                                    </span>
                                     <Progress :percent="item.progress" status="active" stroke-color="#7CB7F5" />
                                 </div>
                             </div>
@@ -128,6 +151,9 @@ export default {
             ],
             isUpload: false,
             uploadList: [], //上传文件列表
+            maxSize: 100*1024*1024, //文件最大
+            extLimit: ['doc', 'docx', 'xls', 'xlsx', 'csv', 'ppt', 'pptx', 'pdf', 'txt', 'mp4', 'webm', 'mp3', 'wav', 'wma', 'jpg', 'jpeg', 'png', 'gif', 'zip', 'rar', '7zip'],
+            fileTag: false,
         }
     },
     computed: {
@@ -206,20 +232,37 @@ export default {
         selectionChange(selection) {
             this.selectionFile = selection
         },
+        batchImport() {
+            // 上传文件前询问是否为证书
+            this.$Modal.confirm({
+                title: this.$t('studentWeb.art.tip2'),
+                // content: '<p style="font-size: 16px;"></p>',
+                okText: this.$t("teachermgmt.yes"),
+                cancelText: this.$t("teachermgmt.no"),
+                onOk: () => {
+                    this.fileTag = true
+                    this.$refs["upload1"].click()
+                },
+                onCancel: () => {
+                    this.fileTag = false
+                    this.$refs["upload1"].click()
+                }
+            });
+        },
         // 文件展示
         customUpload(file) {
             // 有限制格式
-            /* if(this.homeworkInfo.extLimit.length) {
+            if(this.extLimit.length) {
                 let extension = file.name.substring(file.name.lastIndexOf(".") + 1, file.name.length).toLowerCase()
-                if(!this.homeworkInfo.extLimit.includes(extension)) {
-                    this.$Message.warning(this.$t("jyzx.offline.extLimit"))
+                if(!this.extLimit.includes(extension)) {
+                    this.$Message.warning(this.$t("studentWeb.homework.extLimit"))
                     return false
                 }
             }
             if(this.maxSize < file.size) {
-                this.$Message.warning(this.$t("jyzx.offline.sizeOver"))
+                this.$Message.warning(this.$t("studentWeb.homework.sizeOver"))
                 return false
-            } */
+            }
             let newFile = {
                 file,
                 name: file.name,
@@ -227,6 +270,9 @@ export default {
                 type: file.type,
                 progress: 0,
             }
+            if(this.fileTag) {
+                newFile.tag = ["校级"]
+            }
             this.uploadList.push(newFile)
             return false
         },
@@ -301,6 +347,9 @@ export default {
                                 res.hash = this.$tools.convertFileMD5ToString(res.md5)
                                 res.cnt = this.userInfo.azp
                                 res.creator = this.userInfo.sub
+                                if(item.tag) {
+                                    res.tag = item.tag
+                                }
                                 /* if(index === this.uploadIndex) {
                                     res.prime = true
                                 } else {
@@ -351,6 +400,14 @@ export default {
         async onDownload(item){
             this.$tools.doDownloadByUrl(item.url, item.name)
         },
+
+        setCertLevel(index, type) {
+            // 目前只能选一个
+            if(this.uploadList[index].tag[0] != type) {
+                this.uploadList[index].tag = [type]
+            }
+            this.$forceUpdate()
+        }
     }
 }
 </script>
@@ -415,9 +472,13 @@ export default {
             .file-name {
                 font-weight: bold;
                 margin-bottom: 5px;
+
+                span {
+                    margin-left: 10px;
+                }
             }
 
-            span {
+            & > div span {
                 color: #16a3b5;
                 margin-right: 15px;
                 cursor: pointer;
@@ -425,4 +486,29 @@ export default {
         }
     }
 }
+
+.cert-level {
+    margin-left: 15px;
+
+    .tag-style {
+        cursor: pointer;
+    }
+}
+
+.upload-box {
+    position: relative;
+
+    .cert-botton {
+        position: absolute;
+        top: 0;
+        left: 0;
+        width: 100%;
+        height: 115px;
+        opacity: 0;
+    }
+}
+
+.uploadHm {
+    margin-bottom: 15px;
+}
 </style>

+ 5 - 5
TEAMModelOS/ClientApp/src/components/student-web/EventView/EventList.vue

@@ -121,7 +121,7 @@
                                         {{ $t("studentWeb.exam.source.evMode3") }}
                                     </span>
                                     <span class="paper-extType" style="border-color: #88a1d8; color: #9fa91d;" v-if="item.source === null">
-                                        艺术评测
+                                        {{ $t("area.base.artData") }}
                                     </span>
                                     
                                     <!-- 课中评量才判断:qamode:0(书面问答),1(纸本测验) -->
@@ -416,7 +416,7 @@ import QuesNaire from "./EventContentTypeTemplate/QuesNaire";
             } else {
                 this.getActivityInfo()
             }
-            if(sessionStorage.getItem("areaId")) {
+            if(this.userInfo.area) {
                 this.getAreaSetting()
             }
             this.selectedCondition(this.getItemTitle);
@@ -553,7 +553,7 @@ import QuesNaire from "./EventContentTypeTemplate/QuesNaire";
             // 获取区级设置指标
             getAreaSetting() {
                 this.$api.areaArt.findArtSetting({
-                    areaId: sessionStorage.getItem("areaId")
+                    areaId: this.userInfo.area
                 }).then((res) => {
                     if (res.setting) {
                         this.getStuQuota(res.setting.quotas)
@@ -653,8 +653,8 @@ import QuesNaire from "./EventContentTypeTemplate/QuesNaire";
                             if(!this.showType.includes("Vote")) {
                             // if(this.showType[0] === 'Exam') {
                                 // 只匹配班级id
-                                // return this.showType.includes(item.type) && item.classIds.includes(this.courseNow.list)
-                                return this.showType.includes(item.type) && (item.classIds.includes(this.courseNow.list) || item.subjects.includes(this.courseNow.subject.id) || item.creatorId === this.courseNow.roster.teacherId)
+                                return this.showType.includes(item.type) && item.classIds.includes(this.courseNow.list)
+                                // return this.showType.includes(item.type) && (item.classIds.includes(this.courseNow.list) || item.subjects.includes(this.courseNow.subject.id) || item.creatorId === this.courseNow.roster.teacherId)
                             } else {
                                 return this.showType.includes(item.type)
                             }

+ 13 - 8
TEAMModelOS/ClientApp/src/components/student-web/HomeView/newHomeView.vue

@@ -4,7 +4,7 @@
         <div class="home-head home-card">
             <p class="home-title-name">
                 {{ $t("studentWeb.home.title.joinClass") }}
-                <Icon type="md-add-circle" color="#02B35A" size="17" title="加入课程" @click="addCourse = true" v-if="getAllCourse.length" />
+                <Icon type="md-add-circle" color="#02B35A" size="17" title="加入课程" @click="addCourse = true" v-if="getAllCourse.length && !onlySystem" />
             </p>
             <div class="home-join" v-if="getAllCourse.length">
                 <vuescroll>
@@ -86,7 +86,7 @@
                             <div v-for="(activity, aIndex) in activityList" :key="aIndex" class="list-new" @click="sentSelectedEventTitle(activity)">
                                 <div class="list-new-icon">
                                     <svg-icon v-if="activity.type == 'Homework'" icon-class="doc" color="#f0be72" />
-                                    <svg-icon v-if="activity.type == 'Exam'" color="#94a1e4" icon-class="test" class="reset-testIcon" />
+                                    <svg-icon v-if="activity.type == 'Exam' || activity.type == 'Atr'" color="#94a1e4" icon-class="test" class="reset-testIcon" />
                                     <svg-icon v-if="activity.type == 'Vote'" icon-class="vote" color="#6a9a8b" />
                                     <svg-icon v-if="activity.type == 'Survey'" icon-class="quesnaire" color="#d19f9c" />
                                 </div>
@@ -94,7 +94,7 @@
                                     <div class="paper-item-school">
                                         <span class="paper-owner" v-if="activity.owner === 'school'" style="background-color: #88a1d8;">{{ $t('studentWeb.public.school') }}</span>
                                         <span class="paper-owner" v-else>{{ $t('studentWeb.public.private') }}</span>
-                                        <template v-if="activity.type == 'Exam'">
+                                        <template v-if="activity.type == 'Exam' || activity.type == 'Atr'">
                                             <span class="paper-extType" :style="{'border-color': activity.owner === 'school' ? '#88a1d8' :'#ababab'}" v-if="activity.source === '0'">
                                                 {{ $t("studentWeb.exam.source.evMode1") }}
                                             </span>
@@ -104,6 +104,9 @@
                                             <span class="paper-extType" :style="{'border-color': activity.owner === 'school' ? '#88a1d8' :'#ababab', color: '#b68268'}" v-if="activity.source === '2'">
                                                 {{ $t("studentWeb.exam.source.evMode3") }}
                                             </span>
+                                            <span class="paper-extType" style="border-color: #88a1d8; color: #9fa91d;" v-if="activity.source === null">
+                                                {{ $t("area.base.artData") }}
+                                            </span>
                                             <!-- 课中评量才判断:qamode:0(书面问答),1(纸本测验) -->
                                             <template v-if="activity.source === '1'">
                                                 <span class="paper-extType" :style="{'border-color': activity.owner === 'school' ? '#88a1d8' :'#ababab', color: '#b68268'}"
@@ -211,8 +214,9 @@
         </Row>
         <Modal v-model="showNotice" :title="nowNotice ? nowNotice.title : ''" width="800">
             <template v-if="nowNotice">
-                <p>{{ nowNotice.content }}</p>
+                <p style="word-break: break-all;">{{ nowNotice.content }}</p>
             </template>
+            <div slot="footer"></div>
         </Modal>
         <!-- 添加课程 -->
         <Modal v-model="addCourse" :title="$t('studentWeb.home.title.addModal')" mask-closable>
@@ -528,7 +532,7 @@ export default {
                         })
                         item.teacherName = !nameIds ? undefined : nameIds.name
                         this.activityList.push(item)
-                        if(item.type === 'Exam') {
+                        if(item.type === 'Exam' || item.type === 'Atr') {
                             this.activityType.exam += 1
                         } else if(item.type === 'Homework') {
                             this.activityType.homework += 1
@@ -719,15 +723,16 @@ export default {
                 // 要同时匹配课程和班级id
                 // 学校发布的只匹配老师和名单
                 // return (isSchool ? ids === course.roster.teacherId : (ids.includes(course.id) || ids.includes(course.subject.id))) && classIds.includes(course.list)
-                // return classIds.includes(course.list)
-                let type1 = isSchool ? ids === course.roster.teacherId : (ids.includes(course.id))
-                return classIds.includes(course.list) || ids.includes(course.subject.id) || type1
+                return classIds.includes(course.list)
+                /* let type1 = isSchool ? ids === course.roster.teacherId : (ids.includes(course.id))
+                return classIds.includes(course.list) || ids.includes(course.subject.id) || type1 */
             })
         },
     },
     computed: {
         ...mapState({
             userInfo: state => state.userInfo,
+            onlySystem: state => state.studentWeb.onlySystem
         }),
         ...mapGetters(['getAllCourse'])
     },

Plik diff jest za duży
+ 311 - 313
TEAMModelOS/ClientApp/src/router/routes.js


+ 4 - 2
TEAMModelOS/ClientApp/src/store/index.js

@@ -9,6 +9,7 @@ import scboard from './module/scboard'
 import { GLOBAL } from '@/static/Global.js'
 import answerSheet from './module/answerSheet'
 import dashboard from './module/dashboard'
+import artDashboard from './module/artDashboard'
 import student from './module/student'
 
 
@@ -90,7 +91,7 @@ const getters = {
             if (item.schedule) {
                 item.schedule.forEach(classItem => {
                     let id = classItem.classId || classItem.stulist
-                    if (classItem.teacherId == teammodelId && id && !ids.includes(id)){
+                    if (classItem.teacherId == teammodelId && id && !ids.includes(id)) {
                         ids.push(id)
                     }
                 })
@@ -123,6 +124,7 @@ export default new Vuex.Store({
         scboard,
         answerSheet,
         dashboard,
-        student
+        student,
+        artDashboard
     }
 })

+ 43 - 0
TEAMModelOS/ClientApp/src/store/module/artDashboard.js

@@ -0,0 +1,43 @@
+// 艺术评测看板数据
+export default {
+    namespaced: true,
+    state: {
+        artAnalysisJson: null,
+        curGradeId: 0,
+        curClassId: 0,
+        knowledges: null,
+        scatterStuArr: []
+    },
+
+    mutations: {
+        setAnalysisJson: (state, value) => {
+            state.artAnalysisJson = value
+            let result = []
+            value.kno.forEach(point => {
+                point.block.forEach(block => {
+                    result.push({
+                        level1: value.blk.find(i => i.name === block).dimension[0],
+                        level2: block,
+                        name: point.name,
+                        val: +((point.score * 100).toFixed(2))
+                    })
+                })
+            })
+            console.error(result)
+            state.knowledges = result
+            state.scatterStuArr = value.students
+        },
+        setGradeId: (state, value) => {
+            state.curGradeId = value
+            state.scatterStuArr = state.artAnalysisJson.students.filter(i => i.gradeId === value)
+        },
+        setClassId: (state, value) => {
+            state.curClassId = value
+            state.scatterStuArr = state.artAnalysisJson.students.filter(i => i.classId === value)
+        },
+        setAllStus: (state, value) => {
+            state.scatterStuArr = state.artAnalysisJson.students
+        },
+    },
+    actions: {},
+};

+ 4 - 0
TEAMModelOS/ClientApp/src/store/module/studentWeb.js

@@ -92,6 +92,7 @@ export default {
         composeData: undefined,
         nowCourse: undefined,
         allCourse: [],
+        onlySystem: false,
     },
 
     //定義方法
@@ -323,6 +324,9 @@ export default {
         setAllCourse(state, data) {
             state.allCourse = data
         },
+        setOnlySystem(state, data) {
+            state.onlySystem = data
+        },
     },
     getters: {
         getStuUserInfo: (state) => {

+ 1 - 1
TEAMModelOS/ClientApp/src/utils/public.js

@@ -1100,8 +1100,8 @@ export default {
 				promiseArr.push(new Promise((r, j) => {
 					console.error(url)
 					var img = new Image();
-					img.src = url
 					img.setAttribute('crossOrigin', 'anonymous')
+					img.src = url
 					img.onload = function () {
 						var canvas = document.createElement("canvas");
 						canvas.width = img.width;

+ 2 - 3
TEAMModelOS/ClientApp/src/view/Home.vue

@@ -304,14 +304,14 @@ export default {
         let host = srvAdr == 'Global' ? this.$store.state.config.Global.coreAPIUrl : this.$store.state
           .config.China.coreAPIUrl
         this.$api.service.getNotification(host, {
-          "sender": [],
+          "senders": ['IES'],
           "receiver": userId
         }).then(res => {
           if (res.length) {
             let needMsgs = res.filter(msg => {
               let jsonStr = this.getBodyJson(msg.data)
               let bodyJson = jsonStr ? JSON.parse(jsonStr) : false
-              return bodyJson && bodyJson.value.scope
+              return bodyJson && bodyJson.value
             })
             r(needMsgs)
           } else {
@@ -537,7 +537,6 @@ export default {
 </style>
 
 <style>
-
 #main {
   width: 100%;
   height: 100%;

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

@@ -0,0 +1,336 @@
+<template>
+    <div>
+        <el-tree default-expand-all :data="treeData" :props="defaultProps" class="ac-quo-tree" node-key="id" ref="tree" :render-content="renderContent" :render-after-expand="false" @node-click="nodeClick"></el-tree>
+        <Modal v-model="uploadStatus" fullscreen @on-visible-change="handleHideModal" @on-ok="confirmUpload" :loading="modalLoading">
+            <p slot="header" style="height:fit-content">
+                批量上传
+            </p>
+            <div>
+                <Upload multiple class="batch-upload-box" type="drag" action="" :before-upload="handleBeforeUpload">
+                    <div style="padding: 30px 0;background:#f6f6f6;">
+                        <Icon type="ios-cloud-upload" size="52" style="color: #3399ff"></Icon>
+                        <p style="font-size:18px;margin-top:10px;margin-bottom:10px">
+                            批量上传学生资料<strong style="color:#ed4014">(文件请以学生姓名或者学生id命名)</strong>
+                        </p>
+                        <p>
+                            <Tag color="primary">
+                                {{curClass.name}}
+                            </Tag>
+                            <Tag color="success">
+                                {{subjectId}}
+                            </Tag>
+                        </p>
+                    </div>
+                </Upload>
+                <Table :columns="columns" :data="fileData">
+                    <template slot-scope="{ row }" slot="status">
+                        <strong :style="{color:row.studentId ? '#19be6b': '#ed4014'}">{{ row.studentId ? '匹配成功' : '匹配失败'}}</strong>
+                    </template>
+                    <template slot-scope="{ row }" slot="studentInfo">
+                        <strong v-if="row.studentId">{{ `${row.studentName}(${row.studentId})`}}</strong>
+                    </template>
+                </Table>
+            </div>
+        </Modal>
+    </div>
+</template>
+<script>
+import ExamData from "./ExamData.vue"
+import WorkData from "./WorkData.vue"
+import BlobTool from '@/utils/blobTool.js'
+export default {
+    components: {
+        ExamData, WorkData
+    },
+    props: {
+        treeData: {
+            type: Array,
+            default: () => {
+                return []
+            }
+        },
+        artInfo: {
+            type: Object,
+            default: () => {
+                return {}
+            }
+        },
+        curClass: {
+            type: Object,
+            default: () => {
+                return {}
+            }
+        },
+        subjectId: {
+            type: String,
+            default: ''
+        }
+    },
+    data() {
+        return {
+            modalLoading: true,
+            uploadStatus: false,
+            defaultProps: {
+                children: "children",
+                label: "name"
+            },
+            activeNode: undefined,
+            columns: [
+                {
+                    title: '文件',
+                    key: 'fileName'
+                },
+                {
+                    title: '状态',
+                    slot: 'status'
+                },
+                {
+                    title: '学生',
+                    slot: 'studentInfo'
+                },
+            ],
+            fileData: []
+        }
+    },
+    methods: {
+        getDefaultOpenNode(treeData) {
+            if (treeData[0].children.length) {
+                return this.getDefaultOpenNode(treeData[0].children)
+            }else{
+                return treeData[0]
+            }
+        },
+        confirmUpload() {
+            if (!this.fileData.length) return
+            let taskId = this.getTaskId(this.uploadNode.id)
+            if (!taskId) return
+            let schoolSas = {
+                sas: '?' + this.$store.state.user.schoolProfile.blob_sas,
+                url: this.$store.state.user.schoolProfile.blob_uri.slice(0, this.$store.state.user.schoolProfile.blob_uri.lastIndexOf(this.$store.state.userInfo.schoolCode) - 1),
+                name: this.$store.state.userInfo.schoolCode
+            }
+            let blobTool = new BlobTool(schoolSas.url, schoolSas.name, schoolSas.sas, 'school')
+            let promises = []
+            let useInfo = this.fileData.filter(item => !!item.studentId)
+            useInfo.forEach(fileItem => {
+                promises.push(blobTool.upload(fileItem.file, {
+                    path: `art/${this.artInfo.id}/homework/${taskId}/${fileItem.studentId}`
+                }))
+            })
+            Promise.all(promises).then(
+                reses => {
+                    console.log(reses)
+                    let stus = []
+                    reses.forEach((res, index) => {
+                        let stuItem = stus.find(s => s.stuId === useInfo[index].studentId)
+                        if (!stuItem) {
+                            stuItem = {
+                                stuId: useInfo[index].studentId,
+                                userType: useInfo[index].userType,
+                                attachments: []
+                            }
+                        }
+                        stuItem.attachments.push({
+                            name: res.name,
+                            url: res.url,
+                            size: res.size,
+                            createTime: res.createTime,
+                            extension: res.extension,
+                            type: res.type,
+                            blob: res.blob
+                        })
+                        stus.push(stuItem)
+                    })
+                    this.doSaveBatchUpload(stus, taskId)
+                },
+                err => {
+
+                }
+            )
+        },
+        doSaveBatchUpload(stus, acId) {
+            let params = {
+                artId: this.artInfo.id,
+                classId: this.curClass.id,
+                quotaId: this.uploadNode.id,
+                acId: acId,
+                subject: this.subjectId,
+                stus
+            }
+            this.$api.areaArt.batchUploadWork(params).then(
+                res => {
+                    if (!res.error) {
+                        this.$Message.success('上传成功')
+                        this.uploadStatus = false
+                        if (this.activeNode.id == this.uploadNode.id) {
+                            let comp = this.$refs[`${this.uploadNode.id}_data`]
+                            if (comp) comp.findArtWorkData()
+                        }
+                    } else {
+                        this.$Message.error('上传失败')
+                    }
+                },
+                err => {
+                    this.$Message.error('上传失败')
+                }
+            )
+        },
+        getTaskId(nodeId) {
+            let setting = this.artInfo?.settings.find(item => item.id === nodeId)
+            if (setting && setting.task) {
+                let task = setting.task.find(item => item.subject === this.subjectId)
+                return task?.acId || undefined
+            }
+            return undefined
+        },
+        handleHideModal(status) {
+            if (!status) this.fileData = []
+        },
+        handleBeforeUpload(file) {
+            let fileItem = {
+                file,
+                fileName: file.name,
+            }
+            if (this.curClass && this.curClass.members) {
+                let n = file.name.substring(0, file.name.lastIndexOf('.'))
+                let studentInfo = this.curClass.members.find(item => item.id == n || item.name === n)
+                if (studentInfo) {
+                    fileItem.studentName = studentInfo.name
+                    fileItem.studentId = studentInfo.id
+                    fileItem.userType = studentInfo.type
+                }
+                console.log(n, studentInfo)
+            }
+            let index = this.fileData.findIndex(item => item.fileName === file.name)
+            if (index >= 0) {
+                this.fileData.splice(index, 1, fileItem)
+            } else {
+                this.fileData.push(fileItem)
+            }
+            return false
+        },
+        getNodeSetting(id) {
+            return this.artInfo?.settings.find(item => item.id === id)
+        },
+        nodeClick(node) {
+            if (node.children.length) return
+            this.activeNode = node
+        },
+        hanldeCheckChange() { },
+        renderContent(h, { node, data, store }) {
+            let _this = this
+            // 不是最后一个节点则直接渲染label
+            if (node.childNodes.length) {
+                return h("span", {}, node.label)
+            } else {
+                return h(
+                    "div",
+                    {
+                        style: {
+                            width: "calc(100% - 30px)"
+                        }
+                    },
+                    [
+                        h("span", node.label),
+                        h(
+                            "Tag",
+                            {
+                                class: "type-setting",
+                                props: {
+                                    color: "default"
+                                },
+                                style: {
+                                    display:
+                                        node.data.type == 1 ||
+                                            node.data.type == 2
+                                            ? undefined
+                                            : "none"
+                                }
+                            },
+                            node.data.type == 1
+                                ? _this.$t('ae.ae0')
+                                : node.data.type == 2
+                                    ? _this.$t('ae.ae1')
+                                    : ""
+                        ),
+                        node.data.type === 2 ? h("Tag",
+                            {
+                                class: "type-setting",
+                                props: {
+                                    color: "primary",
+                                },
+                                nativeOn: {
+                                    click: (evt) => {
+                                        _this.uploadStatus = true
+                                        _this.uploadNode = node.data
+                                    }
+                                }
+                            },
+                            '批量导入') : undefined,
+                        _this.activeNode?.id == node.data.id &&
+                            node.data.type === 1
+                            ? h(
+                                ExamData,
+                                {
+                                    class: ["data-wrap"],
+                                    props: {
+                                        taskInfo: _this.getNodeSetting(node.data.id),
+                                        curClass: _this.curClass,
+                                        subjectId: _this.subjectId
+                                    }
+                                }
+                            )
+                            : undefined,
+                        _this.activeNode?.id == node.data.id &&
+                            node.data.type === 2
+                            ? _this.$createElement(
+                                WorkData,
+                                {
+                                    class: ["data-wrap"],
+                                    ref: node.data.id + '_data',
+                                    props: {
+                                        taskInfo: _this.getNodeSetting(node.data.id),
+                                        curClass: _this.curClass,
+                                        subjectId: _this.subjectId,
+                                    }
+                                }
+                            )
+                            : undefined
+                    ]
+                )
+            }
+        }
+    },
+    mounted() {
+        console.log('*****', this.treeData)
+        if (this.treeData && this.treeData.length) {
+            this.activeNode = this.getDefaultOpenNode(this.treeData)
+			console.log(this.activeNode)
+        }
+    }
+}
+</script>
+
+<style lang="less" scoped>
+.batch-upload-box {
+    // width: 90%;
+    margin: auto;
+    margin-top: 10px;
+}
+</style>
+<style lang="less">
+.ac-quo-tree .el-tree-node__content {
+    height: fit-content;
+    align-items: baseline;
+    padding: 8px 0px;
+}
+.data-wrap {
+    min-height: 300px;
+    background: #f5f7fa;
+    margin: 10px 0px;
+    width: calc(100% - 10px);
+}
+.type-setting {
+    margin-left: 10px;
+}
+</style>

+ 312 - 0
TEAMModelOS/ClientApp/src/view/areaArtExam/DataView.vue

@@ -0,0 +1,312 @@
+<template>
+    <div class="data-view-wrap">
+        <div class="data-count-wrap">
+            <!-- 学科统计 -->
+            <div class="data-count-item">
+                <p class="data-value">
+                    <countTo :startVal='0' :endVal='artInfo.subjects ? artInfo.subjects.length : 0' :duration='1000'></countTo>
+                </p>
+                <p class="data-text">
+                    {{$t('learnActivity.simple.sjLabel')}}
+                </p>
+            </div>
+            <!-- 班级 -->
+            <div class="data-count-item">
+                <p class="data-value">
+                    <countTo :startVal='0' :endVal='artInfo.classes ? artInfo.classes.length : 0' :duration='1000'></countTo>
+                </p>
+                <p class="data-text">
+                    {{$t('learnActivity.simple.classLabel')}}
+                </p>
+            </div>
+            <!-- 总人数 -->
+            <div class="data-count-item">
+                <p class="data-value">
+                    <countTo :startVal='0' :endVal='tableData.length' :duration='1000'></countTo>
+                </p>
+                <p class="data-text">
+                    {{$t('learnActivity.simple.totalPeople')}}
+                </p>
+            </div>
+            <!-- 已评分 -->
+            <div class="data-count-item">
+                <p class="data-value">
+                    -
+                </p>
+                <p class="data-text">
+                    已评分
+                </p>
+            </div>
+            <!-- 平均分 -->
+            <div class="data-count-item">
+                <p class="data-value">
+                    -
+                </p>
+                <p class="data-text">
+                    平均分
+                </p>
+            </div>
+        </div>
+        <div class="table-header-wrap">
+            <Select v-model="schoolId" style="width: 200px;margin-right:10px" placeholder="学校">
+                <Option v-for="item in schoolList" :value="item.id" :key="item.id">{{ item.name }}</Option>
+            </Select>
+            <Select v-model="classId" style="width: 200px">
+                <Option v-for="item in classList" :value="item.id" :key="item.id">{{ item.name }}</Option>
+            </Select>
+            <Input search v-model="keyword" placeholder="搜索学生..." style="margin-left:10px;width: 200px" @on-search="searchStudent" />
+        </div>
+        <div class="table-scroll-wrap">
+            <vuescroll>
+                <Table :columns="columns" :data="tableDataShow" border>
+                    <template slot-scope="{ row }" v-for="s in slotList" :slot="s">
+                        <div :key="s" style="padding:5px 5px">
+                            <ul>
+                                <li class="quota-score-item" v-for="task in row[s]" :key="task.taskId">
+                                    {{task.quotaName}}
+                                    <span class="quota-score-value" v-if="task.score >= 0">{{task.score}}分</span>
+                                    <span class="quota-score-value" v-else>-</span>
+                                </li>
+                            </ul>
+                            <p v-if="!row[s].length" class="no-score-tag">
+                                未评分
+                            </p>
+                        </div>
+                    </template>
+                </Table>
+            </vuescroll>
+        </div>
+        <!-- 分页 -->
+        <div class="page-wrap">
+            <Page show-total size="small" :current="currentPage" :total="tableData.length" :page-size="pageSize" @on-change="pageChange" />
+        </div>
+    </div>
+</template>
+
+<script>
+import countTo from 'vue-count-to'
+export default {
+    components: {
+        countTo
+    },
+    props: {
+        artInfo: {
+            type: Object,
+            default: () => {
+                return {}
+            }
+        },
+        quotaFirstLevel: {
+            type: Array,
+            default: () => {
+                return []
+            }
+        },
+        classList: {
+            type: Array,
+            default: () => {
+                return []
+            }
+        }
+    },
+    computed: {
+        curClass() {
+            if (this.classList && this.classList.length && this.classId) {
+                return this.classList.find(item => item.id == this.classId)
+            }
+            return {}
+        },
+        columns() {
+            let data = [
+                {
+                    title: '姓名',
+                    key: 'name',
+                    align: 'center',
+                    minWidth: 120,
+                    fixed: 'left',
+                },
+                {
+                    title: '总分',
+                    key: 'totalScore',
+                    align: 'center',
+                    minWidth: 80,
+                    fixed: 'right',
+                }
+            ]
+            if (this.artInfo?.subjects?.length) {
+                let stList = []
+                this.artInfo.subjects.forEach(subject => {
+                    let index = data.length - 1
+                    data.splice(index, 0, {
+                        title: subject.name,
+                        align: 'center',
+                        children: []
+                    })
+                    this.quotaFirstLevel.forEach(quo => {
+                        data[index].children.push({
+                            title: quo.name,
+                            align: 'center',
+                            minWidth: 180,
+                            slot: `${subject.id}-${quo.id}`
+                        })
+                        stList.push(`${subject.id}-${quo.id}`)
+                    })
+                })
+                this.slotList = stList
+            }
+            return data
+        }
+    },
+    data() {
+        return {
+            schoolList:[],
+            schoolId:'',
+            keyword: '',
+            pageSize: 10,
+            currentPage: 1,
+            classId: '',
+            slotList: [],
+            oringinData: [],
+            tableData: [],
+            tableDataShow: []
+        }
+    },
+    methods: {
+        searchStudent() {
+            this.tableData = this.oringinData.filter(item => item.name.includes(this.keyword))
+            this.pageChange(1)
+        },
+        // 分页页面变化
+        pageChange(page) {
+            let start = this.pageSize * (page - 1)
+            let end = this.pageSize * page
+            this.currentPage = page
+            this.tableDataShow = this.tableData.slice(start, end)
+        },
+        findArtResult() {
+            if (!this.classId) return
+            this.$api.areaArt.findArtResults({
+                "opt": "find",
+                "artId": this.artInfo.id,
+                "subjects": [],
+                "classIds": [this.classId]
+            }).then(res => {
+                if (!res.error) {
+                    this.setTableData(res.results)
+                } else {
+                    this.$Message.error('Fail1')
+                }
+            }).catch(e => {
+                this.$Message.error('Fail2')
+            })
+        },
+        setTableData(results) {
+            let students = this.curClass.members
+            this.oringinData = []
+            students.forEach(student => {
+                let row = {
+                    id: student.id,
+                    name: student.name
+                }
+                let studentRes = results.find(r => r.studentId === student.id)
+                if (studentRes) {
+                    this.slotList.forEach(st => {
+                        let arr = st.split('-')
+                        row[st] = studentRes.results.filter(res => {
+                            return res.subjectId === arr[0] && res.quotaId.includes(arr[1])
+                        })
+                    })
+                    row.totalScore = studentRes.totalScore
+                } else {
+                    this.slotList.forEach(st => {
+                        row[st] = []
+                    })
+                    row.totalScore = 0
+                }
+                this.oringinData.push(row)
+            })
+            this.searchStudent()
+        }
+    },
+    watch: {
+        classId: {
+            immediate: true,
+            deep: true,
+            handler(n, o) {
+                if (n) {
+                    this.findArtResult()
+                }
+            }
+        },
+        classList: {
+            immediate: true,
+            deep: true,
+            handler(n, o) {
+                if (n && n.length) {
+                    this.classId = n[0].id
+                } else {
+                    this.classId = ''
+                }
+            }
+        }
+    }
+}
+</script>
+<style lang="less" scoped>
+.data-count-item {
+    width: 150px;
+    text-align: center;
+    .data-value {
+        font-size: 30px;
+        font-weight: 600;
+        color: #17233d;
+    }
+}
+.data-count-wrap {
+    width: 100%;
+    height: 100px;
+    margin-bottom: 10px;
+    background: white;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+}
+.table-scroll-wrap {
+    width: 100%;
+    background: #ffffff;
+    padding: 0px 10px 10px 10px;
+    height: ~"calc(100% - 200px)";
+}
+.page-wrap {
+    width: 100%;
+    padding: 10px;
+    background: white;
+}
+.quota-score-item {
+    margin-left: 5px;
+    text-align: left;
+    list-style: square;
+    &::marker {
+        color: #085fec;
+    }
+}
+.quota-score-value {
+    color: #2d8cf0;
+    float: right;
+    font-weight: 600;
+}
+.no-score-tag {
+    color: #ed4014;
+}
+.data-view-wrap {
+    width: 100%;
+    height: ~"calc(100% - 50px)";
+    padding: 10px 10px;
+    background: #f6f6f6;
+}
+.table-header-wrap {
+    margin-top: 10px;
+    background: #ffffff;
+    padding: 10px;
+}
+</style>

+ 305 - 0
TEAMModelOS/ClientApp/src/view/areaArtExam/ExamData.vue

@@ -0,0 +1,305 @@
+<template>
+    <div class="exam-data">
+        <Table :height="520" :columns="tableColumn" :data="tableData" border :loading="tableLoading" style="margin-top:10px">
+            <template slot-scope="{ row,index }" :slot="'qu'+qIndex" v-for="(item,qIndex) in quCount">
+                <div :key="'qu'+qIndex" style="cursor:pointer;">
+                    <span v-if="row.data[qIndex] == -1 && row.status == 1">- -</span>
+                    <Icon size="20" type="ios-create-outline" color="#2db7f5" v-else-if="row.data[qIndex] == -1 && row.status !== 1" />
+                    <span style="color:#2db7f5;" v-else>{{row.data[qIndex]}}</span>
+                </div>
+            </template>
+            <template slot-scope="{ row }" slot="total">
+                <span style="color:#2db7f5;">{{ getTotalScore(row.data)}}</span>
+            </template>
+            <template slot-scope="{ row }" slot="name">
+                <span>{{ row.name}}</span>
+            </template>
+            <template slot-scope="{ row }" slot="id">
+                <span>{{ row.id}}</span>
+            </template>
+            <!-- 1: 未作答 2:未评分 3:已评分 4:缺考 5:补考中 -->
+            <template slot-scope="{ row,index }" slot="status">
+                <span class="stu-status-tag" :style="{'background':row.statusColor}">
+                    {{row.statusText}}
+                </span>
+            </template>
+        </Table>
+        <!-- 分页 -->
+        <div class="page-wrap">
+            <Page show-total size="small" :current="currentPage" :total="studentScore.length" :page-size="pageSize" @on-change="pageChange" />
+        </div>
+    </div>
+</template>
+
+<script>
+export default {
+    props: {
+        taskInfo: {
+            type: Object,
+            default: () => {
+                return {}
+            }
+        },
+        curClass: {
+            type: Object,
+            default: () => {
+                return {}
+            }
+        },
+        subjectId: {
+            type: String,
+            default: ''
+        }
+    },
+    data() {
+        return {
+            isRequesting: false,
+            currentPage: 1,
+            quCount: [],
+            scoreList: [
+                {
+                    title: this.$t('learnActivity.score.column1'),
+                    slot: "name",
+                    fixed: "left",
+                    align: "center",
+                    width: 150,
+                },
+                {
+                    title: 'ID',
+                    slot: "id",
+                    fixed: "left",
+                    align: "center",
+                    width: 150,
+                },
+                {
+                    title: '得分',
+                    slot: "total",
+                    align: "center",
+                    sortable: true,
+                    fixed: "right",
+                    minWidth: 100
+                },
+                {
+                    title: this.$t('learnActivity.score.column3'),
+                    slot: "status",
+                    align: "center",
+                    fixed: "right",
+                    width: 130,
+                }
+            ],
+            tableColumn: [],
+            // subjectId: '',
+            // classId: '',
+            tableLoading: false,
+            studentScore: [],
+            tableData: [],
+            pageSize: 10,
+            curPage: 1,
+            examInfo: {
+                progress: 'going'
+            }
+        }
+    },
+    methods: {
+        getTotalScore(data) {
+            let res = data.reduce((prev, curr, idx, arr) => {
+                prev = prev < 0 ? 0 : prev
+                curr = curr < 0 ? 0 : curr
+                return prev + curr
+            }, 0)
+            res = res.toString()
+            let index = res.indexOf('.')
+            res = index > 0 ? res.substr(0, index + 2) : res
+            return res
+        },
+        getExamId() {
+            let task = this.taskInfo.task?.find(item => item.subject == this.subjectId)
+            return task?.acId || ''
+        },
+        //获取学生作答详情数据
+        getStudentAnswer() {
+            // debugger
+            // if (this.isRequesting) return
+            this.isRequesting = true
+            let examId = this.getExamId()
+            if (!examId || !this.subjectId || !this.curClass.id) return
+            this.tableLoading = true
+            let requestData = {
+                id: examId,
+                code: this.$store.state.userInfo.schoolCode,
+                subjectId: this.subjectId,
+                classId: this.curClass.id,
+            };
+            this.$api.learnActivity.FindAllStudent(requestData).then(
+                (res) => {
+                    if (res.examClassResults && res.examClassResults.length) {
+                        this.setTableData(res.examClassResults[0])
+                        this.tableLoading = false
+                    }
+                },
+                (err) => {
+                }
+            ).finally(() => {
+                this.tableLoading = false
+                this.isRequesting = false
+            })
+        },
+        //初始化表单数据
+        setTableData(studentAns) {
+            let studentData = this.curClass?.members || []
+            this.studentScore = []
+            this.tableColumn = [...this.scoreList]
+            this.quCount = studentAns.studentScores[0] ? studentAns.studentScores[0].length : 0
+            for (let i = 0; i < this.quCount; i++) {
+                let data = {
+                    title: "Q" + (i + 1),
+                    slot: "qu" + i,
+                    renderHeader: (h, params) => {
+                        return h('div', {
+                            style: {
+                                cursor: 'pointer',
+                                fontSize: '13px'
+                            }
+                        }, [
+                            h('p', {
+                                class: 'table-qu-no'
+                            }, i + 1)
+                        ])
+                    },
+                    align: "center",
+                    minWidth: 70
+                }
+                this.tableColumn.push(data);
+            }
+            let ans = []
+            for (let i = 0; i < studentAns.studentIds.length; i++) {
+                for (let k = 0; k < studentData.length; k++) {
+                    let score = {}
+                    if (studentAns.studentIds[i] == studentData[k].id) {
+                        score.name = studentData[k].name
+                        score.type = studentData[k].type
+                        score.id = studentAns.studentIds[i]
+                        score.data = studentAns.studentScores[i]
+                        score.total = this.getcount(score.data)
+                        score.ansBlob = studentAns.studentAnswers[i]
+                        score.mark = studentAns.mark[i]
+                        let { status, statusText, statusColor } = this.getStatusInfo(studentAns.studentScores[i], studentAns.status[i])
+                        score.status = status
+                        score.statusText = statusText
+                        score.statusColor = statusColor
+                        this.studentScore.push(score)
+                    }
+                }
+            }
+            this.pageChange(1)
+        },
+        //分数求和
+        getcount(arr) {
+            return arr.reduce((total, item) => {
+                if (item !== -1) {
+                    return total + item;
+                } else {
+                    return total;
+                }
+            }, 0);
+        },
+        // 分页页面变化
+        pageChange(page) {
+            let start = this.pageSize * (page - 1)
+            let end = this.pageSize * page
+            this.currentPage = page
+            this.tableData = this.studentScore.slice(start, end)
+        },
+        getStatusInfo(score, status) {
+            if (status == -1) {
+                return {
+                    status: -1,
+                    statusText: this.$t('learnActivity.score.status6'),
+                    statusColor: '#ff9900'
+                }
+            }
+            //评测设置学生可以补考
+            if (status == 2 || status == 3) {
+                return {
+                    status: 3,
+                    statusText: this.$t('learnActivity.score.status5'),
+                    statusColor: '#2db7f5'
+                }
+            }
+
+            //评测结束,学生缺考
+            if (status == 1 && this.examInfo.progress == 'finish') {
+                return {
+                    status: 1,
+                    statusText: this.$t('learnActivity.score.status4'),
+                    statusColor: '#ed4014'
+                }
+            }
+
+            //评测进行中,未作答
+            if (status == 1 && this.examInfo.progress == 'going') {
+                return {
+                    status: 0,
+                    statusText: this.$t('learnActivity.score.status1'),
+                    statusColor: '#808695'
+                }
+            }
+            //已作答,未评分
+            if (score.includes(-1)) {
+                return {
+                    status: 2,
+                    statusText: this.$t('learnActivity.score.status7'),
+                    statusColor: '#ff9900'
+                }
+            }
+            //已作答已评分
+            return {
+                status: 4,
+                statusText: this.$t('learnActivity.score.status7'),
+                statusColor: '#19be6b'
+            }
+        },
+    },
+    watch: {
+        subjectId: {
+            immediate: true,
+            handler(n, o) {
+                if (n) {
+                    this.getStudentAnswer()
+                }
+            }
+        },
+        curClass: {
+            immediate: true,
+            deep: true,
+            handler(n, o) {
+                if (n && n.id) {
+                    this.getStudentAnswer()
+                }
+            }
+        }
+    }
+}
+</script>
+
+<style lang="less" scoped>
+.page-wrap {
+    margin-top: 15px;
+}
+
+.stu-status-tag {
+    cursor: pointer;
+    background: #ed4014;
+    font-size: 12px;
+    padding: 3px 8px;
+    font-weight: 800;
+    border-radius: 4px;
+    color: white;
+}
+.exam-data {
+    padding: 10px 10px;
+    border-radius: 6px;
+    // background: #f0f0f0;
+    background: #f5f7fa;
+}
+</style>

+ 374 - 0
TEAMModelOS/ClientApp/src/view/areaArtExam/Mgt.vue

@@ -0,0 +1,374 @@
+<template>
+    <div class="art-exam-container">
+        <Split v-model="split">
+            <div class="art-exam-list-wrap" slot="left">
+                <div class="art-mgt-top light-iview-select light-iview-input">
+                    <span>{{$t('ae.ae28')}}</span>
+                    <span class="to-create-art" @click="toCreate">
+                        <Icon type="md-add" />
+                    </span>
+                    <span class="to-create-art" @click="delArt">
+                        <Icon type="md-trash" />
+                    </span>
+                </div>
+                <div style="height:calc(100% - 50px)">
+                    <vuescroll>
+                        <div :class="['art-item', curIndex == index ? 'art-item-active' : '']" v-for="(item,index) in artList" :key="item.id" @click="selectArt(index)">
+                            <p class="art-name">{{item.name}}</p>
+                            <p class="art-date">
+                                <Icon type="md-time" size="16" />{{$jsFn.dateFormat(item.startTime)}}-{{$jsFn.dateFormat(item.endTime)}}
+                            </p>
+                        </div>
+                        <EmptyData v-show="!artList.length" :top="100"></EmptyData>
+                    </vuescroll>
+                </div>
+            </div>
+            <div class="art-content" slot="right">
+                <div class="art-exam-bar">
+                    <!-- 数据概览 -->
+                    <span :class="curBarIndex == 0 ? 'art-exam-bar-item line-bottom-active line-bottom':'art-exam-bar-item line-bottom'" @click="selectBar(0)">
+                        数据概览
+                    </span>
+                    <!-- 评价指标 -->
+                    <span :class="curBarIndex == 1 ? 'art-exam-bar-item line-bottom-active line-bottom':'art-exam-bar-item line-bottom'" @click="selectBar(1)">
+                        评价指标
+                    </span>
+                </div>
+                <DataView :artInfo="artInfo" :quotaFirstLevel="quotaFirstLevel" :classList="classList" v-if="curBarIndex === 0"></DataView>
+                <div v-else-if="curBarIndex === 1" class="quo-detail-wrap">
+                    <vuescroll style="background:white">
+                        <Tabs v-model="tabName" style="padding: 0px 10px;margin-top:15px">
+                            <template slot="extra">
+                                <div class="art-filter-wrap">
+                                    <!-- <span>{{$t('ae.ae6')}}:</span> -->
+                                    <Select v-model="schoolId" style="width: 200px" placeholder="学校">
+                                        <Option v-for="item in schoolList" :value="item.id" :key="item.id">{{ item.name }}</Option>
+                                    </Select>
+                                    <Select v-model="classId" style="width: 200px" placeholder="班级">
+                                        <Option v-for="item in classList" :value="item.id" :key="item.id">{{ item.name }}</Option>
+                                    </Select>
+                                    <!-- <span style="margin-left:10px">{{$t('ae.ae4')}}:</span> -->
+                                    <Select v-model="subjectId" style="width: 120px" placeholder="学科">
+                                        <Option v-for="item in subjectList" :value="item.id" :key="item.id">{{ item.name }}</Option>
+                                    </Select>
+                                </div>
+                            </template>
+                            <TabPane v-for="(item, index) in tabListShow" :key="index" :label="item.label" :name="item.name">
+                                <AcQuos :artInfo="artInfo" :treeData="tabTree[item.name]" :curClass="curClass" :subjectId="subjectId"></AcQuos>
+                            </TabPane>
+                        </Tabs>
+                    </vuescroll>
+                </div>
+            </div>
+        </Split>
+    </div>
+</template>
+
+<script>
+import AcQuos from "./AcQuos.vue"
+import DataView from "./DataView.vue"
+export default {
+    components: {
+        AcQuos, DataView
+    },
+    data() {
+        return {
+            schoolId:'',
+            schoolList:[],
+            tabName: '',
+            split: 0.2,
+            artList: [],
+            artInfo: {},
+            curIndex: 0,
+            quotas: [],
+            tabListShow: [],
+            tabList: [],
+            classList: [],
+            classId: '',
+            subjectId: '',
+            curBarIndex: 0,
+            tabTree: {}
+        }
+    },
+    computed: {
+        quotaFirstLevel() {
+            if (this.quotas.length && this.artInfo?.settings) {
+                let levelMap = {}
+                this.artInfo.settings.forEach(setting => {
+                    let id = setting.path[0]
+                    if (!levelMap[id]) {
+                        levelMap[id] = this.quotas.find(q => q.id === id)?.name
+                    }
+                })
+                let data = []
+                for (const key in levelMap) {
+                    data.push({
+                        id: key,
+                        name: levelMap[key]
+                    })
+                }
+                console.log('***', data)
+                return data
+            }
+            return []
+        },
+        curClass() {
+            if (this.classList && this.classList.length && this.classId) {
+                return this.classList.find(item => item.id == this.classId)
+            }
+            return {}
+        },
+        subjectList() {
+            return this.artInfo?.subjects || []
+        }
+    },
+    methods: {
+        selectBar(tab) {
+            this.curBarIndex = tab
+        },
+        /* 获取当前区级设置数据 */
+        getAreaSetting() {
+            this.$api.areaArt.findArtSetting({
+                areaId: sessionStorage.getItem("areaId")
+            }).then((res) => {
+                if (res.setting) {
+                    this.quotas = res.setting.quotas
+                    this.tabList = this.quotas.map((q) => {
+                        return {
+                            name: q.id,
+                            label: q.name,
+                            children: q.children
+                        }
+                    })
+                    this.findArtList()
+                } else {
+                    this.$Message.warning(this.$t('ae.ae30'))
+                }
+            })
+        },
+        delArt() {
+            this.$Modal.confirm({
+                title: this.$t('ae.ae31'),
+                content: `${this.$t('ae.ae32')}${this.artList[this.curIndex].name}?`,
+                onOk: () => {
+                    let requestData = {
+                        id: this.artList[this.curIndex].id,
+                        code: this.$store.state.userInfo.schoolCode
+                    }
+                    this.$api.areaArt.delArt(requestData).then(
+                        res => {
+                            this.$Message.success(this.$t('ae.ae33'))
+                            let index = this.curIndex
+                            this.artList.splice(index, 1)
+                            this.curIndex = 0
+                        },
+                        err => {
+                            this.$Message.error(this.$t('ae.ae34'))
+                        }
+                    )
+                }
+            })
+        },
+        selectArt(index) {
+            this.classList = []
+            this.classId = ''
+            this.subjectId = ''
+            this.curIndex = index
+            setTimeout(() => {
+                this.findArtSummary()
+            })
+        },
+        toCreate() {
+            this.$router.push({
+                name: "areaCreateArtMgt"
+            })
+        },
+        toDetailPage(index) { },
+        findArtList() {
+            let params = {
+                id: sessionStorage.getItem('areaId')
+            }
+            this.$api.areaArt.findAreaArtList(params).then(
+                (res) => {
+                    this.artList = res.arts
+                    if (this.artList.length) this.findArtSummary()
+                },
+                (err) => { }
+            )
+        },
+        findArtSummary() {
+            // this.classList = []
+            // let params = {
+            //     id: this.artList[this.curIndex].id,
+            //     code: this.$store.state.userInfo.schoolCode
+            // }
+            // this.$api.areaArt.findArtSummary(params).then(
+            //     (res) => {
+            //         this.artInfo = res.art
+            //         this.tabListShow = this.tabList.filter(item => {
+            //             return !!this.artInfo.settings.find(s => s.id.includes(item.name))
+            //         })
+            //         if (this.tabListShow.length) {
+            //             this.tabName = this.tabListShow[0].name
+            //         }
+            //         this.handleTabTree()
+            //         if (this.artInfo?.classes) {
+            //             this.getClassList()
+            //         }
+            //     },
+            //     (err) => { }
+            // )
+        },
+        curSettings(tabName) {
+            if (tabName && this.artInfo?.settings) {
+                let data = this.artInfo.settings.filter(s => {
+                    return s.id.includes(tabName)
+                })
+                return data || []
+            }
+            return []
+        },
+        handleTabTree() {
+            this.tabListShow.forEach(tab => {
+                if (tab.name && this.quotas.length) {
+                    let curQuo = this.quotas.find(q => q.id === tab.name)
+                    let _this = this
+                    const filterNodes = function (nodes) {
+                        nodes = nodes.filter(n => {
+                            let isShow = _this.curSettings(tab.name).find(c => (n.children.length && c.id.includes(n.id)) || (!n.children.length && c.id == n.id))
+                            return !!isShow
+                        })
+                        nodes.forEach(node => {
+                            if (node.children) {
+                                node.children = filterNodes(node.children)
+                            }
+                        })
+                        console.log('---', nodes)
+                        return nodes
+                    }
+                    let res = filterNodes(curQuo.children)
+                    this.$set(this.tabTree, tab.name, res)
+                } else {
+                    this.$set(this.tabTree, tab.name, [])
+                }
+            })
+            console.log(this.tabTree)
+        },
+        getClassList() {
+            let req = {
+                ids: this.artInfo.classes,
+                schoolId: this.$store.state.userInfo.schoolCode
+            }
+            this.$api.common.getGroupListByIds(req).then(
+                res => {
+                    this.classList = res.groups
+                },
+                err => {
+                    this.$Message.error(this.$t('ae.ae35'))
+                }
+            )
+        },
+    },
+    created() {
+        this.getAreaSetting()
+    },
+    watch: {
+        classList: {
+            immediate: true,
+            deep: true,
+            handler(n, o) {
+                if (n && n.length) {
+                    this.classId = n[0].id
+                }
+            }
+        },
+        subjectList: {
+            immediate: true,
+            deep: true,
+            handler(n, o) {
+                if (n && n.length) {
+                    this.subjectId = n[0].id
+                }
+            }
+        }
+    }
+
+}
+</script>
+
+<style lang="less" scoped>
+.art-exam-list-wrap{
+    height: 100%;
+}
+.quo-detail-wrap {
+    background: #f6f6f6;
+    padding: 10px;
+    width: 100%;
+    height: ~"calc(100% - 45px)";
+}
+.art-exam-bar {
+    width: 100%;
+    height: 45px;
+    line-height: 45px;
+    box-shadow: 0 2px 5px #e9e9e9;
+    padding-left: 10px;
+    color: var(--second-text-color);
+    position: relative;
+
+    .art-exam-bar-item {
+        display: inline-block;
+        cursor: pointer;
+        user-select: none;
+        line-height: 38px;
+    }
+
+    .art-exam-bar-active {
+        color: white;
+        border-bottom: 2px solid white;
+    }
+}
+.art-name {
+    font-weight: 500;
+    font-size: 15px;
+    color: #17233d;
+}
+.art-date {
+    margin-top: 5px;
+    color: #808695;
+}
+.art-content {
+    padding-left: 7px;
+    width: 100%;
+    height: 100%;
+}
+.art-item-active {
+    background: var(--hover-text-color);
+}
+.art-item {
+    padding: 15px 15px;
+    cursor: pointer;
+    &:hover {
+        background: var(--hover-text-color);
+    }
+}
+.art-exam-container {
+    width: 100%;
+    height: 100%;
+    background: white;
+}
+.art-mgt-top {
+    height: 45px;
+    box-shadow: 0px 2px 5px #e9e9e9;
+    padding-left: 15px;
+    padding-right: 20px;
+    margin-bottom: 5px;
+    line-height: 45px;
+}
+.to-create-art {
+    float: right;
+    user-select: none;
+    cursor: pointer;
+    color: #40a8f0;
+    margin-left: 10px;
+}
+</style>

+ 277 - 0
TEAMModelOS/ClientApp/src/view/areaArtExam/WorkData.vue

@@ -0,0 +1,277 @@
+<template>
+    <div class="work-data">
+        <Table :height="520" :columns="tableColumn" :data="tableData" border :loading="tableLoading" style="margin-top:10px">
+            <template slot-scope="{ row }" slot="name">
+                <span style="color:#2db7f5;cursor: pointer;" :title="row.id">{{ row.name}}</span>
+            </template>
+            <template slot-scope="{ row }" slot="status">
+                <span :style="{color:row.createTime ? '#19be6b' : 'red' }">{{row.createTime ? $t('td.td43') : $t('ae.ae29')}}</span>
+            </template>
+            <template slot-scope="{ row }" slot="files">
+                <div v-for="(item,index) in row.files" :key="index">
+                    <div class="file-icon">
+                        <img v-if="item.extension == 'PPT' || item.extension == 'PPTX'" src="../../assets/source/ppt.png" />
+                        <img v-else-if="item.extension == 'DOC' || item.extension == 'DOCX'" src="../../assets/source/word.png" />
+                        <img v-else-if="item.extension == 'XLS' || item.extension == 'XLSX' || item.extension == 'CSV'" src="../../assets/source/excel.png" />
+                        <img v-else-if="item.extension == 'PDF'" src="../../assets/source/pdf.png" />
+                        <img v-else-if="item.extension == 'ZIP' || item.extension == 'RAR'" src="../../assets/source/zip.png" />
+                        <img v-else-if="item.type == 'image'" src="../../assets/source/image.png" />
+                        <img v-else-if="item.type == 'video'" src="../../assets/source/video.png" />
+                        <img v-else-if="item.type == 'audio'" src="../../assets/source/audio.png" />
+                        <img v-else-if="item.type == 'res'" src="../../assets/icon/htex.png" />
+                        <img v-else src="../../assets/source/unknow.png" />
+                    </div>
+                    <p class="work-file-name" @click="handlePreviewHw(item)">
+                        {{item.name}}
+                    </p>
+                </div>
+            </template>
+            <template slot-scope="{ row }" slot="createTime">
+                <span>{{row.createTime ? $jsFn.dateFormat(row.createTime) : '-'}}</span>
+            </template>
+        </Table>
+        <!-- 分页 -->
+        <div class="page-wrap">
+            <Page show-total size="small" :current="currentPage" :total="workData.length" :page-size="pageSize" @on-change="pageChange" />
+        </div>
+        <!-- 作业附件预览 -->
+        <Modal footer-hide v-model="hwViewStatus" width="900" @on-visible-change="closeViewModal">
+            <div slot="header">
+                <span>{{$t('train.detail.hwFile')}}</span>
+            </div>
+            <div v-if="hwPreviewFile.type === 'image' || hwPreviewFile.type === 'video' || hwPreviewFile.type === 'audio'" style="margin-top:20px">
+                <video v-if="hwPreviewFile.type == 'video'" id="previewVideo" autoplay :src="hwPreviewFile.url+schoolSas.sas" width="870" controls="controls" style="max-height: 800px;">
+                    {{$t('teachContent.tips8')}}
+                </video>
+                <audio v-else-if="hwPreviewFile.type == 'audio'" controls>
+                    <source :src="hwPreviewFile.url+schoolSas.sas">
+                    {{$t('teachContent.notAudio')}}
+                </audio>
+                <img v-else-if="hwPreviewFile.type == 'image'" :src="hwPreviewFile.url+schoolSas.sas" style="border-radius: 5px;max-height: 800px;max-width:870px;" />
+            </div>
+        </Modal>
+    </div>
+</template>
+
+<script>
+export default {
+    props: {
+        taskInfo: {
+            type: Object,
+            default: () => {
+                return {}
+            }
+        },
+        curClass: {
+            type: Object,
+            default: () => {
+                return {}
+            }
+        },
+        subjectId: {
+            type: String,
+            default: ''
+        }
+    },
+    data() {
+        let _this = this
+        return {
+            isRequesting:false,
+            schoolSas: {},
+            hwPreviewFile: {},
+            hwViewStatus: false,
+            pageSize: 10,
+            currentPage: 1,
+            tableColumn: [
+                {
+                    title: this.$t('learnActivity.score.column1'),
+                    key: "name",
+                    align: "center",
+                    width: 150,
+                },
+                {
+                    title: this.$t('td.td182'),
+                    slot: "status",
+                    align: "center",
+                },
+                {
+                    title: this.$t('cusMgt.rcd.file'),
+                    slot: "files",
+                    align: "center",
+                },
+                {
+                    title: _this.$t('ae.ae36'),
+                    slot: "createTime",
+                    align: "center",
+                }
+            ],
+            tableData: [],
+            workData: [],
+            tableLoading: false
+        }
+    },
+    methods: {
+        closeViewModal(status) {
+            if (!status) {
+                this.hwPreviewFile = {}
+            }
+        },
+        /**
+         * 预览/下载作业文件
+         * @param {string} type video 删除研修视频 file 删除资料
+         */
+        handlePreviewHw(info) {
+            if (info.type === 'image' || info.type === 'video' || info.type === 'audio') {
+                this.hwPreviewFile = info
+                this.hwViewStatus = true
+            } else if (info.type === 'doc') {
+                this.hwPreviewFile = info
+                if (info.extension === 'PDF') {
+                    window.open('/web/viewer.html?file=' + encodeURIComponent(info.url + this.schoolSas.sas))
+                } else {
+                    window.open('https://view.officeapps.live.com/op/view.aspx?src=' + encodeURIComponent(info.url + this.schoolSas.sas))
+                }
+            } else {
+                this.hwPreviewFile = info
+                this.downloadTrainFile(info.url + this.schoolSas.sas, info.name)
+            }
+        },
+        downloadTrainFile(url, name) {
+            const downloadRes = async () => {
+                let response = await fetch(url); // 内容转变成blob地址
+                let blob = await response.blob();  // 创建隐藏的可下载链接
+                let objectUrl = window.URL.createObjectURL(blob);
+                let a = document.createElement('a');
+                a.href = objectUrl;
+                a.download = name;
+                a.click()
+                a.remove()
+            }
+            downloadRes()
+        },
+        // 分页页面变化
+        pageChange(page) {
+            let start = this.pageSize * (page - 1)
+            let end = this.pageSize * page
+            this.currentPage = page
+            this.tableData = this.workData.slice(start, end)
+        },
+        getWorkId() {
+            let task = this.taskInfo?.task?.find(item => item.subject == this.subjectId)
+            if (task) {
+                return task.acId
+            } else {
+                return undefined
+            }
+        },
+        findArtWorkData() {
+            // if(this.isRequesting) return
+            this.isRequesting = true
+            let workId = this.getWorkId()
+            if (!workId || !this.subjectId || !this.curClass.id) return
+            let req = {
+                id: workId,
+                subject: this.subjectId,
+                classId: this.curClass.id
+            }
+            this.$api.areaArt.findArtWork(req).then(
+                res => {
+                    if (res?.works) this.setTableData(res.works)
+                },
+                err => {
+
+                }
+            ).finally(()=>{
+                this.isRequesting = false
+            })
+        },
+        setTableData(data) {
+            this.workData = []
+            if (this.curClass && this.curClass.members) {
+                let students = this._.cloneDeep(this.curClass.members)
+                students.forEach(s => {
+                    let work = data.find(w => w.stuId === s.id)
+                    let hasWork = work && work.attachments?.length
+                    let info = {
+                        id: s.id,
+                        name: s.name,
+                    }
+                    info.status = hasWork ? 1 : 0
+                    info.createTime = hasWork ? work.attachments[0].createTime : 0
+                    info.count = hasWork ? work.attachments.length : 0
+                    info.files = hasWork ? work.attachments : []
+                    this.workData.push(info)
+                })
+            }
+            this.pageChange(1)
+        },
+        // 分页页面变化
+        pageChange(page) {
+            let start = this.pageSize * (page - 1)
+            let end = this.pageSize * page
+            this.currentPage = page
+            this.tableData = this.workData.slice(start, end)
+        },
+    },
+    watch: {
+        subjectId: {
+            immediate: true,
+            handler(n, o) {
+                if (n) {
+                    this.findArtWorkData()
+                }
+            }
+        },
+        curClass: {
+            immediate: true,
+            deep: true,
+            handler(n, o) {
+                if (n && n.id) {
+                    this.findArtWorkData()
+                }
+            }
+        }
+    },
+    created() {
+        this.$tools.getSchoolSas().then(
+            res => {
+                this.schoolSas = res
+            },
+            err => {
+
+            }
+        )
+    }
+}
+</script>
+
+<style lang="less" scoped>
+.page-wrap {
+    margin-top: 15px;
+}
+
+.file-icon {
+    display: inline-block;
+    img {
+        display: inline-block;
+        margin-right: 5px;
+        vertical-align: inherit;
+        border-radius: 2px;
+        width: 15px;
+    }
+}
+.work-data {
+    padding: 10px 10px;
+    border-radius: 6px;
+    background: #F5F7FA;
+}
+</style>
+<style lang="less">
+.work-file-name {
+    display: inline-block;
+}
+.work-file-name:hover {
+    color: #2d8cf0;
+    text-decoration: underline;
+}
+</style>

+ 15 - 1
TEAMModelOS/ClientApp/src/view/areaMgmt/AreaBase.vue

@@ -134,6 +134,20 @@ export default {
         }
       })
     },
+    isJSON(str) {
+      if (typeof str == 'string') {
+        try {
+          var obj = JSON.parse(str);
+          if (typeof obj == 'object' && obj) {
+            return true;
+          } else {
+            return false;
+          }
+        } catch (e) {
+          return false;
+        }
+      }
+    },
     getBodyJson(str) {
       if (!this.isJSON(str)) {
         return false
@@ -149,7 +163,7 @@ export default {
         let host = srvAdr == 'Global' ? this.$store.state.config.Global.coreAPIUrl : this.$store.state
           .config.China.coreAPIUrl
         this.$api.service.getNotification(host, {
-          "sender": [],
+          "senders": ['IES'],
           "receiver": userId
         }).then(res => {
           if (res.length) {

+ 9 - 0
TEAMModelOS/ClientApp/src/view/areaMgmt/AreaLayout.vue

@@ -406,6 +406,15 @@ export default {
               role: '',
               permission: '',
               menuName: 'areaArtSetting',
+            },
+            {
+              icon: 'iconfont icon-test',
+              name: this.$t('area.base.art3'),
+              router: '/area/areaArtMgt',
+              tag: '',
+              role: '',
+              permission: '',
+              menuName: 'areaArtMgt',
             }
           ],
           isShow: this.isShowStudentMenu && this.isShowArtMenu

+ 140 - 140
TEAMModelOS/ClientApp/src/view/art/SchoolArt.vue

@@ -1,70 +1,70 @@
 <template>
-    <div id="artIndex" ref="appRef">
-        <div class="bg">
-            <dv-loading v-if="loading">{{ $t('researchCenter.dashboard.loading') }}</dv-loading>
-            <div v-else class="host-body">
-                <div class="tools">
-                    <span class="time-text">{{ dateYear }} <span style="display: inline-block; margin: 0 5px;color: #0fa2fe;">{{ dateDay }}</span> </span>
-                    <span class="icon iconfont icon-tuichuquanping" style="font-size: 22px;" :title="$t('researchCenter.dashboard.quit')" @click="goBack"></span>
-                </div>
-                <div class="school-info">
-                    <img :src="schoolInfo.schoolLogo">
-                    <span class="school-info-name">{{ schoolInfo.schoolName }}</span>
-                    <span class="school-info-period">{{ schoolInfo.periodName }}</span>
-                    <span class="school-info-semester">{{ schoolInfo.curSemester }}</span>
-                    <span :class="['dashboard-menu',activeMenu === 'music' ? 'dashboard-menu-active' : '']" @click="onChangeSubject('music')">音乐</span>
-                    <span :class="['dashboard-menu',activeMenu === 'paint' ? 'dashboard-menu-active' : '']" @click="onChangeSubject('paint')">美术</span>
-                </div>
-                <div class="d-flex jc-center" style="margin-top:-5px">
-                    <dv-decoration-10 class="dv-dec-10" />
-                    <div class="d-flex jc-center">
-                        <dv-decoration-8 class="dv-dec-8" :color="['#568aea', '#000000']" />
-                        <div class="title">
-                            <span class="title-text">艺术测评看板</span>
-                            <dv-decoration-6 class="dv-dec-6" :reverse="true" :color="['#50e3c2', '#67a1e5']" />
-                        </div>
-                        <dv-decoration-8 class="dv-dec-8" :reverse="true" :color="['#568aea', '#000000']" />
-                    </div>
-                    <dv-decoration-10 class="dv-dec-10-s" />
-                </div>
+  <div id="artIndex" ref="appRef">
+    <div class="bg">
+      <dv-loading v-if="loading">{{ $t('researchCenter.dashboard.loading') }}</dv-loading>
+      <div v-else class="host-body">
+        <div class="tools">
+          <span class="time-text">{{ dateYear }} <span style="display: inline-block; margin: 0 5px;color: #0fa2fe;">{{ dateDay }}</span> </span>
+          <span class="icon iconfont icon-tuichuquanping" style="font-size: 22px;" :title="$t('researchCenter.dashboard.quit')" @click="goBack"></span>
+        </div>
+        <div class="school-info">
+          <img :src="schoolInfo.schoolLogo">
+          <span class="school-info-name">{{ schoolInfo.schoolName }}</span>
+          <span class="school-info-period">{{ schoolInfo.periodName }}</span>
+          <span class="school-info-semester">{{ schoolInfo.curSemester }}</span>
+          <span :class="['dashboard-menu',activeMenu === 'music' ? 'dashboard-menu-active' : '']" @click="onChangeSubject('music')">音乐</span>
+          <span :class="['dashboard-menu',activeMenu === 'paint' ? 'dashboard-menu-active' : '']" @click="onChangeSubject('paint')">美术</span>
+        </div>
+        <div class="d-flex jc-center" style="margin-top:-5px">
+          <dv-decoration-10 class="dv-dec-10" />
+          <div class="d-flex jc-center">
+            <dv-decoration-8 class="dv-dec-8" :color="['#568aea', '#000000']" />
+            <div class="title">
+              <span class="title-text">艺术测评看板</span>
+              <dv-decoration-6 class="dv-dec-6" :reverse="true" :color="['#50e3c2', '#67a1e5']" />
+            </div>
+            <dv-decoration-8 class="dv-dec-8" :reverse="true" :color="['#568aea', '#000000']" />
+          </div>
+          <dv-decoration-10 class="dv-dec-10-s" />
+        </div>
 
-                <div class="body-box">
-                    <div class="left-box">
-                        <div class="left-box-top">
-                            <dv-border-box-12>
-                                <LeftTop />
-                            </dv-border-box-12>
-                        </div>
-                        <div class="left-box-center">
-                            <dv-border-box-12>
-                                <RightTop :school="schoolInfo.schoolName" />
-                            </dv-border-box-12>
-                        </div>
-                    </div>
+        <div class="body-box">
+          <div class="left-box">
+            <div class="left-box-top">
+              <dv-border-box-12>
+                <LeftTop />
+              </dv-border-box-12>
+            </div>
+            <div class="left-box-center">
+              <dv-border-box-12>
+                <RightTop :school="schoolInfo.schoolName" />
+              </dv-border-box-12>
+            </div>
+          </div>
 
-                    <div class="right-box">
-                        <div class="right-box-top">
-                            <dv-border-box-12>
-                                <LeftBottom />
-                            </dv-border-box-12>
-                        </div>
-                        <div class="right-box-bottom">
-                            <div class="right-bottom-single">
-                                <dv-border-box-12>
-                                    <RightBotL />
-                                </dv-border-box-12>
-                            </div>
-                            <div class="right-bottom-single">
-                                <dv-border-box-12>
-                                    <RightBotR />
-                                </dv-border-box-12>
-                            </div>
-                        </div>
-                    </div>
-                </div>
+          <div class="right-box">
+            <div class="right-box-top">
+              <dv-border-box-12>
+                <LeftBottom />
+              </dv-border-box-12>
+            </div>
+            <div class="right-box-bottom">
+              <div class="right-bottom-single">
+                <dv-border-box-12>
+                  <RightBotL />
+                </dv-border-box-12>
+              </div>
+              <div class="right-bottom-single">
+                <dv-border-box-12>
+                  <RightBotR />
+                </dv-border-box-12>
+              </div>
             </div>
+          </div>
         </div>
+      </div>
     </div>
+  </div>
 </template>
 
 <script>
@@ -75,91 +75,91 @@ import LeftCenter from '@/components/dashboard/art/LeftCenter'
 import LeftBottom from '@/components/dashboard/art/LeftBottom'
 import RightTop from '@/components/dashboard/art/RightTop'
 export default {
-    data() {
-        return {
-            schoolInfo: {
-                schoolName: '',
-                schoolLogo: '',
-                periodName: '',
-                curSemester: ''
-            },
-            activeClassIndex: -1,
-            activeGradeIndex: 0,
-            activeMenu: 'music',
-            timing: null,
-            loading: true,
-            dateDay: null,
-            dateYear: null,
-            dateWeek: null,
-        }
-    },
-    components: {
-        RightBotR,
-        RightBotL,
-        LeftTop,
-        LeftCenter,
-        LeftBottom,
-        RightTop,
+  data() {
+    return {
+      schoolInfo: {
+        schoolName: '',
+        schoolLogo: '',
+        periodName: '',
+        curSemester: ''
+      },
+      activeClassIndex: -1,
+      activeGradeIndex: 0,
+      activeMenu: 'music',
+      timing: null,
+      loading: true,
+      dateDay: null,
+      dateYear: null,
+      dateWeek: null,
+    }
+  },
+  components: {
+    RightBotR,
+    RightBotL,
+    LeftTop,
+    LeftCenter,
+    LeftBottom,
+    RightTop,
+  },
+  mounted() {
+    this.$tools.fullScreen(document.getElementById('artIndex'))
+    this.timeFn()
+  },
+  beforeDestroy() {
+    clearInterval(this.timing)
+  },
+  created() {
+    this.defImg = require('@/assets/image/def-school-img.png')
+    this.curPeriod = this.$store.state.user.curPeriod
+    this.$store.state.dashboard.classType = 'all'
+    let routerData = this.$route.params
+    this.schoolInfo.schoolName = routerData && routerData.schoolInfo ? routerData.schoolInfo.name : ''
+    sessionStorage.setItem('artSchool', this.schoolInfo.schoolName)
+    this.onChangeSubject('music')
+    this.cancelLoading()
+    console.log(routerData)
+  },
+  methods: {
+    onChangeClass(classIndex) {
+      this.$store.commit('setRandomArtData', { index: classIndex, subject: this.activeMenu })
     },
-    mounted() {
-        this.$tools.fullScreen(document.getElementById('artIndex'))
-        this.timeFn()
+    onChangeGrade(gradeIndex) {
+      this.activeClassIndex = -1
+      this.onChangeClass(-1)
     },
-    beforeDestroy() {
-        clearInterval(this.timing)
+    onChangeSubject(subject) {
+      this.activeMenu = subject
+      this.$store.commit('setArtDashboardData', { subject, school: this.schoolInfo.schoolName })
+      this.$store.commit('setRandomArtData', 'all')
     },
-    created() {
-        this.defImg = require('@/assets/image/def-school-img.png')
-        this.curPeriod = this.$store.state.user.curPeriod
-        this.$store.state.dashboard.classType = 'all'
-        let routerData = this.$route.params
-        this.schoolInfo.schoolName = routerData && routerData.schoolInfo ? routerData.schoolInfo.name : ''
-        sessionStorage.setItem('artSchool', this.schoolInfo.schoolName)
-        this.onChangeSubject('music')
+    getDashboardData() {
+      let semesterRange = this.$tools.getSemesterTimeRange()
+      this.$api.lessonRecord.getDashboardData({
+        "stime": semesterRange.st,
+        "etime": semesterRange.et,
+        "code": this.$store.state.userInfo.schoolCode,
+        "periodId": this.$store.state.user.curPeriod.id
+      }).then(res => {
+        this.$store.commit('setRearchDashboardData', res)
         this.cancelLoading()
-        console.log(routerData)
+      })
+    },
+    goBack() {
+      this.$tools.exitFullscreen()
+      this.$router.go(-1)
+    },
+    timeFn() {
+      this.timing = setInterval(() => {
+        this.dateDay = this.$tools.formatTime(new Date(), 'hh:mm:ss')
+        this.dateYear = this.$tools.formatTime(new Date(), 'yyyy-MM-dd')
+      }, 1000)
     },
-    methods: {
-        onChangeClass(classIndex) {
-            this.$store.commit('setRandomArtData', { index: classIndex, subject: this.activeMenu })
-        },
-        onChangeGrade(gradeIndex) {
-            this.activeClassIndex = -1
-            this.onChangeClass(-1)
-        },
-        onChangeSubject(subject) {
-            this.activeMenu = subject
-            this.$store.commit('setArtDashboardData', { subject, school: this.schoolInfo.schoolName })
-            this.$store.commit('setRandomArtData', 'all')
-        },
-        getDashboardData() {
-            let semesterRange = this.$tools.getSemesterTimeRange()
-            this.$api.lessonRecord.getDashboardData({
-                "stime": semesterRange.st,
-                "etime": semesterRange.et,
-                "code": this.$store.state.userInfo.schoolCode,
-                "periodId": this.$store.state.user.curPeriod.id
-            }).then(res => {
-                this.$store.commit('setRearchDashboardData', res)
-                this.cancelLoading()
-            })
-        },
-        goBack() {
-            this.$tools.exitFullscreen()
-            this.$router.go(-1)
-        },
-        timeFn() {
-            this.timing = setInterval(() => {
-                this.dateDay = this.$tools.formatTime(new Date(), 'hh:mm:ss')
-                this.dateYear = this.$tools.formatTime(new Date(), 'yyyy-MM-dd')
-            }, 1000)
-        },
-        cancelLoading() {
-            setTimeout(() => {
-                this.loading = false
-            }, 1000)
-        },
+    cancelLoading() {
+      setTimeout(() => {
+        this.loading = false
+      }, 1000)
     },
+  },
 }
 </script>
 <style lang="less" scope>

+ 315 - 131
TEAMModelOS/ClientApp/src/view/artexam/AcQuos.vue

@@ -1,152 +1,336 @@
 <template>
-	<el-tree
-		:data="treeData"
-		:props="defaultProps"
-		class="ac-quo-tree"
-		node-key="id"
-		ref="tree"
-		:render-content="renderContent"
-		:render-after-expand="false"
-		@node-click="nodeClick"
-	></el-tree>
+    <div>
+        <el-tree default-expand-all :data="treeData" :props="defaultProps" class="ac-quo-tree" node-key="id" ref="tree" :render-content="renderContent" :render-after-expand="false" @node-click="nodeClick"></el-tree>
+        <Modal v-model="uploadStatus" fullscreen @on-visible-change="handleHideModal" @on-ok="confirmUpload" :loading="modalLoading">
+            <p slot="header" style="height:fit-content">
+                批量上传
+            </p>
+            <div>
+                <Upload multiple class="batch-upload-box" type="drag" action="" :before-upload="handleBeforeUpload">
+                    <div style="padding: 30px 0;background:#f6f6f6;">
+                        <Icon type="ios-cloud-upload" size="52" style="color: #3399ff"></Icon>
+                        <p style="font-size:18px;margin-top:10px;margin-bottom:10px">
+                            批量上传学生资料<strong style="color:#ed4014">(文件请以学生姓名或者学生id命名)</strong>
+                        </p>
+                        <p>
+                            <Tag color="primary">
+                                {{curClass.name}}
+                            </Tag>
+                            <Tag color="success">
+                                {{subjectId === 'subject_music' ? '音乐' : '美术'}}
+                            </Tag>
+                        </p>
+                    </div>
+                </Upload>
+                <Table :columns="columns" :data="fileData">
+                    <template slot-scope="{ row }" slot="status">
+                        <strong :style="{color:row.studentId ? '#19be6b': '#ed4014'}">{{ row.studentId ? '匹配成功' : '匹配失败'}}</strong>
+                    </template>
+                    <template slot-scope="{ row }" slot="studentInfo">
+                        <strong v-if="row.studentId">{{ `${row.studentName}(${row.studentId})`}}</strong>
+                    </template>
+                </Table>
+            </div>
+        </Modal>
+    </div>
 </template>
-
 <script>
 import ExamData from "./ExamData.vue"
 import WorkData from "./WorkData.vue"
+import BlobTool from '@/utils/blobTool.js'
 export default {
-    components:{
-        ExamData,WorkData
+    components: {
+        ExamData, WorkData
+    },
+    props: {
+        treeData: {
+            type: Array,
+            default: () => {
+                return []
+            }
+        },
+        artInfo: {
+            type: Object,
+            default: () => {
+                return {}
+            }
+        },
+        curClass: {
+            type: Object,
+            default: () => {
+                return {}
+            }
+        },
+        subjectId: {
+            type: String,
+            default: ''
+        }
+    },
+    data() {
+        return {
+            modalLoading: true,
+            uploadStatus: false,
+            defaultProps: {
+                children: "children",
+                label: "name"
+            },
+            activeNode: undefined,
+            columns: [
+                {
+                    title: '文件',
+                    key: 'fileName'
+                },
+                {
+                    title: '状态',
+                    slot: 'status'
+                },
+                {
+                    title: '学生',
+                    slot: 'studentInfo'
+                },
+            ],
+            fileData: []
+        }
     },
-	props: {
-		treeData: {
-			type: Array,
-			default: () => {
-				return []
-			}
-		},
-		artInfo: {
-			type: Object,
-			default: () => {
-				return {}
-			}
-		},
-		classList: {
-			type: Array,
-			default: () => {
-				return []
-			}
-		}
-	},
-	computed:{
-		subjectList(){
-			return this.artInfo?.subjects || []
-		}
-	},
-	data() {
-		return {
-			defaultProps: {
-				children: "children",
-				label: "name"
-			},
-			activeNode: undefined
-		}
-	},
-	methods: {
-        getNodeSetting(id){
-            return this.artInfo?.settings.find(item=>item.id === id)
+    methods: {
+        getDefaultOpenNode(treeData) {
+            if (treeData[0].children.length) {
+                return this.getDefaultOpenNode(treeData[0].children)
+            }else{
+                return treeData[0]
+            }
+        },
+        confirmUpload() {
+            if (!this.fileData.length) return
+            let taskId = this.getTaskId(this.uploadNode.id)
+            if (!taskId) return
+            let schoolSas = {
+                sas: '?' + this.$store.state.user.schoolProfile.blob_sas,
+                url: this.$store.state.user.schoolProfile.blob_uri.slice(0, this.$store.state.user.schoolProfile.blob_uri.lastIndexOf(this.$store.state.userInfo.schoolCode) - 1),
+                name: this.$store.state.userInfo.schoolCode
+            }
+            let blobTool = new BlobTool(schoolSas.url, schoolSas.name, schoolSas.sas, 'school')
+            let promises = []
+            let useInfo = this.fileData.filter(item => !!item.studentId)
+            useInfo.forEach(fileItem => {
+                promises.push(blobTool.upload(fileItem.file, {
+                    path: `art/${this.artInfo.id}/homework/${taskId}/${fileItem.studentId}`
+                }))
+            })
+            Promise.all(promises).then(
+                reses => {
+                    console.log(reses)
+                    let stus = []
+                    reses.forEach((res, index) => {
+                        let stuItem = stus.find(s => s.stuId === useInfo[index].studentId)
+                        if (!stuItem) {
+                            stuItem = {
+                                stuId: useInfo[index].studentId,
+                                userType: useInfo[index].userType,
+                                attachments: []
+                            }
+                        }
+                        stuItem.attachments.push({
+                            name: res.name,
+                            url: res.url,
+                            size: res.size,
+                            createTime: res.createTime,
+                            extension: res.extension,
+                            type: res.type,
+                            blob: res.blob
+                        })
+                        stus.push(stuItem)
+                    })
+                    this.doSaveBatchUpload(stus, taskId)
+                },
+                err => {
+
+                }
+            )
         },
-		nodeClick(node) {
-			if (node.children.length) return
-			this.activeNode = node
-		},
-		hanldeCheckChange() {},
-		renderContent(h, { node, data, store }) {
-			let _this = this
-			// 不是最后一个节点则直接渲染label
-			if (node.childNodes.length) {
-				return h("span", {}, node.label)
-			} else {
-				return h(
-					"div",
-					{
-						style: {
-							width: "calc(100% - 30px)"
-						}
-					},
-					[
-						h("span", node.label),
-						h(
-							"Tag",
-							{
-								class: "type-setting",
-								props: {
-									color: "primary"
-								},
-								style: {
-									display:
-										node.data.type == 1 ||
-										node.data.type == 2
-											? undefined
-											: "none"
-								}
-							},
-							node.data.type == 1
-								? _this.$t('ae.ae0')
-								: node.data.type == 2
-								? _this.$t('ae.ae1')
-								: ""
-						),
-						_this.activeNode?.id == node.data.id &&
-						node.data.type === 1
-							? h(
-									ExamData,
-									{
-										class: ["data-wrap"],
-                                        props:{
-                                            taskInfo:_this.getNodeSetting(node.data.id),
-											subjectList: _this.subjectList,
-											classList: _this.classList
-                                        }
-									}
-							  )
-							: undefined,
-						_this.activeNode?.id == node.data.id &&
-						node.data.type === 2
-							? h(
-									WorkData,
-									{
-										class: ["data-wrap"],
-                                        props:{
-                                            taskInfo:_this.getNodeSetting(node.data.id),
-											subjectList: _this.subjectList,
-											classList: _this.classList
-                                        }
-									}
-							  )
-							: undefined
-					]
-				)
-			}
-		}
-	}
+        doSaveBatchUpload(stus, acId) {
+            let params = {
+                artId: this.artInfo.id,
+                classId: this.curClass.id,
+                quotaId: this.uploadNode.id,
+                acId: acId,
+                subject: this.subjectId,
+                stus
+            }
+            this.$api.areaArt.batchUploadWork(params).then(
+                res => {
+                    if (!res.error) {
+                        this.$Message.success('上传成功')
+                        this.uploadStatus = false
+                        if (this.activeNode.id == this.uploadNode.id) {
+                            let comp = this.$refs[`${this.uploadNode.id}_data`]
+                            if (comp) comp.findArtWorkData()
+                        }
+                    } else {
+                        this.$Message.error('上传失败')
+                    }
+                },
+                err => {
+                    this.$Message.error('上传失败')
+                }
+            )
+        },
+        getTaskId(nodeId) {
+            let setting = this.artInfo?.settings.find(item => item.id === nodeId)
+            if (setting && setting.task) {
+                let task = setting.task.find(item => item.subject === this.subjectId)
+                return task?.acId || undefined
+            }
+            return undefined
+        },
+        handleHideModal(status) {
+            if (!status) this.fileData = []
+        },
+        handleBeforeUpload(file) {
+            let fileItem = {
+                file,
+                fileName: file.name,
+            }
+            if (this.curClass && this.curClass.members) {
+                let n = file.name.substring(0, file.name.lastIndexOf('.'))
+                let studentInfo = this.curClass.members.find(item => item.id == n || item.name === n)
+                if (studentInfo) {
+                    fileItem.studentName = studentInfo.name
+                    fileItem.studentId = studentInfo.id
+                    fileItem.userType = studentInfo.type
+                }
+                console.log(n, studentInfo)
+            }
+            let index = this.fileData.findIndex(item => item.fileName === file.name)
+            if (index >= 0) {
+                this.fileData.splice(index, 1, fileItem)
+            } else {
+                this.fileData.push(fileItem)
+            }
+            return false
+        },
+        getNodeSetting(id) {
+            return this.artInfo?.settings.find(item => item.id === id)
+        },
+        nodeClick(node) {
+            if (node.children.length) return
+            this.activeNode = node
+        },
+        hanldeCheckChange() { },
+        renderContent(h, { node, data, store }) {
+            let _this = this
+            // 不是最后一个节点则直接渲染label
+            if (node.childNodes.length) {
+                return h("span", {}, node.label)
+            } else {
+                return h(
+                    "div",
+                    {
+                        style: {
+                            width: "calc(100% - 30px)"
+                        }
+                    },
+                    [
+                        h("span", node.label),
+                        h(
+                            "Tag",
+                            {
+                                class: "type-setting",
+                                props: {
+                                    color: "default"
+                                },
+                                style: {
+                                    display:
+                                        node.data.type == 1 ||
+                                            node.data.type == 2
+                                            ? undefined
+                                            : "none"
+                                }
+                            },
+                            node.data.type == 1
+                                ? _this.$t('ae.ae0')
+                                : node.data.type == 2
+                                    ? _this.$t('ae.ae1')
+                                    : ""
+                        ),
+                        node.data.type === 2 ? h("Tag",
+                            {
+                                class: "type-setting",
+                                props: {
+                                    color: "primary",
+                                },
+                                nativeOn: {
+                                    click: (evt) => {
+                                        _this.uploadStatus = true
+                                        _this.uploadNode = node.data
+                                    }
+                                }
+                            },
+                            '批量导入') : undefined,
+                        _this.activeNode?.id == node.data.id &&
+                            node.data.type === 1
+                            ? h(
+                                ExamData,
+                                {
+                                    class: ["data-wrap"],
+                                    props: {
+                                        taskInfo: _this.getNodeSetting(node.data.id),
+                                        curClass: _this.curClass,
+                                        subjectId: _this.subjectId
+                                    }
+                                }
+                            )
+                            : undefined,
+                        _this.activeNode?.id == node.data.id &&
+                            node.data.type === 2
+                            ? _this.$createElement(
+                                WorkData,
+                                {
+                                    class: ["data-wrap"],
+                                    ref: node.data.id + '_data',
+                                    props: {
+                                        taskInfo: _this.getNodeSetting(node.data.id),
+                                        curClass: _this.curClass,
+                                        subjectId: _this.subjectId,
+                                    }
+                                }
+                            )
+                            : undefined
+                    ]
+                )
+            }
+        }
+    },
+    mounted() {
+        console.log('*****', this.treeData)
+        if (this.treeData && this.treeData.length) {
+            this.activeNode = this.getDefaultOpenNode(this.treeData)
+			console.log(this.activeNode)
+        }
+    }
 }
 </script>
 
 <style lang="less" scoped>
+.batch-upload-box {
+    // width: 90%;
+    margin: auto;
+    margin-top: 10px;
+}
 </style>
 <style lang="less">
 .ac-quo-tree .el-tree-node__content {
-	height: fit-content;
-	align-items: baseline;
+    height: fit-content;
+    align-items: baseline;
+    padding: 8px 0px;
 }
 .data-wrap {
-	min-height: 300px;
-	background: #f0f0f0;
-	margin: 10px 0px;
-	width: calc(100% - 10px);
+    min-height: 300px;
+    background: #f5f7fa;
+    margin: 10px 0px;
+    width: calc(100% - 10px);
 }
 .type-setting {
-	margin-left: 10px;
+    margin-left: 10px;
 }
 </style>

Plik diff jest za duży
+ 796 - 838
TEAMModelOS/ClientApp/src/view/artexam/Create.vue


+ 0 - 0
TEAMModelOS/ClientApp/src/view/artexam/DataView.vue


Niektóre pliki nie zostały wyświetlone z powodu dużej ilości zmienionych plików