Ver Fonte

Merge branch 'TPE/develop5.0' into TPE/develop5.0_local

jeff há 3 anos atrás
pai
commit
bff263c9f8
68 ficheiros alterados com 742 adições e 2865 exclusões
  1. 1 0
      TEAMModeBI/ClientApp/package.json
  2. 1 1
      TEAMModeBI/ClientApp/src/api/index.js
  3. 252 55
      TEAMModeBI/ClientApp/src/components/AbilityTree.vue
  4. 38 10
      TEAMModeBI/ClientApp/src/until/blobTool.js
  5. 1 1
      TEAMModeBI/ClientApp/src/view/bindPhone.vue
  6. 0 2
      TEAMModeBI/ClientApp/src/view/common/aside.vue
  7. 2 3
      TEAMModeBI/ClientApp/src/view/created/created.vue
  8. 4 2
      TEAMModeBI/ClientApp/src/view/teachermanage/areamanage.vue
  9. 3 0
      TEAMModeBI/ClientApp/src/view/teachermanage/manage.vue
  10. 4 1
      TEAMModeBI/ClientApp/src/view/teachermanage/school.vue
  11. 9 4
      TEAMModeBI/ClientApp/src/view/teachermanage/traitmanage.vue
  12. 5 3
      TEAMModeBI/Controllers/BIAbility/AbilityTaskMgmtController.cs
  13. 3 4
      TEAMModeBI/Controllers/BIHome/HomeStatisController.cs
  14. 7 1
      TEAMModeBI/Controllers/LoginController.cs
  15. 22 0
      TEAMModelOS.FunctionV4/local.settings.json
  16. 7 3
      TEAMModelOS.SDK/DI/DingDing/DingDing.cs
  17. 2 2
      TEAMModelOS.SDK/Models/Cosmos/Research/TeacherFile.cs
  18. 11 3
      TEAMModelOS.SDK/Models/Cosmos/Research/TeacherTrain.cs
  19. 4 0
      TEAMModelOS.SDK/Models/Cosmos/School/Debate.cs
  20. 3 1
      TEAMModelOS.SDK/Models/Service/ActivityService.cs
  21. 28 3
      TEAMModelOS.SDK/Models/Service/StatisticsService.cs
  22. 2 21
      TEAMModelOS/ClientApp/package.json
  23. 4 7
      TEAMModelOS/ClientApp/public/index.html
  24. 0 1
      TEAMModelOS/ClientApp/public/pdfBuild/pdf.js.map
  25. 0 313
      TEAMModelOS/ClientApp/public/pdfBuild/pdf.sandbox.js
  26. 0 1
      TEAMModelOS/ClientApp/public/pdfBuild/pdf.sandbox.js.map
  27. 0 1
      TEAMModelOS/ClientApp/public/pdfBuild/pdf.worker.js.map
  28. 0 1
      TEAMModelOS/ClientApp/src/api/http.js
  29. 18 13
      TEAMModelOS/ClientApp/src/api/stuAccount.js
  30. 3 3
      TEAMModelOS/ClientApp/src/common/UploadModal.vue
  31. 3 1
      TEAMModelOS/ClientApp/src/locale/lang/en-US/schoolBaseInfo.js
  32. 1 1
      TEAMModelOS/ClientApp/src/locale/lang/en-US/settings.js
  33. 3 1
      TEAMModelOS/ClientApp/src/locale/lang/zh-CN/schoolBaseInfo.js
  34. 1 1
      TEAMModelOS/ClientApp/src/locale/lang/zh-CN/settings.js
  35. 3 1
      TEAMModelOS/ClientApp/src/locale/lang/zh-TW/schoolBaseInfo.js
  36. 1 1
      TEAMModelOS/ClientApp/src/locale/lang/zh-TW/settings.js
  37. 0 86
      TEAMModelOS/ClientApp/src/static/course.json
  38. 0 22
      TEAMModelOS/ClientApp/src/static/diagnosis.json
  39. 0 443
      TEAMModelOS/ClientApp/src/static/policy.json
  40. 0 410
      TEAMModelOS/ClientApp/src/static/video.json
  41. 3 1
      TEAMModelOS/ClientApp/src/utils/blobTool.js
  42. 30 27
      TEAMModelOS/ClientApp/src/view/Home.vue
  43. 1 1
      TEAMModelOS/ClientApp/src/view/areaMgmt/AreaLayout.less
  44. 1 1
      TEAMModelOS/ClientApp/src/view/evaluation/index/CreateExercises.less
  45. 6 1
      TEAMModelOS/ClientApp/src/view/evaluation/index/CreateExercises.vue
  46. 3 1
      TEAMModelOS/ClientApp/src/view/homepage/NewHomePage.vue
  47. 5 5
      TEAMModelOS/ClientApp/src/view/jyzx/HomePage.vue
  48. 3 1
      TEAMModelOS/ClientApp/src/view/login/Index.vue
  49. 1 1
      TEAMModelOS/ClientApp/src/view/questionnaire/ManageQuestionnaire.less
  50. 1 1
      TEAMModelOS/ClientApp/src/view/research-center/BaseVideo.vue
  51. 20 3
      TEAMModelOS/ClientApp/src/view/research-center/ResearchMgt.vue
  52. 0 147
      TEAMModelOS/ClientApp/src/view/resource/DiagDetail.vue
  53. 0 143
      TEAMModelOS/ClientApp/src/view/resource/Diagnosis.vue
  54. 0 256
      TEAMModelOS/ClientApp/src/view/resource/Leadership.vue
  55. 0 229
      TEAMModelOS/ClientApp/src/view/resource/LeadershipDetail.vue
  56. 0 172
      TEAMModelOS/ClientApp/src/view/resource/Literacy.vue
  57. 0 162
      TEAMModelOS/ClientApp/src/view/resource/LiteracyDetail.vue
  58. 0 1
      TEAMModelOS/ClientApp/src/view/resource/Policy.vue
  59. 0 177
      TEAMModelOS/ClientApp/src/view/resource/Probation.vue
  60. 38 18
      TEAMModelOS/ClientApp/src/view/student-account/class/ClassMgt.vue
  61. 2 2
      TEAMModelOS/ClientApp/src/view/user/UserCenter.vue
  62. 46 2
      TEAMModelOS/ClientApp/vue.config.js
  63. 12 0
      TEAMModelOS/Controllers/Common/LessonRecordController.cs
  64. 24 15
      TEAMModelOS/Controllers/Research/AbilitySubController.cs
  65. 83 43
      TEAMModelOS/Controllers/School/ClassController.cs
  66. 10 24
      TEAMModelOS/Controllers/School/StudentController.cs
  67. 4 1
      TEAMModelOS/Controllers/School/TmdUserController.cs
  68. 3 3
      TEAMModelOS/TEAMModelOS.csproj

+ 1 - 0
TEAMModeBI/ClientApp/package.json

@@ -8,6 +8,7 @@
         "lint": "vue-cli-service lint"
     },
     "dependencies": {
+        "@azure/storage-blob": "^12.8.0",
         "@element-plus/icons": "0.0.11",
         "@lywzx/vue.access.control": "^1.0.10",
         "axios": "^0.20.0-0",

+ 1 - 1
TEAMModeBI/ClientApp/src/api/index.js

@@ -149,7 +149,7 @@ export default {
     addChild(data) {
         return post('/biabilitytask/upd-abilitytask', data)
     },
-    //tree删除子节点
+    //tree删除父节点(一级@章节)
     removeChildnodes(data) {
         return post('/biabilitytask/del-abilitytask', data)
     },

+ 252 - 55
TEAMModeBI/ClientApp/src/components/AbilityTree.vue

@@ -14,6 +14,9 @@
         <div class="right-box">
             <div class="resource">
                 <div class="resource-title">关联资源</div>
+                <div class="changebtn" v-show="uploadList.length !==0" @click="changes">
+                    <el-button type="primary" size="small ">存储变更</el-button>
+                </div>
                 <div class="resourceadd-box">
                     <el-dropdown>
                         <span class="el-dropdown-link">
@@ -31,7 +34,7 @@
                 </div>
             </div>
             <div class="resourcebox" v-if="datais">
-                <div class="resource-item" v-for="(item,index) in curNode" :key="item.id" @click=details(item)>
+                <div class="resource-item" v-for="(item,index) in curNode" :key="item.id">
                     <img src="@/assets/icon/image.png" v-if="item.type === 'image'" />
                     <img src="@/assets/icon/word.png" v-else-if="item.type === 'doc' && docType.includes(getSuffix(item.title))" />
                     <img src="@/assets/icon/excel.png" v-else-if="item.type === 'doc' && excelType.includes(getSuffix(item.title))" />
@@ -45,13 +48,13 @@
                     <img src="@/assets/icon/zip.png" v-else-if="item.type === 'res'" />
                     <img src="@/assets/icon/image.png" v-else-if="item.type === 'thum'" />
                     <img src="@/assets/icon/unknow.png" v-else="item.type === 'other'" />
-                    <span class="title-name">{{index+1}}、{{item.title}}</span>
+                    <span class="title-name" @click=details(item)>{{index+1}}、{{item.title}}</span>
                     <div class="resource-tools">
                         <!-- <div class="node-resource-tool" >
 									<Icon type="md-eye" />
 									<span>{{ $t('syllabus.preview') }}</span>
 								</div> -->
-                        <div class="resource-tool" @click="onDeleteResource(item,index)">
+                        <div class="resource-tool" @click="deleteResource(item,index)">
                             <i class="el-icon-delete-solid"></i>
                             <span>删除</span>
                         </div>
@@ -95,7 +98,7 @@
                 </div>
             </el-upload>
             <div class="upload-submitbtn">
-                <el-button type="primary" @click="confirmUpload">确认上传</el-button>
+                <el-button type="primary" @click="confirmUpload(),uploadBtn=true" :loading="uploadBtn">确认上传</el-button>
             </div>
         </el-dialog>
     </div>
@@ -103,8 +106,9 @@
 </template>
 <script>
 import { ref, reactive, onMounted, getCurrentInstance, watch, nextTick } from 'vue'
-import { ElMessageBox } from 'element-plus'
+import { ElMessage, ElMessageBox } from 'element-plus'
 import { useStore } from 'vuex'
+import BlobTool from '@/until/blobTool.js'
 export default {
     props: {
         tree: {
@@ -126,6 +130,9 @@ export default {
             type: Array,
             default: null,
         },
+        areaId: {
+            default: null,
+        },
     },
     setup(props, context) {
         let curNode = ref(null)
@@ -150,44 +157,25 @@ export default {
             url: '',
             type: '',
         })
-        let nowPitchCb = ref({})
+        let nowPitchCb = ref()
         let datais = ref(false)
         let uploadFiles = ref(false)
         let fileList = ref({
             id: '',
             title: '',
         })
+        let uploadFileData = ref({})
+        let uploadBtn = ref(false)
+        let uploadList = ref([])
+        let changeShow = ref(false)
+        let BlobContent = ref({})
         onMounted(() => {
             resourceData = resourceData.push(...props.tree)
             console.log(props.tree, '树状')
         })
-        // let tree = reactive([{
-        //   label: '1.第一章',
-        //   children: [
-        //     {
-        //       label: '1.1 第一节',
-        //       children: [
-        //         {
-        //           label: '1.1.1 哈哈哈哈哈',
-        //         },
-        //       ],
-        //     },
-        //   ],
-        // }, {
-        //   label: '2.第二章',
-        //   children: [
-        //     {
-        //       label: '2.1 第一节',
-        //       children: [
-        //         {
-        //           label: '2.1.1 详细信息显示',
-        //         },
-        //       ],
-        //     },
-        //   ],
-        // }])
-
+        //添加子节点
         function append(data) {
+            console.log(data, '传进方法的内容')
             console.log(props, '获取到的')
             let datas = data
             let user = JSON.parse(localStorage.getItem('userData'))
@@ -239,51 +227,93 @@ export default {
                             idInfo = props.datas[x].id
                         }
                     }
-                    let abilitiesInfo = []
                     let dataInfo = { abilityId: abilityIds, auth: [], codeval: codeVals, scope: scopes, id: idInfo, trees: chidData }
                     let datas = { tmdId: user.tmdId, tmdName: user.tmdName, standard: props.stand, abilityTask: [dataInfo] }
                     proxy.$api.addChild(datas).then((res) => {
                         console.log(res, '增加子节点的返回')
+                        res.state === 200 ? ElMessage.success('节点添加成功') : ''
                     })
                     console.log(datas, '参数')
                 })
                 .catch(() => {})
         }
+        //删除子节点
         function remove(node, data) {
+            console.log(props, '获取现在的树状')
+            console.log(nowPitchCb.value, '当前的数据')
             let user = JSON.parse(localStorage.getItem('userData'))
             console.log(props.stand, '删除查看当前的')
             console.log(node, data)
-            ElMessageBox.confirm('确定要删除节点吗?', '注意', {
+            ElMessageBox.confirm('确定要删除节点吗?', '温馨提示', {
                 confirmButtonText: '确认',
                 cancelButtonText: '取消',
                 type: 'warning',
-            })
-                .then(() => {
-                    let datai = { tmdId: user.tmdId, tmdName: user.tmdName, standard: props.stand, id: data.id }
-                    proxy.$api.removeChildnodes(datai).then((res) => {
-                        console.log(res, '删除返回')
+            }).then(() => {
+                let receiveData = data
+                const result = props.tree.every((item) => {
+                    return item.pid === receiveData.pid
+                })
+                console.log(result, '得到的结果')
+                if (result) {
+                    deleteChapter(props.stand, receiveData.id, node)
+                } else {
+                    let dataEvalue = nowPitchCb.value
+                    let user = JSON.parse(localStorage.getItem('userData'))
+                    let chidData = []
+                    //寻找最上级
+                    for (let i in props.tree) {
+                        let dataOne = props.tree[i]
+                        if (dataEvalue.id === dataOne.id) {
+                            chidData.push(dataOne)
+                        } else if (dataEvalue.id !== dataOne.id && dataOne.children.length !== 0) {
+                            for (let y in dataOne.children) {
+                                if (dataEvalue.id === dataOne.children[y].id) {
+                                    chidData.push(dataOne)
+                                } else if (dataEvalue.id !== dataOne.children[y].id && dataOne.children[y].children.length !== 0) {
+                                    for (let u in dataOne.children[y].children) {
+                                        dataEvalue.id === dataOne.children[y].children[u].id ? chidData.push(dataOne) : ''
+                                    }
+                                }
+                            }
+                        }
+                    }
+                    //准备API参数
+                    let codeVals = ''
+                    let abilityIds = ''
+                    let scopes = ''
+                    let idInfo = ''
+                    for (let x in props.datas) {
+                        if (chidData[0].id === props.datas[x].id) {
+                            codeVals = props.datas[x].codeval
+                            abilityIds = props.datas[x].abilityId
+                            scopes = props.datas[x].scope
+                            idInfo = props.datas[x].id
+                        }
+                    }
+                    let dataInfo = { abilityId: abilityIds, auth: [], codeval: codeVals, scope: scopes, id: idInfo, trees: chidData }
+                    let datas = { tmdId: user.tmdId, tmdName: user.tmdName, standard: props.stand, abilityTask: [dataInfo] }
+                    proxy.$api.addChild(datas).then((res) => {
+                        console.log(res, '删除子节点的返回')
+                        res.state === 200 ? ElMessage.success('节点删除成功') : ElMessage.error('节点删除失败')
                     })
                     const parent = node.parent
                     const children = parent.data.children || parent.data
                     const index = children.findIndex((d) => d.id === data.id)
                     children.splice(index, 1)
-                })
-                .catch(() => {
-                    ElMessage({
-                        type: 'info',
-                        message: 'Delete canceled',
-                    })
-                })
+                }
+            })
         }
-        //
         function handleNodeClick(data) {
             console.log(data, '当前点击的元素')
             curNode.value = data.rnodes
+            console.log(curNode.value, '资源数据')
             fileList.value.id = data.id
             fileList.value.title = data.title
+            nowPitchCb.value = data
             data.rnodes.length ? (datais.value = true) : (datais.value = false)
             // console.log(curNode.value.title)
         }
+        //渲染tree
         function renderContent(h, { node, data }) {
             return h(
                 'span',
@@ -330,11 +360,13 @@ export default {
         //查看资源详情
         function details(val) {
             // detailsInfo = val
+            console.log(val, '查看详情传进来的值')
             console.log(detailsInfo, '赋值过后')
             detailsInfo.value = {}
             let bolbs = JSON.parse(localStorage.getItem('blobInfo'))
             let host = getBlobHost(bolbs.osblob_uri)
-            let urls = host + '/' + val.cntr + val.link + '?' + bolbs.osblob_sas
+            let space = bolbs.osblob_uri.slice(bolbs.osblob_uri.indexOf('cn') + 3)
+            let urls = host + '/' + space + val.link + '?' + bolbs.osblob_sas
             if (getSuffix(val.title.toLowerCase()) == 'pdf') {
                 window.open('https://www.teammodel.cn/web/viewer.html?file=' + encodeURIComponent(urls))
             } else if (val.type === 'doc') {
@@ -350,20 +382,148 @@ export default {
         //上传资源
         function uploadResource(val) {
             console.log(val, '触发了')
-            fileList.value.push({ name: val.raw.name, size: val.raw.size, type: val.raw.type })
+            uploadFileData.value = val.raw
+            // fileList.value.push({ name: val.raw.name, size: val.raw.size, type: val.raw.type })
             // fileList.value.push(val)
             // return true
         }
         //确认上传
         function confirmUpload() {
             let urlHost = JSON.parse(localStorage.getItem('blobInfo'))
+            let urlheader = urlHost.osblob_uri.substring(0, urlHost.osblob_uri.indexOf('cn') + 2)
+            let space = urlHost.osblob_uri.slice(urlHost.osblob_uri.indexOf('cn') + 3)
             let standard = props.stand
-            let nowid = curNode.value
+            let nowid = fileList.value.id
             console.log(nowid)
-            let url = urlHost.osblob_uri + '/yxpt/' + standard + '/jyzx/' + nowid + '/' + fileList.value[0].name + '?/' + urlHost.osblob_sas
+            let url = urlHost.osblob_uri + '/yxpt/' + standard + '/jyzx/' + nowid + '/' + uploadFileData.value.name + '?/' + urlHost.osblob_sas
             console.log(url, '上传地址')
+            let Blobs = new BlobTool(urlheader, space, '?' + urlHost.osblob_sas, 'school')
+            BlobContent.value = Blobs
+            let path = 'yxpt/' + standard + '/jyzx/' + nowid
+            Blobs.upload(uploadFileData.value, path, {}, false, props.areaId).then((res) => {
+                console.log(res, '上传成功的返回')
+                res.url ? ElMessage.success('上传成功') : ElMessage.error('上传失败')
+                uploadList.value.push(res)
+                let datas = curNode.value
+                datas.push({ title: res.name, link: res.blob, size: res.size, type: res.type, id: guid(), cntr: 'teammodelos' })
+                curNode.value = datas
+                uploadBtn.value = false
+                uploadFiles.value = false
+                console.log(props, '后面的数据')
+            })
         }
-        //添加当前父节点
+        //存储变更
+        function changes() {
+            let data = nowPitchCb.value
+            let user = JSON.parse(localStorage.getItem('userData'))
+            console.log(data, '变更数据')
+            let chidData = []
+            //寻找最上级
+            for (let i in props.tree) {
+                let dataOne = props.tree[i]
+                if (data.id === dataOne.id) {
+                    chidData.push(dataOne)
+                } else if (data.id !== dataOne.id && dataOne.children.length !== 0) {
+                    for (let y in dataOne.children) {
+                        if (data.id === dataOne.children[y].id) {
+                            chidData.push(dataOne)
+                        } else if (data.id !== dataOne.children[y].id && dataOne.children[y].children.length !== 0) {
+                            for (let u in dataOne.children[y].children) {
+                                data.id === dataOne.children[y].children[u].id ? chidData.push(dataOne) : ''
+                            }
+                        }
+                    }
+                }
+            }
+            //准备API参数
+            let codeVals = ''
+            let abilityIds = ''
+            let scopes = ''
+            let idInfo = ''
+            for (let x in props.datas) {
+                if (chidData[0].id === props.datas[x].id) {
+                    console.log(props.datas[x].id, '进入的值')
+                    codeVals = props.datas[x].codeval
+                    abilityIds = props.datas[x].abilityId
+                    scopes = props.datas[x].scope
+                    idInfo = props.datas[x].id
+                }
+            }
+            let dataInfo = { abilityId: abilityIds, auth: [], codeval: codeVals, scope: scopes, id: idInfo, trees: chidData }
+            let datas = { tmdId: user.tmdId, tmdName: user.tmdName, standard: props.stand, abilityTask: [dataInfo] }
+            proxy.$api.addChild(datas).then((res) => {
+                console.log(res, '存储变更的返回')
+                res.state === 200 ? (ElMessage.success('储存变更成功'), (uploadList.value = [])) : ElMessage.error('储存变更失败')
+            })
+        }
+        //删除资源
+        function deleteResource(val, index) {
+            let urlHost = JSON.parse(localStorage.getItem('blobInfo'))
+            let urlheader = urlHost.osblob_uri.substring(0, urlHost.osblob_uri.indexOf('cn') + 2)
+            let space = urlHost.osblob_uri.slice(urlHost.osblob_uri.indexOf('cn') + 3)
+            console.log(val, index, '删除的信息')
+            ElMessageBox.confirm('确定要移除该资源吗?', '温馨提示', {
+                confirmButtonText: '确定',
+                cancelButtonText: '取消',
+                type: 'warning',
+            })
+                .then(() => {
+                    console.log(BlobContent.value, 'BLOB的信息')
+                    let user = JSON.parse(localStorage.getItem('userData'))
+                    let blobs = {}
+                    BlobContent.value.containerClient ? (blobs = BlobContent.value) : ((blobs = new BlobTool(urlheader, space, '?' + urlHost.osblob_sas, 'school')), (BlobContent.value = blobs))
+                    blobs.deleteBlob(val.link).then((res) => {
+                        console.log(res, '删除的返回')
+                        if (res === 200) {
+                            curNode.value.splice(index, 1)
+                            let data = nowPitchCb.value
+                            console.log(props, '现在的数据')
+                            console.log(data, '现在选中的')
+                            let chidData = []
+                            for (let i in props.tree) {
+                                let dataOne = props.tree[i]
+                                if (data.id === dataOne.id) {
+                                    chidData.push(dataOne)
+                                } else if (data.id !== dataOne.id && dataOne.children.length !== 0) {
+                                    for (let y in dataOne.children) {
+                                        if (data.id === dataOne.children[y].id) {
+                                            chidData.push(dataOne)
+                                        } else if (data.id !== dataOne.children[y].id && dataOne.children[y].children.length !== 0) {
+                                            for (let u in dataOne.children[y].children) {
+                                                data.id === dataOne.children[y].children[u].id ? chidData.push(dataOne) : ''
+                                            }
+                                        }
+                                    }
+                                }
+                            }
+                            //准备API参数
+                            let codeVals = ''
+                            let abilityIds = ''
+                            let scopes = ''
+                            let idInfo = ''
+                            for (let x in props.datas) {
+                                if (chidData[0].id === props.datas[x].id) {
+                                    console.log(props.datas[x].id, '进入的值')
+                                    codeVals = props.datas[x].codeval
+                                    abilityIds = props.datas[x].abilityId
+                                    scopes = props.datas[x].scope
+                                    idInfo = props.datas[x].id
+                                }
+                            }
+                            let dataInfo = { abilityId: abilityIds, auth: [], codeval: codeVals, scope: scopes, id: idInfo, trees: chidData }
+                            let datas = { tmdId: user.tmdId, tmdName: user.tmdName, standard: props.stand, abilityTask: [dataInfo] }
+                            proxy.$api.addChild(datas).then((res) => {
+                                console.log(res, '删除的API返回')
+                                res.state === 200 ? ElMessage.success('删除成功') : ElMessage.error('删除失败')
+                            })
+                        } else {
+                            ElMessage.warning('删除失败')
+                        }
+                    })
+                })
+                .catch(() => {})
+        }
+        //添加当前父节点(tree一级@章节)
         function addFirstnode() {
             let user = JSON.parse(localStorage.getItem('userData'))
             ElMessageBox.prompt('输入新的章节节点名称', '新增节点', {
@@ -385,6 +545,23 @@ export default {
                 console.log(datas, '参数')
             })
         }
+        //删除父节点(tree一级@章节)
+        function deleteChapter(standard, chapterId, node) {
+            let user = JSON.parse(localStorage.getItem('userData'))
+            let data = { standard: standard, id: chapterId, tmdId: user.tmdId, tmdName: user.tmdName }
+            proxy.$api.removeChildnodes(data).then((res) => {
+                console.log(res, '删除父节点的返回')
+                if (res.state === 200) {
+                    ElMessage.success('节点删除成功')
+                    const parent = node.parent
+                    const children = parent.data.children || parent.data
+                    const index = children.findIndex((d) => d.id === data.id)
+                    children.splice(index, 1)
+                } else {
+                    ElMessage.error('节点删除失败')
+                }
+            })
+        }
         //处理HOST返回截取
         function getBlobHost(url) {
             let s = url || store.state.user.userProfile.blob_uri || store.state.user.studentProfile.blob_uri
@@ -403,8 +580,12 @@ export default {
             props,
             (nweProps) => {
                 nextTick(() => {
-                    treeRef.value.setCurrentKey(nweProps.defaultinfo)
-                    curNode.value = nweProps.tree[0].rnodes
+                    console.log(fileList.value.id, '触发了监听')
+                    if (!fileList.value.id) {
+                        treeRef.value.setCurrentKey(nweProps.defaultinfo)
+                        fileList.value.id = nweProps.defaultinfo
+                        curNode.value = nweProps.tree[0].rnodes
+                    }
                 })
             },
             { immediate: true, deep: true }
@@ -435,6 +616,13 @@ export default {
             fileList,
             confirmUpload,
             addFirstnode,
+            uploadFileData,
+            uploadBtn,
+            uploadList,
+            nowPitchCb,
+            changes,
+            deleteResource,
+            deleteChapter,
         }
     },
 }
@@ -525,7 +713,7 @@ export default {
     z-index: 999;
     line-height: 55px;
     text-align: center;
-    margin-left: 20%;
+    margin-left: 5%;
     border: 1px dashed #ccc;
     border-radius: 5px;
     padding: 5px;
@@ -665,4 +853,13 @@ export default {
 .addzy:hover {
     cursor: pointer;
 }
+.changebtn {
+    width: 100px;
+    height: 40px;
+    line-height: 40px;
+}
+.changebtn button {
+    width: 100%;
+    height: 40px;
+}
 </style>

+ 38 - 10
TEAMModeBI/ClientApp/src/until/blobTool.js

@@ -1,16 +1,44 @@
-import { GLOBAL } from '@/static/Global.js';
-import JsFn from '@/utils/js-fn.js';
-import Tools from '@/utils/public.js';
-import API from '@/api/index.js';
+// import API from '@/api/index.js';
 const blobPath = ['avatar', 'audio', 'doc', 'exam', 'image', 'item', 'notice', 'other', 'paper', 'syllabus', 'res', 'student', 'survey', 'temp', 'thum', 'video', 'vote', 'jyzx', 'train', 'yxpt', 'homework', 'policy']
 const { BlobServiceClient, BlobClient } = require("@azure/storage-blob")
-    //获取文件后缀和类型
+const CONTENT_TYPES = {
+        'image': ['JPG', 'JPEG', 'BMP', 'TIF', 'PNG', 'GIF', 'SVG'],
+        'video': ['RMVB', 'WMV', 'ASF', 'AVI', '3GP', 'MPG', 'MKV', 'MP4', 'DVD', 'OGM', 'MOV', 'MPEG2', 'MPEG4'],
+        'doc': ['PPT', 'PPTX', 'DOC', 'DOCX', 'PDF', 'XLS', 'XLSX', 'CSV'],
+        'res': ['HTE', 'HTEX'],
+        'audio': ['MP3', 'OGG', 'WAV', 'APE', 'CDA', 'AU', 'MIDI', 'MAC', 'AAC']
+    }
+    // 获取某个字符在字符串中第num次出现的index
+function findChartIndex(str, cha, num) {
+    var x = str.indexOf(cha);
+    for (var i = 0; i < num; i++) {
+        x = str.indexOf(cha, x + 1);
+    }
+    return x;
+}
+/* 获取文件的MD5 */
+function getFileMD5(file) {
+    return new Promise((r, j) => {
+        const CHUNK_SIZE = 4194304; // 大文件获取数前4M大小
+        const fileReader = new FileReader();
+        const chunkFile = file.slice(0, CHUNK_SIZE);
+        fileReader.readAsBinaryString(chunkFile);
+        let spark = new SparkMD5();
+        console.time('getMd5')
+        fileReader.onload = e => {
+            spark.appendBinary(e.target.result);
+            var md5 = spark.end(true);
+            r(this.stringToUint8Array(md5))
+        };
+    })
+}
+//获取文件后缀和类型
 function getExAndType(fileName) {
     let ex = fileName.substring(fileName.lastIndexOf('.') + 1)
     let type = 'other'
     ex = ex.toUpperCase()
-    for (let key in GLOBAL.CONTENT_TYPES) {
-        if (GLOBAL.CONTENT_TYPES[key].indexOf(ex) != -1) {
+    for (let key in CONTENT_TYPES) {
+        if (CONTENT_TYPES[key].indexOf(ex) != -1) {
             type = key
             break
         }
@@ -159,7 +187,7 @@ export default class BlobTool {
                     res => {
                         //设置blob MD5 (解决大文件分块上传没有MD5的问题)
                         if (!res.contentMD5) {
-                            Tools.getFileMD5(file).then(
+                            getFileMD5(file).then(
                                 res => {
                                     console.log('前端获取的MD5:', res)
                                     let option = {
@@ -209,8 +237,8 @@ export default class BlobTool {
             fileList.forEach((item, index) => {
                 if (item.url.indexOf('/res/') > 0 && item.url.indexOf('.HTE') < 0) {
                     let fileItem = {}
-                    let startIndex = JsFn.findChartIndex(item.blob, '/', 1)
-                    let endIndex = JsFn.findChartIndex(item.blob, '/', 2)
+                    let startIndex = findChartIndex(item.blob, '/', 1)
+                    let endIndex = findChartIndex(item.blob, '/', 2)
                     let name = item.blob.substring(startIndex + 1, endIndex)
                     let nameIndex = names.indexOf(name)
                     if (nameIndex == -1) {

+ 1 - 1
TEAMModeBI/ClientApp/src/view/bindPhone.vue

@@ -1,5 +1,5 @@
 <template>
-    <el-dialog v-model="centerDialogVisible" title="验证手机号" width="30%" center>
+    <el-dialog v-model="centerDialogVisible" title="验证手机号" width="30%" center :close-on-click-modal="false" :close-on-press-escape="false">
         <div class="bindbox">
             <div class="bind-phone">
                 <el-row :gutter="20">

+ 0 - 2
TEAMModeBI/ClientApp/src/view/common/aside.vue

@@ -237,6 +237,4 @@ body > .el-container {
     overflow: hidden;
     margin-right: 5px;
 }
-.asidebox:hover {
-}
 </style>

+ 2 - 3
TEAMModeBI/ClientApp/src/view/created/created.vue

@@ -139,9 +139,9 @@
                     <el-form-item label="学校名称:" class="school-name">
                         <el-input v-model="item.name" placeholder="学校名称"></el-input>
                     </el-form-item>
-                    <el-form-item label="学校编码:" class="school-code">
+                    <!-- <el-form-item label="学校编码:" class="school-code">
                         <el-input v-model="item.code" maxlength="6" placeholder='6位字符简码'></el-input>
-                    </el-form-item>
+                    </el-form-item> -->
                     <el-form-item label="学段选择:">
                         <el-checkbox-group v-model="item.radio1">
                             <el-checkbox label="小学" border></el-checkbox>
@@ -460,7 +460,6 @@ export default {
             let users = JSON.parse(localStorage.getItem('userData'))
             for (let i in schoolForm.value) {
                 datas.push({
-                    id: schoolForm.value[i].code,
                     name: schoolForm.value[i].name,
                     admin: schoolForm.value[i].presupposeAdmin,
                     period: schoolForm.value[i].radio1,

+ 4 - 2
TEAMModeBI/ClientApp/src/view/teachermanage/areamanage.vue

@@ -1,5 +1,5 @@
 <template>
-    <div>
+    <div class="areamanabox">
         <div class="nowtitle">
             <div class="boxselect" @click="createArea" v-if="PowerShow">
                 <a href="#">
@@ -200,7 +200,6 @@ export default {
                 areaId: currentlySelect.value.id,
                 schoolCode: [schoolcode],
             }
-            console.log(data)
             proxy.$api.areaAddSchool(data).then((res) => {
                 console.log(res, '区域加入学校返回')
                 res.state === 200 ? (ElMessage.success('学校加入成功'), getNotjoin(), getAreaschool()) : ElMessage.error('学校添加失败')
@@ -593,6 +592,9 @@ export default {
 .details-list-school .el-image {
     margin-top: 25%;
 }
+.areamanabox .el-table__header-wrapper {
+    line-height: 60px;
+}
 </style>
 
 

+ 3 - 0
TEAMModeBI/ClientApp/src/view/teachermanage/manage.vue

@@ -350,4 +350,7 @@ export default {
     padding: 8px 12px;
     line-height: 20px;
 }
+.manage-table .el-table__header-wrapper {
+    line-height: 60px;
+}
 </style>

+ 4 - 1
TEAMModeBI/ClientApp/src/view/teachermanage/school.vue

@@ -66,7 +66,7 @@
                     </template>
                 </el-table-column>
                 <!-- <el-table-column prop="state" label="状态" width="110" align="center" /> -->
-                <el-table-column fixed="right" label="操作" width="120" align="center" v-if="PowerShow">
+                <el-table-column label="操作" width="200" align="center" v-if="PowerShow">
                     <template #default="scope">
                         <el-button type="text" size="small" @click.prevent="deleteRow(scope.$index, scope.row)">编辑</el-button>
                     </template>
@@ -761,4 +761,7 @@ export default {
 .school-form-area .el-select {
     width: 100%;
 }
+.school-list .el-table__header-wrapper {
+    line-height: 60px;
+}
 </style>

+ 9 - 4
TEAMModeBI/ClientApp/src/view/teachermanage/traitmanage.vue

@@ -83,7 +83,7 @@
             </div>
         </div>
         <div class="tree-box">
-            <AbilityTree :tree="treeDatas" :datas="treeDataInfo" :defaultinfo="defaultPitch" :stand="nowPitchAbility.standard" :abilityId="nowPitchAbility.id"></AbilityTree>
+            <AbilityTree :tree="treeDatas" :datas="treeDataInfo" :defaultinfo="defaultPitch" :stand="nowPitchAbility.standard" :abilityId="nowPitchAbility.id" :areaId="areaIds"></AbilityTree>
         </div>
     </div>
     <!--选中方案的内容end-->
@@ -439,6 +439,8 @@ export default {
         let submitValue = ref([])
         let redactSaveloading = ref(false)
         let idBtnLoading = ref(false)
+        //区域ID 上传资源使用
+        let areaIds = ref('')
         //维度
         const dimensionInfo = ref([
             {
@@ -463,6 +465,7 @@ export default {
         //获取到方案下的能力点,并默认选中第一个
         function pitch(data) {
             console.log(data, '查询册别传入的data')
+            areaIds.value = data.id
             nowPitch = Object.assign({}, data)
             console.log(nowPitch, data, '查询册别')
             let dataSA = data
@@ -500,7 +503,7 @@ export default {
                 nowPitchAbility.id = res.abilities[0].id
                 nowPitchAbility.standard = res.abilities[0].standard
                 console.log(originalAbilityData.value, '原始数据')
-                addClass(1, res.abilities[0])
+                addClass(0, res.abilities[0])
             })
         }
         //给元素添加样式和当前选中的能力点
@@ -536,9 +539,7 @@ export default {
                 for (let y in outermostData) {
                     treeDatas.value.push(outermostData[y])
                 }
-                console.log(outermostData, '查看数据')
                 treeDataInfo.value.push(resultD)
-                console.log(treeDataInfo.value, '未改装数据')
             }
             defaultPitch.value = treeDatas.value[0].id
             console.log(treeDatas.value[0].id, treeDatas.value, '处理后的数据')
@@ -759,6 +760,7 @@ export default {
             addtaskInfo,
             redactTaskInfo,
             taskModel,
+            areaIds,
         }
     },
 }
@@ -1250,4 +1252,7 @@ export default {
 .redactTask .el-form-item {
     margin-bottom: 10px;
 }
+.allbox .el-table__header-wrapper {
+    line-height: 60px;
+}
 </style>

+ 5 - 3
TEAMModeBI/Controllers/BIAbility/AbilityTaskMgmtController.cs

@@ -256,12 +256,14 @@ namespace TEAMModeBI.Controllers.BIAbility
 
                 operateLog.operateDescribe = $"{_tmdName}【{_tmdId}】已操作删除册别,删除状态:{response.Status},删除ID:{id}";
                 await _azureStorage.Save<OperateLog>(operateLog);//保存操作记录
-
-                return Ok(new { state = response.Status });
+                if (response.Status == 204)
+                    return Ok(new { state = 200 });
+                else
+                    return Ok(new { state = response.Status });
             }
             catch (Exception ex)
             {
-                await _dingDing.SendBotMsg($"BI,{_option.Location}   /biabilitytask/delabilitytask    \n  {ex.Message} {ex.StackTrace}", GroupNames.成都开发測試群組);
+                await _dingDing.SendBotMsg($"BI,{_option.Location}   /biabilitytask/del-abilitytask    \n  {ex.Message} {ex.StackTrace}", GroupNames.成都开发測試群組);
                 return Ok(new { state = 404 });
             }
         }

+ 3 - 4
TEAMModeBI/Controllers/BIHome/HomeStatisController.cs

@@ -103,7 +103,6 @@ namespace TEAMModeBI.Controllers.BIHome
                     }
                 }
 
-
                 //查询教师的大小和教师集合信息
                 await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "Teacher").GetItemQueryStreamIterator(queryText: $"select sum(c.size) as size from c", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("Base") }))
                 {
@@ -1017,7 +1016,7 @@ namespace TEAMModeBI.Controllers.BIHome
         }
 
         /// <summary>
-        /// 统计空间类型
+        /// 统计学校空间类型
         /// </summary>
         /// <returns></returns>
         [HttpPost("get-datatypestics")]
@@ -1083,7 +1082,7 @@ namespace TEAMModeBI.Controllers.BIHome
                     }
                 }
 
-                return Ok(new { schoolSize, sizeS, typeStics });
+                return Ok(new { state = 200, schoolSize, sizeS, typeStics });
             }
             catch (Exception ex)
             {
@@ -1093,7 +1092,7 @@ namespace TEAMModeBI.Controllers.BIHome
         }
 
         /// <summary>
-        /// 统计教师的空间
+        /// 统计教师的空间类型
         /// </summary>
         /// <returns></returns>
         [HttpPost("get-teachetypestics")]

+ 7 - 1
TEAMModeBI/Controllers/LoginController.cs

@@ -961,6 +961,8 @@ namespace TEAMModeBI.Controllers
                                 itemUser.picture = item.GetProperty("picture").ToString();
                                 itemUser.mail = item.GetProperty("mail").ToString();
 
+                                operateLog.operateType = "修改";
+                                operateLog.funModule = "钉钉绑定";
                                 operateLog.tmdId = item.GetProperty("id").ToString();
                                 operateLog.tmdName = item.GetProperty("name").ToString();
                                 operateLog.operateDescribe = $"{tmdName}【{tmdId}】醍摩豆账号和{itemUser.name}【{itemUser.RowKey}】钉钉账户绑定成功";
@@ -1047,7 +1049,11 @@ namespace TEAMModeBI.Controllers
                                     itemUser.tmdMobile = item.GetProperty("mobile").ToString();
                                     itemUser.picture = item.GetProperty("picture").ToString();
                                     itemUser.mail = item.GetProperty("mail").ToString();
+                                    roles = !string.IsNullOrEmpty($"{itemUser.roles}") ? new List<string>(itemUser.roles.Split(",")) : new List<string>();
+                                    permissions = !string.IsNullOrEmpty($"{itemUser.permissions}") ? new List<string>(itemUser.permissions.Split(",")) : new List<string>();
 
+                                    operateLog.operateType = "修改";
+                                    operateLog.funModule = "钉钉绑定";
                                     operateLog.tmdId = item.GetProperty("id").ToString();
                                     operateLog.tmdName = item.GetProperty("name").ToString();
                                     operateLog.operateDescribe = $"{tmdName}【{tmdId}】醍摩豆账号和{itemUser.name}【{itemUser.RowKey}】钉钉账户绑定成功";
@@ -1074,7 +1080,7 @@ namespace TEAMModeBI.Controllers
                 await _dingDing.SendBotMsg($"BI,{_option.Location}  /common/login/get-ddinfo   \n  {ex.Message}{ex.StackTrace}", GroupNames.成都开发測試群組);
                 return BadRequest();
             }
-        }
+        }        
 
         public record DingDingbinds
         {

+ 22 - 0
TEAMModelOS.FunctionV4/local.settings.json

@@ -0,0 +1,22 @@
+{
+  "IsEncrypted": false,
+  "Values": {
+    "AzureWebJobsStorage": "DefaultEndpointsProtocol=https;AccountName=teammodellog;AccountKey=lxVDrgs+6rKtmASL3k1WrarrEd5Rk42wS1Mu5+sqQlPya1JLSlFDtnZUvMPeHHe7zlESfn/1NY7CZdGviy2UCw==;EndpointSuffix=core.chinacloudapi.cn",
+    "Azure:Storage:ConnectionString": "DefaultEndpointsProtocol=https;AccountName=teammodelstorage;AccountKey=Yq7D4dE6cFuer2d2UZIccTA/i0c3sJ/6ITc8tNOyW+K5f+/lWw9GCos3Mxhj47PyWQgDL8YbVD63B9XcGtrMxQ==;EndpointSuffix=core.chinacloudapi.cn",
+    "Azure:ServiceBus:ConnectionString": "Endpoint=sb://teammodelos.servicebus.chinacloudapi.cn/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=Sy4h4EQ8zP+7w/lOLi1X3tGord/7ShFHimHs1vC50Dc=",
+    //"Azure:Cosmos:ConnectionString": "AccountEndpoint=https://teammodelos.documents.azure.cn:443/;AccountKey=clF73GwPECfP1lKZTCvs8gLMMyCZig1HODFbhDUsarsAURO7TcOjVz6ZFfPqr1HzYrfjCXpMuVD5TlEG5bFGGg==;",
+    //"Azure:Redis:ConnectionString": "CoreRedisCN.redis.cache.chinacloudapi.cn:6380,password=LyJWP1ORJdv+poXWofAF97lhCEQPg1wXWqvtzXGXQuE=,ssl=True,abortConnect=False",
+    //"Azure:ServiceBus:ActiveTask": "active-task",
+    //"Azure:ServiceBus:NoticeTask": "notice-task",
+    "Azure:Cosmos:ConnectionString": "AccountEndpoint=https://cdhabookdep-free.documents.azure.cn:443/;AccountKey=JTUVk92Gjsx17L0xqxn0X4wX2thDPMKiw4daeTyV1HzPb6JmBeHdtFY1MF1jdctW1ofgzqkDMFOtcqS46by31A==;",
+    "Azure:Redis:ConnectionString": "106.12.23.251:6379,password=habook,ssl=false,abortConnect=False,writeBuffer=10240",
+    "Azure:ServiceBus:ActiveTask": "dep-active-task",
+    "Azure:ServiceBus:NoticeTask": "dep-notice-task",
+    "Azure:ServiceBus:ItemCondQueue": "dep-itemcond",
+    "Option:Location": "China-Dep",
+    "FUNCTIONS_WORKER_RUNTIME": "dotnet",
+    "HaBookAuth:CoreService:sendnotification": "https://api2.teammodel.net/service/sendnotification",
+    "HaBookAuth:CoreService:clientID": "c7317f88-7cea-4e48-ac57-a16071f7b884",
+    "HaBookAuth:CoreService:clientSecret": "kguxh:V.PLmxBdaI@jnrTrDSth]A3346"
+  }
+}

+ 7 - 3
TEAMModelOS.SDK/DI/DingDing/DingDing.cs

@@ -24,7 +24,7 @@ namespace TEAMModelOS.SDK.DI
             _httpClient = httpClient;
         }
 
-        // <summary>
+        /// <summary>
         /// 發送需要加簽驗證的Bot訊息(msgtype為text)
         /// </summary>
         /// <param name="robotUrl">釘釘Robot發送Url</param>
@@ -34,7 +34,11 @@ namespace TEAMModelOS.SDK.DI
         public async Task SendBotMsg(string msg, GroupNames groupkey)
         {
             var content = new { msgtype = "text", text = new { content = msg } };
+#if DEBUG
+            var keys = GroupNames.成都开发測試群組.GetDescriptionText().Split(',');
+#else
             var keys = groupkey.GetDescriptionText().Split(',');
+#endif
             if (keys.Length == 1) await _httpClient.PostAsJsonAsync($"{url}{keys[0]}", content);
             else
             {
@@ -64,7 +68,7 @@ namespace TEAMModelOS.SDK.DI
 
         }
 
-        #region private
+#region private
         /// <summary>
         /// 釘釘Bot簽名生成方法
         /// </summary>
@@ -83,7 +87,7 @@ namespace TEAMModelOS.SDK.DI
                 return System.Web.HttpUtility.UrlEncode(Convert.ToBase64String(hashmessage), Encoding.UTF8);
             }
         }
-        #endregion
+#endregion
     }
 
     public enum GroupNames

+ 2 - 2
TEAMModelOS.SDK/Models/Cosmos/Research/TeacherFile.cs

@@ -30,7 +30,7 @@ namespace TEAMModelOS.SDK.Models
         /// <summary>
         /// 观看时长
         /// </summary>
-        public long view { get; set; }
+        public double view { get; set; }
         /// <summary>
         /// video ,doc,
         /// </summary>
@@ -45,7 +45,7 @@ namespace TEAMModelOS.SDK.Models
     public  class TeacherFileRcd {
         public string hash { get; set; }
         public double duration { get; set; }
-        public long view { get; set; }
+        public double view { get; set; }
         public bool  done { get; set; }
         public string abilityId { get; set; }
         public string taskId { get; set; }

+ 11 - 3
TEAMModelOS.SDK/Models/Cosmos/Research/TeacherTrain.cs

@@ -151,7 +151,7 @@ namespace TEAMModelOS.SDK.Models
         /// <summary>
         /// 线上观看视频的学时
         /// </summary>
-        public long videoTime { get; set; }
+        public double videoTime { get; set; }
         /// <summary>
         /// 认证材料学时
         /// </summary>
@@ -186,11 +186,11 @@ namespace TEAMModelOS.SDK.Models
         /// <summary>
         /// 线上观看视频的学时
         /// </summary>
-        public long onlineTime { get; set; }
+        public double onlineTime { get; set; }
         /// <summary>
         /// 线上观看视频的时长
         /// </summary>
-        public long videoTime { get; set; }
+        public double videoTime { get; set; }
         /// <summary>
         /// 限制学习时长
         /// </summary>
@@ -247,6 +247,14 @@ namespace TEAMModelOS.SDK.Models
         /// 互评记录
         /// </summary>
         public List<TeacherHprecord> hprecord { get; set; } = new List<TeacherHprecord>();
+        /// <summary>
+        /// -1 未评论任何人。
+        /// </summary>
+        public int debateOrther { get; set; } = -1;
+        /// <summary>
+        /// 评论别人的id集合
+        /// </summary>
+        public List<string> replyIds { get; set; } = new List<string>();
     }
     /// <summary>
     /// 线下研修记录

+ 4 - 0
TEAMModelOS.SDK/Models/Cosmos/School/Debate.cs

@@ -37,6 +37,10 @@ namespace TEAMModelOS.SDK.Models
         /// 评论/话题的统一id,用于被关联
         /// </summary>
         public string comid { get; set; }
+        ///// <summary>
+        ///// 统一id集合。
+        ///// </summary>
+        //public string unionid { get; set; }
         /// <summary>
         /// 点赞数
         /// </summary>

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

@@ -66,7 +66,7 @@ namespace TEAMModelOS.SDK
                 foreach (MQActivity activity in datas)
                 {
                     //已经完结的不再允许加入,还未开始的。
-                    if (string.IsNullOrEmpty(activity.progress)|| activity.progress.Equals("finish") || activity.progress.Equals("pending"))
+                    if (string.IsNullOrEmpty(activity.progress) || activity.progress.Equals("finish") || activity.progress.Equals("pending"))
                     {
                         continue;
                     }
@@ -186,6 +186,8 @@ namespace TEAMModelOS.SDK
                         }
                     }
                 }
+            } catch (CosmosException e) {
+                await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}-GroupListService-FixActivity\n{e.Message}\n{e.StackTrace}CosmosException{e.Status}", GroupNames.成都开发測試群組);
             }
             catch (Exception ex)
             {

+ 28 - 3
TEAMModelOS.SDK/Models/Service/StatisticsService.cs

@@ -753,17 +753,31 @@ namespace TEAMModelOS.SDK
            
             }
             await foreach (var item in client.GetContainer("TEAMModelOS", "Normal")
-                .GetItemQueryIterator<Ability>(queryText: $"select c.id,c.name,c.currency,c.no,c.dimension,c.hour,c.stds,c.abilityCount from c {insql} ", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Ability-{area.standard}") }))
+                .GetItemQueryIterator<Ability>(queryText: $"select c.comid, c.id,c.name,c.currency,c.no,c.dimension,c.hour,c.stds,c.abilityCount from c {insql} ", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Ability-{area.standard}") }))
             {
                 abilities.Add(item);
 
             }
+            List<Debate> debates= new List<Debate>();
+            if (abilities.IsNotEmpty()) {
+                await foreach (var item in client.GetContainer("TEAMModelOS", "School")
+                .GetItemQueryIterator<Debate>(queryText: $"select distinct value(c) from c join b in  c.replies  where b.tmdid='{_tmdid}'and  c.source='uploadscore' and c.comid in ({string.Join(",", abilities.Select(o => $"'{o.comid}'"))})",
+                requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Debate-{_school}") }))
+                {
+                    debates.Add(item);
+                }
+            }
             Currency currency= new Currency();
             Currency currencyAll = new Currency();
+
+
             abilitySubs.ForEach(item => {
                 int currencyInt = item.from == 1 ? 1 : 0;
                 Ability ability = abilities.Find(x=>x.id.Equals(item.id));
                 if (ability != null) {
+
+                  
+
                     if (ability != null)
                     {
                         currencyInt = item.from == 0 ? ability.currency : 1;
@@ -804,8 +818,19 @@ namespace TEAMModelOS.SDK
                     }
 
                     List<TeacherHprecord> hprecords = new List<TeacherHprecord>();
+                    List<Debate> debateOrthers = debates.FindAll(x => x.comid.Equals(ability.comid));
+                    int debateOrther = -1;
+                    List<string> replyIds = new List<string>();
+                    if (debateOrthers.IsNotEmpty()) {
+                        debateOrther = debateOrthers.Count;
+                        var replies= debateOrthers.SelectMany(x => x.replies).Where(z => z.tmdid.Equals(_tmdid));
+                        if (replies != null && replies.Count() > 0) {
+                            replyIds = replies.Select(x => x.id).ToList();
+                        }
+                    }
                     TeacherAbility teacherAbility = new Models.TeacherAbility
-                    {
+                    { replyIds = replyIds,
+                        debateOrther = debateOrther,
                         id = ability.id,
                         currency = currencyInt,
                         no = ability.no,
@@ -818,7 +843,7 @@ namespace TEAMModelOS.SDK
 
                     if (file != null)
                     {
-                        long view = 0;
+                        double view = 0;
                         file.fileRecords.ForEach(record => {
                             var abilityVideo = record.files.FindAll(x => x.abilityId.Equals(item.id));
                             if (abilityVideo.IsNotEmpty())

+ 2 - 21
TEAMModelOS/ClientApp/package.json

@@ -16,16 +16,13 @@
   "dependencies": {
     "@azure/abort-controller": "^1.0.4",
     "@azure/storage-blob": "^12.8.0",
-    "@azure/storage-file-datalake": "^12.2.0",
     "@babel/plugin-proposal-optional-chaining": "^7.16.5",
-    "@ckeditor/ckeditor5-build-inline": "^12.3.1",
     "@jiaminghi/data-view": "^2.10.0",
     "@lywzx/vue.access.control": "^1.0.10",
     "@vue/eslint-config-standard": "^4.0.0",
     "animate.css": "^3.7.2",
     "apexcharts": "^3.26.1",
     "axios": "^0.21.4",
-    "bcryptjs": "^2.4.3",
     "core-js": "^3.1.2",
     "d3": "^5.9.2",
     "dr-vue-echarts": "^1.0.13",
@@ -34,8 +31,6 @@
     "element-ui": "^2.12.0",
     "fabric": "^4.5.1",
     "file-saver": "^2.0.2",
-    "firebase": "^7.19.0",
-    "firestore": "^1.1.6",
     "hevue-img-preview": "^5.0.1",
     "html2canvas": "^1.0.0-rc.7",
     "html2pdf.js": "^0.10.1",
@@ -53,17 +48,11 @@
     "less": "^3.9.0",
     "lodash": "^4.17.21",
     "mockjs": "^1.0.1-beta3",
-    "mux.js": "^5.11.0",
     "node-fetch": "^2.6.1",
     "oidc-client": "^1.9.1",
-    "print-js": "^1.6.0",
     "qrcodejs2": "0.0.2",
     "snapsvg": "^0.5.1",
     "spark-md5": "^3.0.2",
-    "svg-sprite-loader": "^5.0.0",
-    "unsplash-js": "^6.0.0",
-    "v-calendar": "^1.0.8",
-    "v-distpicker": "^1.2.12",
     "video.js": "^7.17.0",
     "videojs-contrib-hls": "^5.15.0",
     "videojs-contrib-hls.js": "^3.2.0",
@@ -72,17 +61,10 @@
     "vue": "^2.6.10",
     "vue-apexcharts": "^1.6.0",
     "vue-audio-native": "^0.1.41",
-    "vue-axios": "^2.1.5",
-    "vue-ckeditor5": "^0.4.1",
     "vue-clipboard2": "^0.3.1",
     "vue-count-to": "^1.0.13",
-    "vue-echarts": "^5.0.0-beta.0",
-    "vue-happy-scroll": "^2.1.0",
-    "vue-img-viewr": "^1.0.6",
-    "vue-infinite-loading": "^2.4.4",
     "vue-lazyload": "^1.3.3",
     "vue-loading-overlay": "^3.3.3",
-    "vue-msal": "^2.0.0",
     "vue-pdf": "^4.2.0",
     "vue-router": "^3.0.6",
     "vue-scroll": "^2.1.12",
@@ -146,11 +128,9 @@
     "font-awesome": "^4.7.0",
     "less-loader": "^5.0.0",
     "mini-css-extract-plugin": "^0.5.0",
-    "node-sass": "^4.14.1",
     "optimize-css-assets-webpack-plugin": "^5.0.3",
     "postcss": "^7.0.36",
     "postcss-loader": "^3.0.0",
-    "sass-loader": "^7.1.0",
     "style-loader": "^0.23.1",
     "style-resources-loader": "^1.3.3",
     "svg-sprite-loader": "^5.0.0",
@@ -158,7 +138,8 @@
     "vue-cli-plugin-style-resources-loader": "^0.1.4",
     "vue-i18n": "^8.12.0",
     "vue-loader": "^15.7.1",
-    "vue-template-compiler": "^2.6.10"
+    "vue-template-compiler": "^2.6.10",
+    "webpack-bundle-analyzer": "^4.5.0"
   },
   "postcss": {
     "plugins": {

+ 4 - 7
TEAMModelOS/ClientApp/public/index.html

@@ -7,12 +7,9 @@
 		<link rel="icon" href="<%= BASE_URL %>favicon.ico">
 		<link id="theme" type="text/css" rel="stylesheet" href="<%= BASE_URL %>theme/dark-theme.css">
 		<title></title>
-		<script>
+		<!-- <script>
 		MathJax = {
 		  loader: {load: ["input/tex", "output/svg"]},
-		  // mml:{
-			 //  forceReparse:true
-		  // },
 		  tex: {
 		    inlineMath: [['$', '$'], ['\\(', '\\)']],
 			displayMath: [["$$", "$$"], ["\\[", "\\]"]]
@@ -22,10 +19,10 @@
 			},
             skipTags: ["script", "noscript", "style", "textarea", "pre", "code", "a"] //避开某些标签
 		};
-		</script>
-		<script type="text/javascript" id="MathJax-script" async
+		</script> -->
+		<!-- <script type="text/javascript" id="MathJax-script" async
 		  src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-svg.js">
-		</script>
+		</script> -->
 		<script>
 			let cloudSetting = localStorage.getItem('cloudSetting')
 			if (cloudSetting) {

Diff do ficheiro suprimidas por serem muito extensas
+ 0 - 1
TEAMModelOS/ClientApp/public/pdfBuild/pdf.js.map


Diff do ficheiro suprimidas por serem muito extensas
+ 0 - 313
TEAMModelOS/ClientApp/public/pdfBuild/pdf.sandbox.js


Diff do ficheiro suprimidas por serem muito extensas
+ 0 - 1
TEAMModelOS/ClientApp/public/pdfBuild/pdf.sandbox.js.map


Diff do ficheiro suprimidas por serem muito extensas
+ 0 - 1
TEAMModelOS/ClientApp/public/pdfBuild/pdf.worker.js.map


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

@@ -134,7 +134,6 @@ axios.interceptors.response.use(
             localStorage.clear()
             window.location.href = window.location.origin + '/login'
             Message.error(app.$t('http.error401'))
-            alert(401)
         } else if (error.response.status === 500) {
             Message.error(app.$t('http.error500'))
         } else if (error.response.status === 404) {

+ 18 - 13
TEAMModelOS/ClientApp/src/api/stuAccount.js

@@ -1,7 +1,7 @@
 import { fetch, post } from '@/api/http'
 
 export default {
-  saveStudent: function(schoolId, data) {
+  saveStudent: function (schoolId, data) {
     let format = {
       grant_type: 'create',
       schoolId: schoolId,
@@ -11,7 +11,7 @@ export default {
     return post('/student/student-manage', format)
   },
   //查询
-  findStudent: function(schoolId) {
+  findStudent: function (schoolId) {
     let format = {
       grant_type: 'read',
       schoolId: schoolId
@@ -19,17 +19,22 @@ export default {
     return post('/student/student-manage', format)
   },
   //更新学生信息
-  updStudent: function(schoolId, data) {
+  updStudent: function (schoolId, data) {
     let format = {
       grant_type: 'update',
       schoolId: schoolId,
       students: []
     }
+    data.forEach(item => {
+      if (item.pw == '******') {
+        delete item.pw
+      }
+    })
     format.students.push(...data)
     return post('/student/student-manage', format)
   },
   //设置学生头像
-  setStudentAvatar: function(schoolId, data) {
+  setStudentAvatar: function (schoolId, data) {
     let format = {
       grant_type: 'avatar',
       schoolId: schoolId,
@@ -42,7 +47,7 @@ export default {
    * 刪除學生
    * @param {any} data
    */
-  deleteStudent: function(schoolId, data) {
+  deleteStudent: function (schoolId, data) {
     let format = {
       grant_type: 'delete',
       schoolId: schoolId,
@@ -52,7 +57,7 @@ export default {
     return post('/student/student-manage', format)
   },
   // 匯入
-  ImportStudent: function(schoolId, data) {
+  ImportStudent: function (schoolId, data) {
     let format = {
       grant_type: 'import',
       schoolId: schoolId,
@@ -61,36 +66,36 @@ export default {
     format.students.push(...data)
     return post('/student/student-manage', format)
   },
-  updateStudent: function(data) {
+  updateStudent: function (data) {
     return post('/api/Student/upsert', data)
   },
-  updateAllStudent: function(data) {
+  updateAllStudent: function (data) {
     return post('/api/Student/upsertAll', data)
   },
   //取得AClassOne數目API
-  getSchoolAclassOne: function(data) {
+  getSchoolAclassOne: function (data) {
     return post('/school/init/get-school-aclassone', { school_code: data })
   },
   //設定固定AClassOne學生ID API
-  setSchoolAclassOneState: function(data, studentIds) {
+  setSchoolAclassOneState: function (data, studentIds) {
     return post('/school/init/set-school-aclasson-sta', {
       school_code: data,
       student_ids: studentIds //回傳預設定學生帳號的陣列,ex:["ID0001","ID0002","ID0003"]
     })
   },
   //回收AClassOne學生ID API
-  recallSchoolAclassOne: function(data) {
+  recallSchoolAclassOne: function (data) {
     //※mode: 非必須,sta:回收固定  dync:回收動態
     return post('/school/init/recall-school-aclasson', {
       school_code: data
     })
   },
   //从班级移除学生
-  removeStudent: function(data){
+  removeStudent: function (data) {
     return post('/student/student-manage', data)
   },
   //班级添加学生
-  updateStudent: function(data){
+  updateStudent: function (data) {
     return post('/student/student-manage', data)
   }
 }

+ 3 - 3
TEAMModelOS/ClientApp/src/common/UploadModal.vue

@@ -52,10 +52,10 @@
                             <Icon class="is-parse-htex" size="12" custom="iconfont icon-convert" />
                             {{$t('updModal.resolve')}}
                         </span> -->
-                        <!-- pptx是否转换 -->
-                        <Checkbox class="upload-info-wrap parse-label" v-if="item.extension == 'PPTX'" v-model="item.isParse">
+                        <!-- pptx是否转换 暂时隐藏此功能-->
+                        <!-- <Checkbox class="upload-info-wrap parse-label" v-if="item.extension == 'PPTX'" v-model="item.isParse">
                             {{$t('updModal.resolve')}}
-                        </Checkbox>
+                        </Checkbox> -->
                         <Progress style="top:-10px;" v-if="item.status == 0" :percent="item.progress" status="active" stroke-color="#1CC0F3" :stroke-width="2" hide-info />
                     </div>
                     <div class="upload-item-right" v-show="!textLoading">

+ 3 - 1
TEAMModelOS/ClientApp/src/locale/lang/en-US/schoolBaseInfo.js

@@ -248,5 +248,7 @@ export default {
   updOk:'Modified successfully',
   updErr:'Failed to modify',
   warmTips: 'Reminder',
-  noStuContent:'The school has not imported any student account, do you want to go to Student Management to import student accounts?'
+  noStuContent:'The school has not imported any student account, do you want to go to Student Management to import student accounts?',
+  setIrsWarning:'請設置IRS編號',
+  setNoWarning:'請設置座號'
 }

+ 1 - 1
TEAMModelOS/ClientApp/src/locale/lang/en-US/settings.js

@@ -55,7 +55,7 @@ export default {
 	status3: 'Applying',
 	searchTip: 'Please enter <span>school name</span> or <span>school code</span> to search',
 	searchNone: '* If you do not find the school you are looking for, you can click below to request to create your school',
-	searchPoper: 'If your school has a human icon, your application will be reviewed and approved by an administrator to become a member of the school. If your school does not have an administrator, your school and the "applying" status will be displayed after you apply and re-login.',
+	searchPoper: 'If your school has an administrator, your application will be reviewed and approved by an administrator to become a member of the school. If your school does not have an administrator, your school and the "applying" status will be displayed after you apply and re-login.',
 	applyBtn: 'Apply to create a school',
 	openList: 'Open Platform List',
 	openInfo: 'Platform Information',

+ 3 - 1
TEAMModelOS/ClientApp/src/locale/lang/zh-CN/schoolBaseInfo.js

@@ -248,5 +248,7 @@ export default {
   updOk: '修改成功',
   updErr: '修改失败',
   warmTips: '温馨提示',
-  noStuContent:'学校暂未导入学生账号,是否前往学生管理汇入学生账号?'
+  noStuContent:'学校暂未导入学生账号,是否前往学生管理汇入学生账号?',
+  setIrsWarning:'请设置IRS编号',
+  setNoWarning:'请设置座号'
 }

+ 1 - 1
TEAMModelOS/ClientApp/src/locale/lang/zh-CN/settings.js

@@ -55,7 +55,7 @@ export default {
 	status3:'申请中',
 	searchTip:'请输入<span>学校名称</span>或者<span>关键词</span>进行搜索',
 	searchNone:'* 如未搜索到您想要的学校,可点击下方申请建立您的学校',
-	searchPoper: '若您的学校名称有人型符号,申请后将由管理员为您审核通过,正式成为学校成员。 若尚未有管理员,您申请后并重新登录,将会显示您所属学校及”申请中”状态。',
+	searchPoper: '若您的学校有管理员,申请后将由管理员为您审核通过,正式成为学校成员。 若尚未有管理员,您申请后并重新登录,将会显示您所属学校及”申请中”状态。',
 	applyBtn:'申请建立学校',
 	openList: '开放平台应用列表',
 	openInfo: '应用信息',

+ 3 - 1
TEAMModelOS/ClientApp/src/locale/lang/zh-TW/schoolBaseInfo.js

@@ -248,5 +248,7 @@ export default {
   updOk:'修改成功',
   updErr:'修改失敗',
   warmTips: '溫馨提示',
-  noStuContent:'學校暫未導入學生帳號,是否前往學生管理匯入學生帳號?'
+  noStuContent:'學校暫未導入學生帳號,是否前往學生管理匯入學生帳號?',
+  setIrsWarning:'請設置IRS編號',
+  setNoWarning:'請設置座號'
 }

+ 1 - 1
TEAMModelOS/ClientApp/src/locale/lang/zh-TW/settings.js

@@ -55,7 +55,7 @@ export default {
     status3: '申請中',
     searchTip: '請輸入<span>學校名稱</span>或者<span>關鍵詞</span>進行搜尋',
     searchNone: '*如未搜尋到您想要的學校,可點擊下方申請建立 您的學校',
-    searchPoper: '若您的學校名稱有人型符號,申請後將由管理員為您審核通過,正式成為學校成員。 若尚未有管理員,您申請後並重新登入,將會顯示您所屬學校及”申請中”之狀態。',
+    searchPoper: '若您的學校有管理員,申請後將由管理員為您審核通過,正式成為學校成員。 若尚未有管理員,您申請後並重新登入,將會顯示您所屬學校及”申請中”之狀態。',
     applyBtn: '申請建立學校',
     openList: '開放平臺清單',
     openInfo: '平臺資訊',

+ 0 - 86
TEAMModelOS/ClientApp/src/static/course.json

@@ -1,86 +0,0 @@
-[
-  {
-    "link": "/jyzx/a5125c2d-3131-9514-7b29-b50066959a91/1.1案例导入.mp4",
-    "title": "1.1案例导入",
-    "time": "2021-01-22",
-    "poster":"https://teammodelstorage.blob.core.chinacloudapi.cn/0-public/image/1.jpg"
-  },
-  {
-    "link": "/jyzx/d1f8519f-dd44-3604-4962-8730a351e151/1.2 关键词“以校为本”.mp4",
-    "title": "1.2 关键词“以校为本”",
-    "time": "2021-01-22",
-    "poster":"https://teammodelstorage.blob.core.chinacloudapi.cn/0-public/image/2.jpg"
-  },
-  {
-    "link": "/jyzx/9612794f-599c-088b-bdf6-27b812221a1e/1.3 关键词“基于课堂”.mp4",
-    "title": "1.3 关键词“基于课堂”",
-    "time": "2021-01-22",
-    "poster":"https://teammodelstorage.blob.core.chinacloudapi.cn/0-public/image/3.jpg"
-  },
-  {
-    "link": "/jyzx/246b745e-9cb6-141a-611b-518c4b1b6fcd/1.4 关键词“应用驱动”.mp4",
-    "title": "1.4 关键词“应用驱动”",
-    "time": "2021-01-22",
-    "poster":"https://teammodelstorage.blob.core.chinacloudapi.cn/0-public/image/4.jpg"
-  },
-  {
-    "link": "/jyzx/35d4b310-05c5-ef88-d6e8-4d9eac44ec25/1.5 关键词“精准测评”(上).mp4",
-    "title": "1.5 关键词“精准测评”(上)",
-    "time": "2021-01-22",
-    "poster":"https://teammodelstorage.blob.core.chinacloudapi.cn/0-public/image/5.jpg"
-  },
-  {
-    "link": "/jyzx/b5b7461f-25d2-a49a-8e60-0894b066576f/1.6 关键词“精准测评”(下).mp4",
-    "title": "1.6 关键词“精准测评”(下)",
-    "time": "2021-01-22",
-    "poster":"https://teammodelstorage.blob.core.chinacloudapi.cn/0-public/image/6.jpg"
-  },
-  {
-    "link": "/jyzx/328be38b-cd85-aed6-aa20-83142b94d75f/2.1 认识测评体系 1.mp4",
-    "title": "2.1 认识测评体系 1",
-    "time": "2021-01-22",
-    "poster":"https://teammodelstorage.blob.core.chinacloudapi.cn/0-public/image/7.jpg"
-  },
-  {
-    "link": "/jyzx/328be38b-cd85-aed6-aa20-83142b94d75f/2.1 认识测评体系 2.mp4",
-    "title": "2.1 认识测评体系 2",
-    "time": "2021-01-22",
-    "poster":"https://teammodelstorage.blob.core.chinacloudapi.cn/0-public/image/8.jpg"
-  },
-  {
-    "link": "/jyzx/328be38b-cd85-aed6-aa20-83142b94d75f/2.1.1多媒体教学环境(上.mp4",
-    "title": "2.1.1多媒体教学环境(上)",
-    "time": "2021-01-22",
-    "poster":"https://teammodelstorage.blob.core.chinacloudapi.cn/0-public/image/9.jpg"
-  },
-  {
-    "link": "/jyzx/328be38b-cd85-aed6-aa20-83142b94d75f/2.1.2多媒体教学环境(下).mp4",
-    "title": "2.1.2多媒体教学环境(下)",
-    "time": "2021-01-22",
-    "poster":"https://teammodelstorage.blob.core.chinacloudapi.cn/0-public/image/10.jpg"
-  },
-  {
-    "link": "/jyzx/c687e4df-b74b-882d-4bd1-7f7342625987/2.2.1混合学习环境(上).mp4",
-    "title": "2.2.1混合学习环境(上",
-    "time": "2021-01-22",
-    "poster":"https://teammodelstorage.blob.core.chinacloudapi.cn/0-public/image/11.jpg"
-  },
-  {
-    "link": "/jyzx/c687e4df-b74b-882d-4bd1-7f7342625987/2.2.2混合学习环境(下).mp4",
-    "title": "2.2.2混合学习环境(下)",
-    "time": "2021-01-22",
-    "poster":"https://teammodelstorage.blob.core.chinacloudapi.cn/0-public/image/12.jpg"
-  },
-  {
-    "link": "/jyzx/c687e4df-b74b-882d-4bd1-7f7342625987/2.3智慧学习环境.mp4",
-    "title": "2.3智慧学习环境",
-    "time": "2021-01-22",
-    "poster":"https://teammodelstorage.blob.core.chinacloudapi.cn/0-public/image/13.jpg"
-  },
-  {
-    "link": "/jyzx/d7766778-67bd-5544-2402-35fedf21f0d7/2.4测评参与建议.mp4",
-    "title": "2.4测评参与建议",
-    "time": "2021-01-22",
-    "poster":"https://teammodelstorage.blob.core.chinacloudapi.cn/0-public/image/14.jpg"
-  }
-]

+ 0 - 22
TEAMModelOS/ClientApp/src/static/diagnosis.json

@@ -1,22 +0,0 @@
-[
-  {
-    "link": "https://teammodelstorage.blob.core.chinacloudapi.cn/0-public/video/%E8%AF%8A%E6%96%AD/%E3%80%8A%E5%AD%A6%E6%A0%A1%E4%BF%A1%E6%81%AF%E5%8C%96%E8%AF%8A%E6%96%AD%E4%B8%8E%E8%A7%84%E5%88%92%E5%88%B6%E5%AE%9A%E3%80%8B%E8%AF%BE%E7%A8%8B%E4%B8%80%EF%BC%88%E7%8E%AF%E5%A2%83%E4%B8%8E%E6%94%BF%E7%AD%96%E7%AF%87%EF%BC%89.mp4",
-    "poster": "https://teammodelstorage.blob.core.chinacloudapi.cn/0-public/video/%E8%AF%8A%E6%96%AD/%E5%9B%BE1%EF%BC%9A%E7%8E%AF%E5%A2%83%E4%B8%8E%E6%94%BF%E7%AD%96%E7%AF%87%E5%B0%81%E9%9D%A2.png",
-    "title": "《学校信息化诊断与规划制定》课程一(环境与政策篇)"
-  },
-  {
-    "link": "https://teammodelstorage.blob.core.chinacloudapi.cn/0-public/video/%E8%AF%8A%E6%96%AD/%E3%80%8A%E5%AD%A6%E6%A0%A1%E4%BF%A1%E6%81%AF%E5%8C%96%E8%AF%8A%E6%96%AD%E4%B8%8E%E8%A7%84%E5%88%92%E5%88%B6%E5%AE%9A%E3%80%8B%E8%AF%BE%E7%A8%8B%E4%BA%8C%EF%BC%88%E5%9B%A2%E9%98%9F%E4%BA%BA%E5%91%98%E7%AF%87%EF%BC%89.mp4",
-    "poster": "https://teammodelstorage.blob.core.chinacloudapi.cn/0-public/video/%E8%AF%8A%E6%96%AD/%E5%9B%BE2%EF%BC%9A%E5%9B%A2%E9%98%9F%E4%BA%BA%E5%91%98%E7%AF%87%E5%B0%81%E9%9D%A2.png",
-    "title": "《学校信息化诊断与规划制定》课程二(团队人员篇)"
-  },
-  {
-    "link": "https://teammodelstorage.blob.core.chinacloudapi.cn/0-public/video/%E8%AF%8A%E6%96%AD/%E3%80%8A%E5%AD%A6%E6%A0%A1%E4%BF%A1%E6%81%AF%E5%8C%96%E8%AF%8A%E6%96%AD%E4%B8%8E%E8%A7%84%E5%88%92%E5%88%B6%E5%AE%9A%E3%80%8B%E8%AF%BE%E7%A8%8B%E4%B8%89%EF%BC%88%E5%8F%91%E5%B1%95%E8%A7%84%E5%88%92%E7%AF%87%EF%BC%89.mp4",
-    "poster": "https://teammodelstorage.blob.core.chinacloudapi.cn/0-public/video/%E8%AF%8A%E6%96%AD/%E5%9B%BE3%EF%BC%9A%E5%8F%91%E5%B1%95%E8%A7%84%E5%88%92%E7%AF%87%E5%B0%81%E9%9D%A2.png",
-    "title": "《学校信息化诊断与规划制定》课程三(发展规划篇)"
-  },
-  {
-    "link": "https://teammodelstorage.blob.core.chinacloudapi.cn/0-public/video/%E8%AF%8A%E6%96%AD/%E3%80%8A%E5%AD%A6%E6%A0%A1%E4%BF%A1%E6%81%AF%E5%8C%96%E8%AF%8A%E6%96%AD%E4%B8%8E%E8%A7%84%E5%88%92%E5%88%B6%E5%AE%9A%E3%80%8B%E8%AF%BE%E7%A8%8B%E5%9B%9B%EF%BC%88%E2%80%9D%E6%A0%A1%E6%9C%AC%E7%A0%94%E4%BF%AE%E4%B8%8E%E8%80%83%E6%A0%B8%E2%80%9C%E4%B8%A4%E6%A1%88%E7%AF%87%EF%BC%89.mp4",
-    "poster": "https://teammodelstorage.blob.core.chinacloudapi.cn/0-public/video/%E8%AF%8A%E6%96%AD/%E5%9B%BE4%EF%BC%9A%E4%B8%A4%E6%A1%88%E7%AF%87%E5%B0%81%E9%9D%A2.png",
-    "title": "《学校信息化诊断与规划制定》课程四(”校本研修与考核“两案篇)"
-  }
-]

Diff do ficheiro suprimidas por serem muito extensas
+ 0 - 443
TEAMModelOS/ClientApp/src/static/policy.json


Diff do ficheiro suprimidas por serem muito extensas
+ 0 - 410
TEAMModelOS/ClientApp/src/static/video.json


+ 3 - 1
TEAMModelOS/ClientApp/src/utils/blobTool.js

@@ -19,7 +19,9 @@ function getExAndType(fileName) {
         ex, type
     }
 }
-
+/**
+ * 授权信息可以内部处理,不用每次调用都需要处理好授权信息
+ */
 export default class BlobTool {
     /**
      * 初始化Blob,需要先调用授权API

+ 30 - 27
TEAMModelOS/ClientApp/src/view/Home.vue

@@ -4,8 +4,8 @@
         <BaseLayout>
             <!-- 头部右侧个人中心部分 -->
             <div class="header-right-box fl-around" slot="header-content">
-				<Icon style="display:block" custom="iconfont icon-home" :color="routerName == 'homePage' ? '#1CC0F3':'#d0d0d0'" @click="toHome" :title="$t('system.goHome')" />
-                <Icon type="md-open" @click="changePlatform" :title="$t('system.changePlat')" v-if="hasArea"/>
+                <Icon style="display:block" custom="iconfont icon-home" :color="isHomeLight ? '#1CC0F3':'#d0d0d0'" @click="toHome" :title="$t('system.goHome')" />
+                <Icon type="md-open" @click="changePlatform" :title="$t('system.changePlat')" v-if="hasArea" />
                 <BaseNotification :msgs="msgs"></BaseNotification>
                 <span class="header-split"></span>
                 <BaseUserPoptip @logout="basicMenu('quit')"></BaseUserPoptip>
@@ -43,10 +43,10 @@ export default {
         }
     },
     created() {
-		this.$Message.config({
-		    duration: 3
-		});
-        console.log('config',this.$store.state.config)
+        this.$Message.config({
+            duration: 3
+        });
+        console.log('config', this.$store.state.config)
         // 检查超时操作页面,清空缓存数据
         let webEndTime = localStorage.getItem('webEndTime')
         let time_now = new Date().getTime()
@@ -57,7 +57,7 @@ export default {
         this.$store.dispatch('user/checkSchoolCode');// 設定登入成功的學校簡碼
         this.$store.dispatch('user/checkUserProfile');// 檢查使用者個人詳細資訊
         this.$store.dispatch('user/checkStudentProfile');// 檢查學生的詳細資訊
-        
+
         // this.$store.dispatch('user/checkSchoolProfile').then(res => {
         //     if (res) {
         //         this.$User.freshLogin()
@@ -122,25 +122,25 @@ export default {
             let schoolNotice = await this.getSchoolNotice(user_profile, userId)
             let privateNotice = await this.getPrivateNotice(userId)
             this.msgs = schoolNotice.concat(privateNotice)
-			if(this.msgs.length){
-				this.msgs.forEach(i => {
-					i.body = JSON.parse(this.getBodyJson(i.body))
-				})
-			}
-			let localMsgs = JSON.parse(localStorage.getItem('msgs'))
-			if(localMsgs){
-				localStorage.setItem('msgs',JSON.stringify([...this.msgs,...localMsgs]))
-			}else{
-				localStorage.setItem('msgs',JSON.stringify(this.msgs))
-			}
+            if (this.msgs.length) {
+                this.msgs.forEach(i => {
+                    i.body = JSON.parse(this.getBodyJson(i.body))
+                })
+            }
+            let localMsgs = JSON.parse(localStorage.getItem('msgs'))
+            if (localMsgs) {
+                localStorage.setItem('msgs', JSON.stringify([...this.msgs, ...localMsgs]))
+            } else {
+                localStorage.setItem('msgs', JSON.stringify(this.msgs))
+            }
             console.log('端外通知', this.msgs)
         },
-		getBodyJson(str) {
-			let reg = /\\/g;
-			//使用replace方法将全部匹配正则表达式的转义符替换为空
-			let replaceAfter = str.replace(reg, '').replace(/=/g, ':');
-			return replaceAfter
-		},
+        getBodyJson(str) {
+            let reg = /\\/g;
+            //使用replace方法将全部匹配正则表达式的转义符替换为空
+            let replaceAfter = str.replace(reg, '').replace(/=/g, ':');
+            return replaceAfter
+        },
         getSchoolNotice(user_profile, userId) {
             return new Promise((r, j) => {
                 if (this.hasSchool) {
@@ -216,6 +216,9 @@ export default {
         },
         hasArea() {
             return this.$store.state.user.userProfile.areas.length > 0
+        },
+        isHomeLight() {
+           return this.routerName == 'homePage' || (this.$store.state.config.srvAdr == 'Global' && this.routerName == 'myCourse')
         }
     },
     watch: {
@@ -238,19 +241,19 @@ export default {
     width: 0;
     background: #636363;
     height: 25px;
-	margin: 0 15px;
+    margin: 0 15px;
 }
 .header-right-box {
     margin: 12px 0;
     line-height: 1.5;
     float: right;
     height: 25px;
-	padding-right: 20px;
+    padding-right: 20px;
 }
 
 .header-right-box .ivu-icon {
     font-size: 20px;
-	margin-right: 20px;
+    margin-right: 20px;
     color: #d0d0d0;
     cursor: pointer;
 }

+ 1 - 1
TEAMModelOS/ClientApp/src/view/areaMgmt/AreaLayout.less

@@ -27,7 +27,7 @@
         position: absolute;
         top: 50px;
         left: 0px;
-        z-index: 999;
+        z-index: 900;
         overflow: hidden;
         background: var(--header-bg);
     }

+ 1 - 1
TEAMModelOS/ClientApp/src/view/evaluation/index/CreateExercises.less

@@ -8,7 +8,7 @@
 		position: sticky;
 		top: 0;
 		background: #fff;
-		z-index: 994;
+		z-index: 899;
 		display: flex;
 		justify-content: space-between;
 		border-bottom: 1px solid #eaeaea;

+ 6 - 1
TEAMModelOS/ClientApp/src/view/evaluation/index/CreateExercises.vue

@@ -9,7 +9,9 @@
 						* 已开启醍摩豆教学助手,可以获得更好图文粘贴体验
 					</span>
 					<span v-else style="color: #e72f32;">
-						* 检测到您暂未开启醍摩豆教学助手,启动后可获得更好图文粘贴体验<span style="margin-left: 5px;text-decoration: underline;color: #0074D9;cursor: pointer;" @click="openHiTool">启动</span>
+						* 检测到您暂未开启醍摩豆教学助手,启动后可获得更好图文粘贴体验
+						<span style="margin-left: 5px;text-decoration: underline;color: #0074D9;cursor: pointer;" @click="openHiTool">启动</span>
+						<!-- <span style="margin-left: 5px;text-decoration: underline;color: #0074D9;cursor: pointer;" @click="doDownload">下载</span> -->
 					</span>
 				</span>
 			</span>
@@ -274,6 +276,9 @@
 			openHiTool(){
 				window.open("hitools://")
 			},
+			doDownload(){
+				
+			},
 
 			onSelectPoints() {
 				if(this.isSchool && (!this.subjectList.length  || !this.gradeList.length)){

+ 3 - 1
TEAMModelOS/ClientApp/src/view/homepage/NewHomePage.vue

@@ -518,7 +518,9 @@ export default {
         this.getTeacherRecordData()
         if (this.$store.state.userInfo.hasSchool) {
             this.findNotice()
-            this.getStudyTime()
+            if(this.$store.state.config.srvAdr == 'China'){
+                this.getStudyTime()
+            }
         }
     },
     mounted() {

+ 5 - 5
TEAMModelOS/ClientApp/src/view/jyzx/HomePage.vue

@@ -295,27 +295,27 @@ export default {
                     if (this.onlineTime >= 20) {
                         this.onlinePer = 100
                     } else {
-                        this.onlinePer = Math.floor((res.teacherTrain.onlineTime / res.setting.onlineTime) * 100)
+                        this.onlinePer = ((res.teacherTrain.onlineTime / res.setting.onlineTime) * 100).toFixed(1)
                     }
                     // 校本研修
                     if (this.offlineTime >= 10) {
                         this.offlinePer = 100
                     } else {
-                        this.offlinePer = Math.floor((res.teacherTrain.offlineTime / res.setting.offlineTime) * 100)
+                        this.offlinePer = ((res.teacherTrain.offlineTime / res.setting.offlineTime) * 100).toFixed(1)
                     }
                     // 应用考核
                     if (this.applicaTime >= 15) {
                         this.applicaPer = 100
                     } else {
-                        this.applicaPer = Math.floor((res.teacherTrain.currency.submitTime / res.setting.submitTime) * 100)
+                        this.applicaPer = ((res.teacherTrain.currency.submitTime / res.setting.submitTime) * 100).toFixed(1)
                     }
                     // 课堂实录
                     if (this.discussTime >= 5) {
                         this.discussPer = 100
                     } else {
-                        this.discussPer = Math.floor((res.teacherTrain.classTime / res.setting.classTime) * 100)
+                        this.discussPer = ((res.teacherTrain.classTime / res.setting.classTime) * 100).toFixed(1)
                     }
-                    this.percent = Math.floor((this.totalTime / res.setting.allTime) * 100)
+                    this.percent = ((this.totalTime / res.setting.allTime) * 100).toFixed(1)
                     // 检测
                     this.allAbilityOkNum = res.teacherTrain.currency.exerciseAbility
                     this.allAbilityOkTotal = res.teacherTrain.currency.subCount

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

@@ -454,7 +454,7 @@ export default {
             this.$Spin.show(); //開啟加載畫面
             let redirect_uri = window.location.origin + '/login'
             this.$api.OauthLogin(this.userOauth.state, this.userOauth.code, redirect_uri).then(res => {
-                this.$Spin.hide(); //關閉加載畫面
+                
                 //TODO 大陆站微信登陆需要验证是否启用
                 let tokenData = jwtDecode(res.id_token)
                 localStorage.setItem("access_token",res.access_token)
@@ -468,6 +468,7 @@ export default {
                             id: tokenData.sub
                         }
                     })
+                    this.$Spin.hide(); //關閉加載畫面
                 } else {
                     //已启用,正常登录
                     this.loginProcess(res, this.defaultSchool.code)
@@ -526,6 +527,7 @@ export default {
                         this.identityFlag = true;
                     }
                 })
+                this.$Spin.hide(); //關閉加載畫面
             }
         },
         saveUserCodes: function (res) {

+ 1 - 1
TEAMModelOS/ClientApp/src/view/questionnaire/ManageQuestionnaire.less

@@ -164,7 +164,7 @@
                 }
 
                 &-name {
-                    font-size: @title-fontSize;
+                    font-size: @second-fontSize;
                     color: @primary-textColor;
                     // font-weight: bold;
 					word-break: break-all;

+ 1 - 1
TEAMModelOS/ClientApp/src/view/research-center/BaseVideo.vue

@@ -11,7 +11,7 @@
 			</div>
 			<div class="video-item-infos">
 				<p class="video-name text-cut">{{ item.name }}</p>
-				<p class="teacher-name">{{ item.teacher }}</p>
+				<p class="teacher-name">{{ item.tmdname }}</p>
 				<p class="subject-name">
 					<span>{{ item.subject || '暂无' }} | {{ item.grade }}</span>
 					<span>{{ transferSecond(item.duration) }}</span>

+ 20 - 3
TEAMModelOS/ClientApp/src/view/research-center/ResearchMgt.vue

@@ -14,7 +14,7 @@
 				</div>
 				<div class="filter-item">
 					<span class="filter-item-title">归属年级</span>
-					<Select v-model="filterJson.gradeId">
+					<Select v-model="filterJson.gradeId" clearable>
 						<Option v-for="(item,index) in curPeriod.grades" :value="index" :key="index">{{ item }}
 						</Option>
 					</Select>
@@ -57,10 +57,25 @@
 				<Icon type="md-settings" />
 				<span>类别管理</span>
 			</div>
+			<div class="tool-item">
+				<Icon type="ios-paper-plane" />
+				<span>分享至公开课</span>
+			</div>
 		</div>
 		<div class="table-box">
 			<Scroll :on-reach-bottom="getMore" height="750"  :distance-to-edge="[15, 15]">
 				<Table stripe :columns="tableColumn" :data="tableData" :loading="tableLoading">
+					<template slot-scope="{ row,index }" slot="tmdname">
+						<span>
+						<!-- <Tag color="#dfa718" v-if="index === 0">公开课</Tag> -->
+						{{ row.tmdname || row.tmdid }}</span>
+					</template>
+					<template slot-scope="{ row,index }" slot="name">
+						<span>
+						<!-- <Tag color="#dfa718" v-if="index === 0">公开课</Tag> -->
+						{{ row.name }}
+						</span>
+					</template>
 					<template slot-scope="{ row }" slot="category">
 						<span>{{ row.category.length ? row.category.join(',') : '-' }}</span>
 					</template>
@@ -71,6 +86,7 @@
 						<span>{{ row.grade.map(i => curPeriod.grades[+i]).join(',') }}</span>
 					</template>
 					<template slot-scope="{ row }" slot="action">
+						<Icon class="action-icon" type="ios-paper-plane" color="#27a3cf" @click="doPlay(row)"/>
 						<Icon class="action-icon" type="md-play" color="#34cf97" @click="doPlay(row)"/>
 						<Icon class="action-icon" type="md-podium" color="#3197e5" @click="showAnalysis(row)"/>
 						<Icon class="action-icon" type="md-create" color="#3197e5" @click="doEdit(row)"/>
@@ -147,7 +163,7 @@
 					},
 					{
 						title: '教师名称',
-						key: 'teacherName',
+						slot: 'tmdname',
 						sortable: true,
 						width: 150
 					},
@@ -156,7 +172,7 @@
 						tooltip: true,
 						tooltipTheme: 'light',
 						tooltipMaxWidth: 200,
-						key: 'name'
+						slot: 'name'
 					},
 					{
 						title: '类别',
@@ -204,6 +220,7 @@
 		methods: {
 			onTchSelect(tchId){
 				this.filterJson.tchId = tchId
+				this.doSearch()
 			},
 			/* 初始化请求参数 */
 			initFilter(){

+ 0 - 147
TEAMModelOS/ClientApp/src/view/resource/DiagDetail.vue

@@ -1,147 +0,0 @@
-<template>
-    <div class="literacy-detail-container">
-        <div class="literacy-mgt-top" @click="backList">
-            <Icon type="md-arrow-round-back" size="20" />
-            <span style="margin-left:10px">返回规划诊断课程</span>
-        </div>
-        <vuescroll>
-            <div class="literacy-block-box">
-                <div class="video-info-wrap">
-                    <h1 class="video-title">{{videoInfo.title}}</h1>
-                    <video :src="videoInfo.link" class="video-box" controls="controls" autoplay="autoplay"></video>
-                </div>
-                <div class="more-link-wrap">
-                    <p class="more-title">
-                        更多课程
-                        <Icon custom="iconfont icon-handle-down" size="20" />
-                    </p>
-                    <div class="video-list-box">
-                        <vuescroll>
-                            <div class="video-item" v-for="(item,index) in videoList" :key="index" @click="chooseCus(index)">
-                                <img :src="item.poster" class="video-poster">
-                                <div style="margin-left:15px">
-                                    <p class="video-attr-item">
-                                        <span>{{item.title}}</span>
-                                    </p>
-                                </div>
-                            </div>
-                        </vuescroll>
-                    </div>
-                </div>
-            </div>
-        </vuescroll>
-    </div>
-</template>
-<script>
-const courseData = require('@/static/course.json')
-export default {
-    data() {
-        return {
-            keyword: '',
-            videoInfo: {},
-            videoList: []
-        }
-    },
-    methods: {
-        search() {
-
-        },
-        backList() {
-            this.$router.push({
-                name: 'diagnosis'
-            })
-        },
-        chooseCus(index){
-            this.videoInfo = this.videoList[index]
-        }
-    },
-    created() {
-        this.videoList = require('@/static/diagnosis.json')
-        this.videoInfo = this.$route.params.videoInfo || this.videoList[0]
-        this.course = []
-        this.course = this._.cloneDeep(courseData)
-        let sas = this.$store.state.user.schoolProfile.blob_sas
-        let blobUrl = JSON.parse(decodeURIComponent(localStorage.school_profile, "utf-8")).blob_uri
-        this.course.forEach(item => {
-            let url = `${blobUrl}${item.link}?${sas}`
-            item.link = url
-        })
-        this.videoList.push(...this.course)
-    }
-}
-</script>
-<style lang="less" scoped>
-.info-label{
-    color: #808695;
-    display: block;
-}
-.video-list-box {
-    height: 620px;
-}
-.video-attr-item {
-    font-size: 12px;
-}
-.video-item {
-    padding: 10px;
-    border-bottom: 1px solid #eee;
-    display: flex;
-    cursor: pointer;
-    &:hover{
-        background: #f0f0f0;
-    }
-}
-.video-poster {
-    width: 120px;
-}
-.more-title {
-    height: 35px;
-    line-height: 35px;
-    font-size: 12px;
-    border-bottom: 1px solid #eee;
-    padding-left: 10px;
-}
-.video-title {
-    margin-bottom: 15px;
-}
-.info-item {
-    margin: 10px 0px;
-    font-size: 16px;
-}
-.video-box {
-    // height: 600px;
-    width: 66%;
-}
-.literacy-detail-container {
-    width: 100%;
-    height: 100%;
-    padding: 15px;
-}
-.literacy-block-box {
-    width: 100%;
-    display: flex;
-    justify-content: space-between;
-}
-.video-info-wrap {
-    width: 80%;
-    min-height: 600px;
-    background: white;
-    padding: 15px;
-}
-.more-link-wrap {
-    width: 20%;
-    min-height: 600px;
-    background: white;
-}
-.literacy-mgt-top {
-    width: 100%;
-    height: 40px;
-    background: white;
-    box-shadow: 0px 2px 5px #e9e9e9;
-    padding-left: 10px;
-    padding-right: 20px;
-    padding-bottom: 10px;
-    line-height: 40px;
-    cursor: pointer;
-    color: #40A8F0;
-}
-</style>

+ 0 - 143
TEAMModelOS/ClientApp/src/view/resource/Diagnosis.vue

@@ -1,143 +0,0 @@
-<template>
-    <div class="literacy-container">
-        <div class="literacy-search light-iview-input">
-            <Input v-special-char v-model="keyword" suffix="ios-search" placeholder="搜索" style="width: 200px;margin-top:0px" @on-change="search" />
-        </div>
-        <!-- <div class="literacy-mgt-top">
-            <Input v-model="keyword" suffix="ios-search" placeholder="搜索" style="width: 200px;margin-top:0px" @on-change="search" />
-        </div> -->
-        <vuescroll>
-            <div class="literacy-block-box">
-                <div class="literacy-item" v-for="(item,index) in videoListShow" :key="index" @click="toDetailPage(index)">
-                    <div class="literacy-img" :style="{backgroundImage: `url(${item.poster})`}"></div>
-                    <p class="img-text">{{item.title}}<p/>
-                </div>
-                <div class="literacy-item" v-for="(item,index) in course" :key="index" @click="toDetailPage(index)">
-                    <div class="literacy-img" :style="{backgroundImage: `url(${item.poster})`}"></div>
-                    <p class="img-text">{{item.title}}<p/>
-                </div>
-            </div>
-            <EmptyData textContent="未搜到相关课程" v-show="!videoListShow.length" :top="100"></EmptyData>
-        </vuescroll>
-    </div>
-</template>
-<script>
-const courseData = require('@/static/course.json')
-export default {
-    data() {
-        return {
-            keyword: '',
-            videoList: [],
-            videoListShow: [],
-            course:[]
-        }
-    },
-    methods: {
-        search() {
-            if (this.keyword) {
-                this.videoListShow = this.videoList.filter(item => {
-                    return JSON.stringify(item).includes(this.keyword)
-                })
-            }else{
-                this.videoListShow = this.videoList
-            }
-
-        },
-        toDetailPage(index) {
-            this.$router.push({
-                name: 'diagDetail',
-                params: {
-                    videoInfo: this.videoList[index]
-                }
-            })
-        }
-    },
-    created() {
-        this.videoList = require('@/static/diagnosis.json')
-        this.videoListShow = this.videoList
-        this.course = []
-        this.course = this._.cloneDeep(courseData)
-        let sas = this.$store.state.user.schoolProfile.blob_sas
-        let blobUrl = JSON.parse(decodeURIComponent(localStorage.school_profile, "utf-8")).blob_uri
-        this.course.forEach(item => {
-            let url = `${blobUrl}${item.link}?${sas}`
-            item.link = url
-        })
-    }
-}
-</script>
-<style lang="less" scoped>
-.literacy-container {
-    width: 100%;
-    height: 100%;
-    background: white;
-    padding: 15px;
-}
-.literacy-block-box {
-    width: 100%;
-    // padding: 10px;
-    display: flex;
-    flex-wrap: wrap;
-}
-.literacy-item {
-    width: 300px;
-    background: #fcfcfc;
-    border: 1px solid #f0f0f0;
-    border-radius: 5px;
-    overflow: hidden;
-    margin-bottom: 30px;
-    margin-right: 30px;
-    cursor: pointer;
-    height: 180px;
-    position: relative;
-    transition: all 0.2s ease 0s;
-    &:hover {
-        box-shadow: 0 26px 40px -24px #aaa;
-        transform: translateY(-4px);
-    }
-    &:hover .literacy-img {
-        transform: scale(1.1);
-    }
-    .literacy-img-box {
-        width: 300px;
-        height: 150px;
-        overflow: hidden;
-    }
-    .literacy-img {
-        transition: all 0.3s;
-        width: 300px;
-        height: 180px;
-        background-size: cover;
-        background-repeat: no-repeat;
-    }
-    .img-text{
-        width: 300px;
-        height: 180px;
-        position: absolute;
-        left: 0px;
-        top: 0px;
-        background: rgba(20, 20, 20, .1);
-        color: white;
-        font-size: 14px;
-        padding: 5px;
-        &:hover{
-            background: rgba(20, 20, 20, .2);
-        }
-    }
-}
-.literacy-mgt-top {
-    width: ~"calc(100% - 20px)";
-    height: 40px;
-    box-shadow: 0px 2px 5px #e9e9e9;
-    margin-left: 10px;
-    padding-left: 10px;
-    padding-right: 20px;
-    padding-bottom: 10px;
-    line-height: 40px;
-}
-.literacy-search{
-    float: right;
-    margin-top: -52px;
-    margin-right: 20px;
-}
-</style>

Diff do ficheiro suprimidas por serem muito extensas
+ 0 - 256
TEAMModelOS/ClientApp/src/view/resource/Leadership.vue


Diff do ficheiro suprimidas por serem muito extensas
+ 0 - 229
TEAMModelOS/ClientApp/src/view/resource/LeadershipDetail.vue


+ 0 - 172
TEAMModelOS/ClientApp/src/view/resource/Literacy.vue

@@ -1,172 +0,0 @@
-<template>
-    <div class="literacy-container">
-        <div class="literacy-search light-iview-input">
-            <Input v-special-char v-model="keyword" suffix="ios-search" placeholder="搜索" style="width: 200px;margin-top:0px" @on-change="search" />
-        </div>
-        <!-- <div class="literacy-mgt-top">
-            <Input v-model="keyword" suffix="ios-search" placeholder="搜索" style="width: 200px;margin-top:0px" @on-change="search" />
-        </div> -->
-        <vuescroll>
-            <div class="literacy-block-box">
-                <div class="literacy-item" v-for="(item,index) in videoListShow" :key="item.title" @click="toDetailPage(index)">
-                    <div class="literacy-img-box">
-                        <div class="literacy-img" :style="{backgroundImage: `url(${item.poster})`}"></div>
-                    </div>
-                    <div class="info-item" v-show="!item.type">
-                        <span class="info-label">报告人:</span>
-                        <span>{{item.reporter}}</span>
-                    </div>
-                    <div class="info-item">
-                        <span class="info-label">主题:</span>
-                        <span>{{item.title}}</span>
-                    </div>
-                </div>
-                <!-- <div class="literacy-item" v-for="(item,index) in course" :key="index" @click="toDetailPage1(index)">
-                    <div class="literacy-img-box">
-                        <div class="literacy-img" :style="{backgroundImage: `url(${item.poster})`}"></div>
-                    </div>
-                    <div class="info-item">
-                        <span class="info-label">主题:</span>
-                        <span>{{item.title}}</span>
-                    </div>
-                </div> -->
-            </div>
-            <EmptyData textContent="未搜到相关课程" v-show="!videoListShow.length" :top="100"></EmptyData>
-        </vuescroll>
-    </div>
-</template>
-<script>
-const courseData = require('@/static/course.json')
-export default {
-    data() {
-        return {
-            keyword: '',
-            videoList: [],
-            videoListShow: [],
-            course: []
-        }
-    },
-    methods: {
-        search() {
-            if (this.keyword) {
-                this.videoListShow = this.videoList.filter(item => {
-                    return JSON.stringify(item).includes(this.keyword)
-                })
-            } else {
-                this.videoListShow = this.videoList
-            }
-
-        },
-        toDetailPage(index) {
-            this.$router.push({
-                name: 'literacyDetail',
-                params: {
-                    videoInfo: this.videoList[index]
-                }
-            })
-        },
-        toDetailPage1(index) {
-            this.$router.push({
-                name: 'literacyDetail',
-                params: {
-                    videoInfo: this.course[index]
-                }
-            })
-        }
-    },
-    created() {
-        this.videoList = require('@/static/video.json')
-        this.course = []
-        this.course = this._.cloneDeep(courseData)
-        let sas = this.$store.state.user.schoolProfile.blob_sas
-        let blobUrl = JSON.parse(decodeURIComponent(localStorage.school_profile, "utf-8")).blob_uri
-        this.course.forEach(item => {
-            let url = `${blobUrl}${item.link}?${sas}`
-            item.link = url
-            item.type = 'policy'
-        })
-        this.videoList.push(...this.course)
-        //如果后面数据量更多需要做分页或者懒加载
-        this.videoListShow = this.videoList
-
-    }
-}
-</script>
-<style lang="less" scoped>
-
-.literacy-container {
-    width: 100%;
-    height: 100%;
-    background: #f7f7f7;
-    padding: 15px;
-}
-.literacy-block-box {
-    // background: white;
-    width: 100%;
-    // min-height: 800px;
-    // padding: 10px;
-    display: flex;
-    flex-wrap: wrap;
-}
-.literacy-item {
-    width: 300px;
-    background: white;
-    border: 1px solid #f0f0f0;
-    padding-bottom: 10px;
-    border-radius: 5px;
-    overflow: hidden;
-    margin-bottom: 30px;
-    margin-right: 30px;
-    cursor: pointer;
-    height: fit-content;
-    transition: all 0.2s ease 0s;
-    .info-label {
-        color: #a5a5a5;
-    }
-    &:hover {
-        box-shadow: 0 26px 40px -24px #aaa;
-        transform: translateY(-4px);
-    }
-    &:hover .literacy-img {
-        transform: scale(1.1);
-    }
-    .literacy-img-box {
-        width: 300px;
-        height: 150px;
-        overflow: hidden;
-    }
-    .literacy-img {
-        transition: all 0.3s;
-        width: 300px;
-        height: 150px;
-        background-size: cover;
-        background-repeat: no-repeat;
-    }
-    .info-item {
-        margin-top: 10px;
-        width: 100%;
-        padding-left: 5px;
-        font-size: 16px;
-        text-overflow: ellipsis;
-        overflow: hidden;
-        white-space: nowrap;
-    }
-}
-.literacy-mgt-top {
-    width: ~"calc(100% - 20px)";
-    height: 40px;
-    box-shadow: 0px 2px 5px #e9e9e9;
-    margin-left: 10px;
-    padding-left: 10px;
-    padding-right: 20px;
-    padding-bottom: 10px;
-    line-height: 40px;
-}
-.literacy-search{
-    float: right;
-    margin-top: -52px;
-    margin-right: 20px;
-}
-</style>
-<style lang="less">
-</style>

+ 0 - 162
TEAMModelOS/ClientApp/src/view/resource/LiteracyDetail.vue

@@ -1,162 +0,0 @@
-<template>
-    <div class="literacy-detail-container">
-        <div class="literacy-mgt-top" @click="backList">
-            <Icon type="md-arrow-round-back" size="20" />
-            <span style="margin-left:10px">返回素养课程</span>
-        </div>
-        <vuescroll>
-            <div class="literacy-block-box">
-                <div class="video-info-wrap">
-                    <h1 class="video-title">{{videoInfo.title}}</h1>
-                    <video :src="videoInfo.link" class="video-box" controls="controls"  autoplay="autoplay"></video>
-                    <p class="info-item" v-show="videoInfo.reporter">
-                        <span class="info-label">报告人:</span>
-                        <span>{{videoInfo.reporter}}</span>
-                    </p>
-                    <p class="info-item" v-show="videoInfo.unit">
-                        <span class="info-label">单位:</span>
-                        <span>{{videoInfo.unit}}</span>
-                    </p>
-                    <p class="info-item"  v-show="videoInfo.content">
-                        <span class="info-label">简介:</span>
-                        <span>{{videoInfo.content}}</span>
-                    </p>
-                </div>
-                <div class="more-link-wrap">
-                    <p class="more-title">
-                        更多课程
-                        <Icon custom="iconfont icon-handle-down" size="20" />
-                    </p>
-                    <div class="video-list-box">
-                        <vuescroll>
-                            <div class="video-item" v-for="(item,index) in videoList" :key="index" @click="chooseCus(index)">
-                                <img :src="item.poster" class="video-poster">
-                                <div style="margin-left:15px">
-                                    <p class="video-attr-item">
-                                        <span class="info-label">报告人:</span>
-                                        <span>{{item.reporter}}</span>
-                                    </p>
-                                    <p class="video-attr-item">
-                                        <span class="info-label">单位:</span>
-                                        <span>{{item.unit}}</span>
-                                    </p>
-                                    <p class="video-attr-item">
-                                        <span class="info-label">主题:</span>
-                                        <span>{{item.title}}</span>
-                                    </p>
-                                </div>
-                            </div>
-                        </vuescroll>
-                    </div>
-
-                </div>
-            </div>
-        </vuescroll>
-    </div>
-</template>
-<script>
-export default {
-    data() {
-        return {
-            keyword: '',
-            videoInfo: {},
-            videoList: []
-        }
-    },
-    methods: {
-        search() {
-
-        },
-        backList() {
-            this.$router.push({
-                name: 'literacy'
-            })
-        },
-        chooseCus(index){
-            this.videoInfo = this.videoList[index]
-        }
-    },
-    created() {
-        this.videoList = require('@/static/video.json')
-        this.videoInfo = this.$route.params.videoInfo || this.videoList[0]
-    }
-}
-</script>
-<style lang="less" scoped>
-.info-label{
-    color: #808695;
-}
-.video-list-box {
-    height: 620px;
-}
-.video-attr-item {
-    font-size: 12px;
-    text-overflow: ellipsis;
-    overflow: hidden;
-    width: 180px;
-    white-space: nowrap;
-}
-.video-item {
-    padding: 10px;
-    border-bottom: 1px solid #eee;
-    display: flex;
-    cursor: pointer;
-    &:hover{
-        background: #f0f0f0;
-    }
-}
-.video-poster {
-    width: 120px;
-}
-.more-title {
-    height: 35px;
-    line-height: 35px;
-    font-size: 12px;
-    border-bottom: 1px solid #eee;
-    padding-left: 10px;
-}
-.video-title {
-    margin-bottom: 15px;
-}
-.info-item {
-    margin: 10px 0px;
-    font-size: 16px;
-}
-.video-box {
-    // height: 600px;
-    width: 60%;
-}
-.literacy-detail-container {
-    width: 100%;
-    height: 100%;
-    padding: 15px;
-}
-.literacy-block-box {
-    width: 100%;
-    display: flex;
-    justify-content: space-between;
-}
-.video-info-wrap {
-    width: 80%;
-    min-height: 600px;
-    background: white;
-    padding: 15px;
-}
-.more-link-wrap {
-    width: 20%;
-    min-height: 600px;
-    background: white;
-}
-.literacy-mgt-top {
-    width: 100%;
-    height: 40px;
-    background: white;
-    box-shadow: 0px 2px 5px #e9e9e9;
-    padding-left: 10px;
-    padding-right: 20px;
-    padding-bottom: 10px;
-    line-height: 40px;
-    cursor: pointer;
-    color: #40A8F0;
-}
-</style>

+ 0 - 1
TEAMModelOS/ClientApp/src/view/resource/Policy.vue

@@ -112,7 +112,6 @@
 </template>
 <script>
 import BlobTool from '@/utils/blobTool.js'
-const courseData = require('@/static/course.json')
 export default {
     data() {
         const validateUrl = (rule, value, callback) => {

+ 0 - 177
TEAMModelOS/ClientApp/src/view/resource/Probation.vue

@@ -1,177 +0,0 @@
-<template>
-    <div class="literacy-container">
-        <div class="literacy-search light-iview-input">
-            <Input v-model="keyword" suffix="ios-search" placeholder="搜索" style="width: 200px;margin-top:0px" @on-change="search" />
-        </div>
-        <!-- <div class="literacy-mgt-top">
-            <Input v-model="keyword" suffix="ios-search" placeholder="搜索" style="width: 200px;margin-top:0px" @on-change="search" />
-        </div> -->
-        <vuescroll>
-            <div class="literacy-block-box">
-                <div class="literacy-item" v-for="(item,index) in videoListShow" :key="index" @click="toDetailPage(index)">
-                    <template v-if="item.isShow">
-                        <div :class="['literacy-img',index == 5 || index == 6 ? 'last-img' : '']" :style="{backgroundImage: `url(${item.img})`}"></div>
-                        <div class="info-item">
-                            <!-- <span class="info-label">报告人:</span> -->
-                            <span>{{item.name}}</span>
-                        </div>
-                    </template>
-                </div>
-            </div>
-            <EmptyData textContent="未搜到相关课程" v-show="!videoListShow.length" :top="100"></EmptyData>
-        </vuescroll>
-    </div>
-</template>
-<script>
-export default {
-    data() {
-        return {
-            keyword: '',
-            videoList: [],
-            videoListShow: []
-        }
-    },
-    methods: {
-        search() {
-            if (this.keyword) {
-                this.videoListShow = this.videoList.filter(item => {
-                    return JSON.stringify(item).includes(this.keyword)
-                })
-            } else {
-                this.videoListShow = this.videoList
-            }
-
-        },
-        toDetailPage(index) {
-            window.open(this.videoListShow[index].link)
-        }
-    },
-    created() {
-        let host = window.location.host
-        console.log(host)
-        this.videoList = [
-            {
-                name: '《中国教育学刊》课堂变革于教学领导力提升专题培训',
-                img: 'https://teammodelstorage.blob.core.chinacloudapi.cn/0-public/image/%E4%B8%AD%E5%9B%BD%E6%95%99%E8%82%B2%E5%AD%A6%E5%88%8A.PNG',
-                link: 'https://sokrates.teammodel.cn/exhibition/tbavideo#/myChannel/858',
-                isShow: true
-            },
-            {
-                name: '《中国教育学刊》基础教育国家级教学成果推广会',
-                img: 'https://teammodelstorage.blob.core.chinacloudapi.cn/0-public/image/%E4%B8%AD%E5%9B%BD%E6%95%99%E8%82%B2%E5%AD%A6%E5%88%8A1.PNG',
-                link: 'https://sokrates.teammodel.cn/exhibition/tbavideo#/myChannel/837',
-                isShow: true
-            },
-            {
-                name: '智慧教学实验学校联盟(西南大学)',
-                img: 'https://teammodelstorage.blob.core.chinacloudapi.cn/0-public/image/%E8%A5%BF%E5%8D%97%E5%A4%A7%E5%AD%A6.PNG',
-                link: 'https://sokrates.teammodel.cn/district/JXLMPT#/',
-                isShow: host != 'jinniu.teammodel.cn'
-            },
-            {
-                name: '全球醍摩豆AI智慧学校联盟',
-                img: 'https://teammodelstorage.blob.core.chinacloudapi.cn/0-public/image/%E6%99%BA%E6%85%A7%E5%AD%A6%E6%A0%A1%E8%81%94%E7%9B%9F.PNG',
-                link: 'https://sokrates.teammodel.cn/exhibition/tbavideo#/myChannel/82',
-                isShow: host != 'jinniu.teammodel.cn'
-            },
-            {
-                name: '苏格拉底平台',
-                img: 'https://teammodelstorage.blob.core.chinacloudapi.cn/0-public/image/team-model.jpg',
-                link: 'https://sokrates.teammodel.cn/exhibition/tbavideo#/',
-                isShow: host != 'jinniu.teammodel.cn'
-            },
-            {
-                name: '国家公共资源教育平台',
-                img: 'https://teammodelstorage.blob.core.chinacloudapi.cn/0-public/image/%E5%85%AC%E5%85%B1%E6%9C%8D%E5%8A%A1%E5%B9%B3%E5%8F%B0.png',
-                link: 'https://www.eduyun.cn/',
-                isShow: true
-            },
-            {
-                name: '中国大学慕课MOOC',
-                img: 'https://teammodelstorage.blob.core.chinacloudapi.cn/0-public/image/MOOC.png',
-                link: 'https://www.icourse163.org/',
-                isShow: true
-            },
-            {
-                name: '政策解读',
-                img: 'https://teammodelstorage.blob.core.chinacloudapi.cn/0-public/image/%E6%94%BF%E7%AD%96%E8%A7%A3%E8%AF%BB1.jpg',
-                link: '/home/policy',
-                isShow: true
-            }
-        ]
-        this.videoListShow = this.videoList
-    }
-}
-</script>
-<style lang="less" scoped>
-.last-img {
-    background-size: contain !important;
-    background-position: center;
-}
-.literacy-container {
-    width: 100%;
-    height: 100%;
-    background: white;
-    padding: 15px;
-}
-.literacy-block-box {
-    // background: white;
-    width: 100%;
-    // min-height: 800px;
-    // padding: 10px;
-    display: flex;
-    flex-wrap: wrap;
-}
-.literacy-item {
-    width: 200px;
-    height: 200px;
-    background: #fcfcfc;
-    border: 1px solid #f0f0f0;
-    padding-bottom: 10px;
-    border-radius: 5px;
-    overflow: hidden;
-    margin-bottom: 30px;
-    margin-right: 30px;
-    cursor: pointer;
-    height: fit-content;
-    padding: 10px;
-    box-sizing: content-box;
-    transition: all 0.2s ease 0s;
-    .info-label {
-        color: #a5a5a5;
-    }
-    &:hover {
-        box-shadow: 0 26px 40px -24px #aaa;
-        transform: translateY(-4px);
-    }
-    .literacy-img {
-        width: 200px;
-        height: 200px;
-        background-size: cover;
-        background-repeat: no-repeat;
-        border-radius: 50%;
-    }
-    .info-item {
-        margin-top: 10px;
-        width: 100%;
-        padding-left: 5px;
-        font-size: 16px;
-        text-align: center;
-    }
-}
-.literacy-mgt-top {
-    width: ~"calc(100% - 20px)";
-    height: 40px;
-    box-shadow: 0px 2px 5px #e9e9e9;
-    margin-left: 10px;
-    padding-left: 10px;
-    padding-right: 20px;
-    padding-bottom: 10px;
-    line-height: 40px;
-}
-.literacy-search {
-    float: right;
-    margin-top: -52px;
-    margin-right: 20px;
-}
-</style>

+ 38 - 18
TEAMModelOS/ClientApp/src/view/student-account/class/ClassMgt.vue

@@ -161,13 +161,13 @@
                             </template>
                             <template slot-scope="{ row,index }" slot="no">
                                 <span v-show="editIndex !== index" :style="{color:row.no ? '#303030':'red'}">{{row.no || $t('cusMgt.notSet')}}</span>
-                                <InputNumber :min="1" v-special-char v-model="students[index].no" v-show="editIndex == index" style="width: 60px;" type="number" />
+                                <InputNumber :min="1" v-special-char v-model="editNo" v-show="editIndex == index" style="width: 60px;" type="number" />
                                 <Icon type="md-checkmark" v-show="editIndex == index" @click="confirmSetNo(students[index])" class="reset-no-btn" />
                                 <Icon type="md-close" v-show="editIndex == index" @click="cancelSetNo()" class="reset-no-btn" />
                             </template>
                             <template slot-scope="{ row,index }" slot="irs">
                                 <span v-show="editIndex !== index" :style="{color:row.irs ? '#303030':'red'}">{{row.irs || $t('cusMgt.notSet')}}</span>
-                                <InputNumber :min="1" v-special-char v-model="students[index].irs" v-show="editIndex == index" style="width: 60px;" type="number" />
+                                <InputNumber :min="1" v-special-char v-model="editIrs" v-show="editIndex == index" style="width: 60px;" type="number" />
                                 <Icon type="md-checkmark" v-show="editIndex == index" @click="confirmSetNo(students[index])" class="reset-no-btn" />
                                 <Icon type="md-close" v-show="editIndex == index" @click="cancelSetNo()" class="reset-no-btn" />
                             </template>
@@ -307,7 +307,9 @@ export default {
             callback()
         }
         return {
-            btnLoading:false,
+            editIrs: 1,
+            editNo: 1,
+            btnLoading: false,
             fastType: [],
             fastStatus: false,
             mLoading: true,
@@ -556,7 +558,7 @@ export default {
                 err => {
                     this.$Message.error(this.$t('common.generatErr'))
                 }
-            ).finally(()=>{
+            ).finally(() => {
                 this.btnLoading = false
             })
         },
@@ -605,31 +607,49 @@ export default {
         },
         resetNo(index) {
             this.editIndex = index
-            this.resetNoBef = this.students[index].no
-            this.resetIRSBef = this.students[index].irs
+            // this.resetNoBef = this.students[index].no
+            // this.resetIRSBef = this.students[index].irs
+            this.editIrs = this.students[index].irs ? parseInt(this.students[index].irs) : null
+            this.editNo = this.students[index].no ? parseInt(this.students[index].no) : null
         },
         confirmSetNo(row) {
+            if (!this.editIrs) {
+                this.$Message.warning(this.$t('schoolBaseInfo.setIrsWarning'))
+                return
+            }
+            if (!this.editNo) {
+                this.$Message.warning(this.$t('schoolBaseInfo.setNoWarning'))
+                return
+            }
             //检查irs重复
-            let irsList = this.students.map(item => item.irs)
-            let irsRep = irsList.some((item, index) => {
-                return !!item && irsList.indexOf(item) != index
+            let isRepeat = this.students.some((item, index) => {
+                return index != this.editIndex && item.irs == this.editIrs
             })
-            if (irsRep) {
+            // let irsList = this.students.map(item => item.irs)
+            // let irsRep = irsList.some((item, index) => {
+            //     return !!item && irsList.indexOf(item) != index
+            // })
+            if (isRepeat) {
                 this.$Message.warning(this.$t('schoolBaseInfo.irsRep'))
                 return
             }
             // 检查no重复
-            let noList = this.students.map(item => item.no)
-            let noRep = noList.some((item, index) => {
-                return !!item && noList.indexOf(item) != index
+            let noRepeat = this.students.some((item, index) => {
+                return index != this.editIndex && item.no == this.editNo
             })
-            if (noRep) {
+            // let noList = this.students.map(item => item.no)
+            // let noRep = noList.some((item, index) => {
+            //     return !!item && noList.indexOf(item) != index
+            // })
+            if (noRepeat) {
                 this.$Message.warning(this.$t('schoolBaseInfo.noRep'))
                 return
             }
-            this.editIndex = -1
+            this.$set(this.students[this.editIndex], 'irs', this.editIrs + '')
+            this.$set(this.students[this.editIndex], 'no', this.editNo + '')
             let schoolId = this.$store.state.userInfo.schoolCode
-            let data = [row]
+            let data = [this.students[this.editIndex]]
+            this.editIndex = -1
             this.$api.stuAccount.updStudent(schoolId, data).then(
                 res => {
                     this.$Message.success(this.$t('schoolBaseInfo.updOk'))
@@ -640,8 +660,8 @@ export default {
             )
         },
         cancelSetNo() {
-            this.students[this.editIndex].no = this.resetNoBef
-            this.students[this.editIndex].irs = this.resetIRSBef
+            // this.students[this.editIndex].no = this.resetNoBef
+            // this.students[this.editIndex].irs = this.resetIRSBef
             this.editIndex = -1
         },
         //移除班级里面的学生

+ 2 - 2
TEAMModelOS/ClientApp/src/view/user/UserCenter.vue

@@ -308,7 +308,7 @@ export default {
         submit() {
             let srvAdr = this.$store.state.config.srvAdr
             let host = srvAdr == 'Global' ? this.$store.state.config.Global.coreAPIUrl : this.$store.state.config.China.coreAPIUrl
-            let clientId = srvAdr == 'Global' ? this.$store.state.config.Gloabl.clientID : this.$store.state.config.China.clientID
+            let clientId = srvAdr == 'Global' ? this.$store.state.config.Global.clientID : this.$store.state.config.China.clientID
             let idToken = localStorage.getItem('id_token')
             let tokenData = jwtDecode(idToken)
             let nonce = tokenData ? tokenData.nonce : ''
@@ -449,7 +449,7 @@ export default {
                 //更新头像
                 let srvAdr = _this.$store.state.config.srvAdr
                 let host = srvAdr == 'Global' ? _this.$store.state.config.Global.coreAPIUrl : _this.$store.state.config.China.coreAPIUrl
-                let clientId = srvAdr == 'Global' ? _this.$store.state.config.Gloabl.clientID : _this.$store.state.config.China.clientID
+                let clientId = srvAdr == 'Global' ? _this.$store.state.config.Global.clientID : _this.$store.state.config.China.clientID
                 let idToken = localStorage.getItem('id_token')
                 let tokenData = jwtDecode(idToken)
                 let nonce = tokenData ? tokenData.nonce : ''

+ 46 - 2
TEAMModelOS/ClientApp/vue.config.js

@@ -1,5 +1,8 @@
 const path = require('path')
 const Timestamp = new Date().getTime();
+// const CompressionPlugin = require("compression-webpack-plugin")
+// const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
+
 
 function resolve(dir) {
 	return path.join(__dirname, './', dir)
@@ -9,7 +12,7 @@ module.exports = {
 	outputDir: '../wwwroot',
 	//lintOnSave: process.env.NODE_ENV !== 'production',
 	lintOnSave: false,
-	productionSourceMap:false,  // 设置上线后是否加载webpack文件
+	productionSourceMap: false, // 设置上线后是否加载webpack文件
 	// filenameHashing:false,
 	pages: {
 		app: {
@@ -56,10 +59,51 @@ module.exports = {
 		}
 	},
 	configureWebpack: config => {
-		config.optimization.minimizer[0].options.terserOptions.compress.drop_console = process.env.NODE_ENV === 'production'
+		config.optimization.minimizer[0].options.terserOptions.compress.drop_console = process.env.NODE_ENV ===
+			'production'
 		// config.entry.app = ["babel-polyfill", "./src/main.js"];
 		config.output.filename = `js/[name].${Timestamp}.js`
 		config.output.chunkFilename = `js/[name].${Timestamp}.js`
+		// config.plugins.push(
+		// 	new CompressionPlugin({
+		// 		test: /\.js$|\.html$|\.css$|\.jpg$|\.jpeg$|\.png/, // 需要压缩的文件类型
+		// 		threshold: 10240, // 归档需要进行压缩的文件大小最小值,我这个是10K以上的进行压缩
+		// 		deleteOriginalAssets: false, // 是否删除原文件
+		// 		minRatio: 0.8
+		// 	})
+		// )
+		// config.plugins = [
+		// 	new BundleAnalyzerPlugin({
+		// 		//  可以是`server`,`static`或`disabled`。
+		// 		//  在`server`模式下,分析器将启动HTTP服务器来显示软件包报告。
+		// 		//  在“静态”模式下,会生成带有报告的单个HTML文件。
+		// 		//  在`disabled`模式下,你可以使用这个插件来将`generateStatsFile`设置为`true`来生成Webpack Stats JSON文件。
+		// 		analyzerMode: 'server',
+		// 		//  将在“服务器”模式下使用的主机启动HTTP服务器。
+		// 		analyzerHost: '127.0.0.1',
+		// 		//  将在“服务器”模式下使用的端口启动HTTP服务器。
+		// 		analyzerPort: 8888,
+		// 		//  路径捆绑,将在`static`模式下生成的报告文件。
+		// 		//  相对于捆绑输出目录。
+		// 		reportFilename: 'report.html',
+		// 		//  模块大小默认显示在报告中。
+		// 		//  应该是`stat`,`parsed`或者`gzip`中的一个。
+		// 		//  有关更多信息,请参见“定义”一节。
+		// 		defaultSizes: 'parsed',
+		// 		//  在默认浏览器中自动打开报告
+		// 		openAnalyzer: true,
+		// 		//  如果为true,则Webpack Stats JSON文件将在bundle输出目录中生成
+		// 		generateStatsFile: false,
+		// 		//  如果`generateStatsFile`为`true`,将会生成Webpack Stats JSON文件的名字。
+		// 		//  相对于捆绑输出目录。
+		// 		statsFilename: 'stats.json',
+		// 		//  stats.toJson()方法的选项。
+		// 		//  例如,您可以使用`source:false`选项排除统计文件中模块的来源。
+		// 		//  在这里查看更多选项:https:  //github.com/webpack/webpack/blob/webpack-1/lib/Stats.js#L21
+		// 		statsOptions: null,
+		// 		logLevel: 'info' // 日志级别。可以是'信息','警告','错误'或'沉默'。
+		// 	})
+		// ]
 	},
 	// configureWebpack: {
 	// 	output: { // 输出重构  打包编译后的 文件名称  【模块名称.版本号.js】

+ 12 - 0
TEAMModelOS/Controllers/Common/LessonRecordController.cs

@@ -33,6 +33,8 @@ namespace TEAMModelOS.Controllers
     
     public class LessonRecordController : ControllerBase
     {
+
+        public static readonly DateTime dateTime1970= new DateTime(1970,1,1).ToLocalTime();
         private readonly AzureCosmosFactory _azureCosmos;
         private readonly SnowflakeId _snowflakeId;
         private readonly AzureServiceBusFactory _serviceBus;
@@ -270,6 +272,16 @@ namespace TEAMModelOS.Controllers
             {
                 dict.Add("$.name", name);
             }
+
+            if (request.TryGetProperty("today", out JsonElement today) && today.GetBoolean())
+            {
+                DateTime dateTimeA= Convert.ToDateTime(DateTimeOffset.UtcNow.ToString("D"));
+                DateTime dateTimeB = Convert.ToDateTime(DateTimeOffset.UtcNow.ToString("D")).AddDays(1);
+                double dayOf00_00_00= (dateTimeA - dateTime1970).TotalMilliseconds;
+                double day1Of00_00_00 = (dateTimeB - dateTime1970).TotalMilliseconds;
+                dict.Add(">=.startTime", dayOf00_00_00);
+                dict.Add("<.startTime", day1Of00_00_00);
+            }
             return dict;
         }
     }

+ 24 - 15
TEAMModelOS/Controllers/Research/AbilitySubController.cs

@@ -118,7 +118,7 @@ namespace TEAMModelOS.Controllers
             }
             catch (Exception ex)
             {
-                await _dingDing.SendBotMsg($"OS,{_option.Location},AbilityController/SaveSubs()\n{ex.Message}", GroupNames.醍摩豆服務運維群組);
+                await _dingDing.SendBotMsg($"OS,{_option.Location},AbilityController/SaveSubs()\n{ex.Message}", GroupNames.成都开发測試群組);
                 return BadRequest();
             }
         }
@@ -834,27 +834,28 @@ namespace TEAMModelOS.Controllers
                         try
                         {
                             teacherFile = await client.GetContainer("TEAMModelOS", "Teacher").ReadItemAsync<TeacherFile>($"{_tmdid}", new PartitionKey($"TeacherFile-{_school}"));
-                            fileRcds.ForEach(fileRcd => {
+                            fileRcds.ForEach(fileRcd =>
+                            {
                                 var file = teacherFile.fileRecords.Find(x => x.hash.Equals(fileRcd.hash));
                                 if (file != null)
                                 {
                                     file.hash = fileRcd.hash;
                                     file.view = fileRcd.view;
                                     file.done = fileRcd.done;
-                                    FileAbility ability = file.files.Find(x => x.abilityId.Equals(fileRcd.abilityId) 
-                                    && x.url.Equals(fileRcd.url)&& x.taskId.Equals(fileRcd.taskId) 
-                                    && x.nodeId.Equals(fileRcd.nodeId));
+                                    FileAbility ability = file.files.Find(x => x.abilityId.Equals(fileRcd.abilityId)
+                                    && x.url.Equals(fileRcd.url) && x.taskId.Equals(fileRcd.taskId)
+                                    && x.nodeId.Equals(fileRcd?.nodeId));
                                     if (ability != null)
                                     {
                                         ability.url = fileRcd.url;
                                         ability.abilityId = fileRcd.abilityId;
                                         ability.taskId = fileRcd.taskId;
                                         ability.nodeId = fileRcd.nodeId;
-                                       
+
                                     }
                                     else
                                     {
-                                        file.files.Add(new FileAbility { url = fileRcd.url, abilityId = fileRcd.abilityId, taskId = fileRcd.taskId, nodeId = fileRcd.nodeId,  });
+                                        file.files.Add(new FileAbility { url = fileRcd.url, abilityId = fileRcd.abilityId, taskId = fileRcd.taskId, nodeId = fileRcd.nodeId, });
                                     }
                                 }
                                 else
@@ -866,8 +867,8 @@ namespace TEAMModelOS.Controllers
                                         duration = fileRcd.duration,
                                         view = fileRcd.view,
                                         type = fileRcd.type,
-                                      done = fileRcd.done,
-                                    files = new List<FileAbility> { new FileAbility { url = fileRcd.url, abilityId = fileRcd.abilityId, taskId = fileRcd.taskId, nodeId = fileRcd.nodeId,  } }
+                                        done = fileRcd.done,
+                                        files = new List<FileAbility> { new FileAbility { url = fileRcd.url, abilityId = fileRcd.abilityId, taskId = fileRcd.taskId, nodeId = fileRcd.nodeId, } }
                                     });
                                 }
                             });
@@ -875,15 +876,16 @@ namespace TEAMModelOS.Controllers
                         }
                         catch (CosmosException)
                         {
-                            fileRcds.ForEach(fileRcd => {
+                            fileRcds.ForEach(fileRcd =>
+                            {
                                 teacherFile = new TeacherFile
                                 {
                                     pk = "TeacherFile",
                                     id = $"{_tmdid}",
                                     code = $"TeacherFile-{_school}",
-                                    fileRecords = new List<FileRecord> 
+                                    fileRecords = new List<FileRecord>
                                     {
-                                        new FileRecord 
+                                        new FileRecord
                                         {
                                             hash= fileRcd.hash,
                                             size=fileRcd.size,
@@ -892,12 +894,15 @@ namespace TEAMModelOS.Controllers
                                             type=fileRcd.type,
                                             done=fileRcd.done,
                                             files= new List<FileAbility> { new FileAbility { url=fileRcd.url,abilityId=fileRcd.abilityId,taskId=fileRcd.taskId,nodeId=fileRcd.nodeId} }
-                                        } 
+                                        }
                                     },
                                 };
                             });
                             await client.GetContainer("TEAMModelOS", "Teacher").CreateItemAsync<TeacherFile>(teacherFile, new PartitionKey($"TeacherFile-{_school}"));
                         }
+                        catch (Exception ex ){
+                            return BadRequest();
+                        }
                         if (fileRcds.Exists(x => x.type.Equals("video"))) {
                             await StatisticsService.SendServiceBus( ($"{standard}",new List<string> { $"{_tmdid}" }, $"{_school}", new List<string> { StatisticsService.TeacherAbility },0) , _configuration, _serviceBus, client);
                         }
@@ -916,7 +921,7 @@ namespace TEAMModelOS.Controllers
                         }
                         catch (Exception ex)
                         {
-
+                            return BadRequest(ex);
                         }
                         return Ok(new { status, files=_files });
                     case "ReadSelfVideoRcd":
@@ -938,7 +943,7 @@ namespace TEAMModelOS.Controllers
 
                             foreach (var abid in abilityIds) {
                                var record= teacherFilercd.fileRecords.FindAll(x => x.files.Where(y => y.abilityId.Equals(abid)).Count() > 0);
-                                long  view = 0;
+                                double  view = 0;
                                 List<dynamic> urls = new List<dynamic>();
                                 record.ForEach(x => {
                                     var file= x.files.FindAll(y => y.abilityId.Equals($"{abid}"));
@@ -965,7 +970,11 @@ namespace TEAMModelOS.Controllers
             }
             catch (Exception ex)
             {
+#if DEBUG
                 await _dingDing.SendBotMsg($"OS,{_option.Location},AbilityController/SubOpt()\n{ex.Message}{ex.StackTrace}{request.ToJsonString()}", GroupNames.醍摩豆服務運維群組);
+#else
+
+#endif
                 return Ok(new { status = -1 });
             }
         } /// <summary>

+ 83 - 43
TEAMModelOS/Controllers/School/ClassController.cs

@@ -23,7 +23,7 @@ namespace TEAMModelOS.Controllers
 {
     [ProducesResponseType(StatusCodes.Status200OK)]
     [ProducesResponseType(StatusCodes.Status400BadRequest)]
-    
+
     [Route("school/classroom")]
     [ApiController]
     public class ClassController : ControllerBase
@@ -47,7 +47,7 @@ namespace TEAMModelOS.Controllers
             try
             {
                 if (!requert.TryGetProperty("classroom", out JsonElement room)) return BadRequest();
-                Class  classroom = room.ToObject<Class>();
+                Class classroom = room.ToObject<Class>();
                 var client = _azureCosmos.GetCosmosClient();
                 classroom.code = "Class-" + classroom.school;
                 if (string.IsNullOrEmpty(classroom.id))
@@ -73,7 +73,8 @@ namespace TEAMModelOS.Controllers
                     classroom.id = Guid.NewGuid().ToString();
                     classroom = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "School").CreateItemAsync(classroom, new PartitionKey(classroom.code));
                 }
-                else {
+                else
+                {
                     var response = await client.GetContainer(Constant.TEAMModelOS, "School").ReadItemStreamAsync(classroom.id, new PartitionKey(classroom.code));
                     if (response.Status == 200)
                     {
@@ -214,48 +215,87 @@ namespace TEAMModelOS.Controllers
                 string school_code = code.ToString();
                 Class classroom = new Class();
                 var client = _azureCosmos.GetCosmosClient();
-                    classroom = await client.GetContainer(Constant.TEAMModelOS, "School").DeleteItemAsync<Class>(id.ToString(), new PartitionKey($"Class-{school_code}"));
-                    //List<TeacherCourse> classes = new List<TeacherCourse>();
+                classroom = await client.GetContainer(Constant.TEAMModelOS, "School").DeleteItemAsync<Class>(id.ToString(), new PartitionKey($"Class-{school_code}"));
+                //List<TeacherCourse> classes = new List<TeacherCourse>();
 
-                    //await client.GetContainer(Constant.TEAMModelOS, "School").DeleteItemStreamAsync(id.ToString(), new PartitionKey($"CourseManagement-{school_code}"));
-                    //await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "Teacher").GetItemQueryStreamIterator(queryText: $"select value(c) from c join A0 in c.classes where A0.id = '{id}'"))
-                    //{
-                    //    using var json = await JsonDocument.ParseAsync(item.ContentStream);
-                    //    if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
-                    //    {
-                    //        foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
-                    //        {
-                    //            classes.Add(obj.ToObject<TeacherCourse>());
-                    //        }
-                    //    }
-                    //}
-                    //for (int i = 0; i < classes.Count; i++)
-                    //{
-                    //    bool flag = false;
-                    //    for (int j = 0; j < classes[i].classes.Count; j++)
-                    //    {
-                    //        if (classes[i].classes[j].id.Equals(id.ToString()))
-                    //        {
-                    //            classes[i].classes.Remove(classes[i].classes[j]);
-                    //            flag = true;
-                    //        }
-                    //    }
-                    //    if (flag)
-                    //    {
-                    //        await client.GetContainer(Constant.TEAMModelOS, "Teacher").ReplaceItemAsync(classes[i], classes[i].id, new PartitionKey($"{classes[i].code}"));
-                    //    }
-                    //}
-                    List<Student> students = new List<Student>();
-                    await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "Student").GetItemQueryIterator<Student>(queryText: $"select *  from c where c.classId='{id}' ", requestOptions:new QueryRequestOptions { PartitionKey= new PartitionKey ($"Base-{code}") }))
+                //await client.GetContainer(Constant.TEAMModelOS, "School").DeleteItemStreamAsync(id.ToString(), new PartitionKey($"CourseManagement-{school_code}"));
+                //await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "Teacher").GetItemQueryStreamIterator(queryText: $"select value(c) from c join A0 in c.classes where A0.id = '{id}'"))
+                //{
+                //    using var json = await JsonDocument.ParseAsync(item.ContentStream);
+                //    if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
+                //    {
+                //        foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
+                //        {
+                //            classes.Add(obj.ToObject<TeacherCourse>());
+                //        }
+                //    }
+                //}
+                //for (int i = 0; i < classes.Count; i++)
+                //{
+                //    bool flag = false;
+                //    for (int j = 0; j < classes[i].classes.Count; j++)
+                //    {
+                //        if (classes[i].classes[j].id.Equals(id.ToString()))
+                //        {
+                //            classes[i].classes.Remove(classes[i].classes[j]);
+                //            flag = true;
+                //        }
+                //    }
+                //    if (flag)
+                //    {
+                //        await client.GetContainer(Constant.TEAMModelOS, "Teacher").ReplaceItemAsync(classes[i], classes[i].id, new PartitionKey($"{classes[i].code}"));
+                //    }
+                //}
+                List<Student> students = new List<Student>();
+                await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "Student").GetItemQueryIterator<Student>(queryText: $"select *  from c where c.classId='{id}' ", requestOptions: new QueryRequestOptions { PartitionKey = new PartitionKey($"Base-{code}") }))
+                {
+                    item.classId = "";
+                    students.Add(item);
+                }
+                List<Task<ItemResponse<Student>>> tasks = new List<Task<ItemResponse<Student>>>();
+                foreach (var stu in students)
+                {
+                    tasks.Add(client.GetContainer(Constant.TEAMModelOS, "Student").ReplaceItemAsync(stu, stu.id, new PartitionKey(($"Base-{code}"))));
+                }
+                //查询含有该班级的课程ID
+                List<string> cids = new List<string>();
+                await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "School").GetItemQueryStreamIterator(
+                    queryText: $"select distinct c.id from c join A0 in c.schedule where A0.classId = '{id}'",
+                    requestOptions: new QueryRequestOptions { PartitionKey = new PartitionKey($"Course-{code}") }))
+                {
+                    using var json = await JsonDocument.ParseAsync(item.ContentStream);
+                    if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
                     {
-                        item.classId = "";
-                        students.Add(item);
+                        var accounts = json.RootElement.GetProperty("Documents").EnumerateArray();
+                        while (accounts.MoveNext())
+                        {
+                            JsonElement account = accounts.Current;
+                            cids.Add(account.GetProperty("id").GetString());
+                        }
                     }
-                    List<Task<ItemResponse<Student>>> tasks = new List<Task<ItemResponse<Student>>>();
-                    foreach (var stu in students) {
-                        tasks.Add(client.GetContainer(Constant.TEAMModelOS, "Student").ReplaceItemAsync(stu, stu.id, new PartitionKey(($"Base-{code}"))));
+                }
+                List<Task<ItemResponse<Course>>> taskCourses = new List<Task<ItemResponse<Course>>>();
+                List<Course> courses = new List<Course>();
+                await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "School").GetItemQueryIterator<Course>(
+                    queryText: $"select value(c) from c where c.id in ({string.Join(",", cids.Select(o => $"'{o}'"))})",
+                    requestOptions: new QueryRequestOptions { PartitionKey = new PartitionKey($"Course-{code}") }))
+                {
+                    courses.Add(item);
+                }
+                foreach (Course course in courses)
+                {
+                    foreach (var sc in course.schedule)
+                    {
+                        if (sc.classId.Equals(id))
+                        {
+                            sc.classId = "";
+                        }
                     }
-                    await Task.WhenAll(tasks);
+                    taskCourses.Add(client.GetContainer(Constant.TEAMModelOS, "School").ReplaceItemAsync(course, course.id, new PartitionKey((course.code))));
+                }
+
+                await Task.WhenAll(tasks);
+                await Task.WhenAll(taskCourses);
                 //}
                 return Ok(new { classroom });
             }
@@ -312,7 +352,7 @@ namespace TEAMModelOS.Controllers
                         deviceBound updDeviceBound = updSerialInfo.deviceBound.Where(d => d.uuid == uuid && d.uuid2 == uuid2).FirstOrDefault();
                         if (updDeviceBound != null)
                         {
-                            if (action .Equals("link"))
+                            if (action.Equals("link"))
                             {
                                 if (updDeviceBound.classId != null)
                                 {
@@ -323,7 +363,7 @@ namespace TEAMModelOS.Controllers
                                     updDeviceBound.classId = classId;
                                 }
                             }
-                            else if (action .Equals("unLink"))
+                            else if (action.Equals("unLink"))
                             {
                                 updDeviceBound.classId = null;
                             }

+ 10 - 24
TEAMModelOS/Controllers/School/StudentController.cs

@@ -1966,7 +1966,16 @@ namespace TEAMModelOS.Controllers
                             //有給pw欄位才進行處理
                             if (student.TryGetProperty("pw", out var tmpPw))
                             {
-                                salt = Utils.CreatSaltString(8);
+                                var response = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS,"Student").ReadItemStreamAsync(id.GetString(), new PartitionKey($"Base-{schoolId}"));
+                                if (response.Status == 200)
+                                {
+                                    var rjson = await JsonDocument.ParseAsync(response.ContentStream);
+                                    rjson.RootElement.TryGetProperty("salt", out JsonElement _salt);
+                                    salt = $"{_salt}";
+                                }
+                                if (string.IsNullOrWhiteSpace(salt)) {
+                                    salt = Utils.CreatSaltString(8);
+                                }
                                 pw = !string.IsNullOrWhiteSpace(tmpPw.GetString())
                                     ? Utils.HashedPassword(tmpPw.GetString(), salt)
                                     : Utils.HashedPassword(id.GetString(), salt);
@@ -2234,27 +2243,6 @@ namespace TEAMModelOS.Controllers
                                                 }
                                                 else
                                                 {
-                                                    //如果要更新的座號,跟已存在的座號相同,則不進行更新。
-                                                    //沒有設定過舊no,或舊座號與新座號不同,則要進行重複座號的檢查。 舊no=null or 舊no!=新no
-
-
-                                                    //if (string.IsNullOrWhiteSpace(irs) || (!string.IsNullOrWhiteSpace(irs) && !irs.Equals(studentInfos[id].irs)))
-                                                    //{
-                                                    //    //如果有檢查到新座號和舊座號重複
-                                                    //    if (existNos.Any(o => o.Item2.Contains(studentInfos[id].irs))) //.Contains(studentInfos[id].no))
-                                                    //    {
-                                                    //        if (errorNos.ContainsKey(id))
-                                                    //        {
-                                                    //            errorNos[id].Add(studentInfos[id].no);
-                                                    //        }
-                                                    //        else
-                                                    //        {
-                                                    //            errorNos.Add(id, new List<string>() { studentInfos[id].no });
-                                                    //        }
-                                                    //        isWrong = true;
-                                                    //        break;
-                                                    //    }
-                                                    //}
                                                     writer.WriteString("irs", studentInfos[id].irs);
                                                 }
                                                 break;
@@ -2264,9 +2252,7 @@ namespace TEAMModelOS.Controllers
                                                 element.WriteTo(writer);
                                                 break;
                                         }
-                                        writer.WriteString("irs", studentInfos[id].irs);
                                     }
-
                                     //如果有錯誤,如座號重覆等,就會跳過該次更新。
                                     if (isWrong)
                                     {

+ 4 - 1
TEAMModelOS/Controllers/School/TmdUserController.cs

@@ -137,7 +137,10 @@ namespace TEAMModelOS.Controllers
                 if (!string.IsNullOrEmpty(defaultschool)) { 
 
                 }
-                return Ok(new { location=_option.Location ,auth_token,  schools, defaultschool , status = 200 });
+                var (blob_uri, blob_sas) = _azureStorage.GetBlobContainerSAS(id, BlobContainerSasPermissions.Write | BlobContainerSasPermissions.Read | BlobContainerSasPermissions.List);
+                blob_uri = blob_uri.Replace(id, "");
+                blob_sas = "";
+                return Ok(new { location=_option.Location ,auth_token,  schools, defaultschool , blob_uri, blob_sas, status = 200 });
 
             }
             catch (CosmosException ex)

+ 3 - 3
TEAMModelOS/TEAMModelOS.csproj

@@ -39,9 +39,9 @@
     <SpaRoot>ClientApp\</SpaRoot>
     <DefaultItemExcludes>$(DefaultItemExcludes);$(SpaRoot)node_modules\**</DefaultItemExcludes>
     <UserSecretsId>078b5d89-7d90-4f6a-88fc-7d96025990a8</UserSecretsId>
-    <Version>5.2201.4</Version>
-    <AssemblyVersion>5.2201.4.1</AssemblyVersion>
-    <FileVersion>5.2201.4.1</FileVersion>
+    <Version>5.2201.6</Version>
+    <AssemblyVersion>5.2201.6.1</AssemblyVersion>
+    <FileVersion>5.2201.6.1</FileVersion>
     <Description>TEAMModelOS(IES5)版本更新。</Description>
     <PackageReleaseNotes>版本说明</PackageReleaseNotes>
   </PropertyGroup>