chenmy 3 gadi atpakaļ
vecāks
revīzija
8b5a86a5ab

+ 2 - 0
TEAMModelBI/ClientApp/package.json

@@ -22,9 +22,11 @@
         "jwt-decode": "^3.1.2",
         "less": "^4.1.2",
         "qs": "^6.10.1",
+        "splitpanes": "^3.1.1",
         "vue": "^3.2.0",
         "vue-loader-v16": "npm:vue-loader@^16.0.0-alpha.3",
         "vue-router": "^4.0.0-rc.5",
+        "vue3-count-to": "^1.1.2",
         "xlsx": "^0.17.4"
     },
     "devDependencies": {

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

@@ -11,7 +11,7 @@
     </title>
 </head>
 <script src="https://g.alicdn.com/dingding/dinglogin/0.0.5/ddLogin.js"></script>
-<script src="https://at.alicdn.com/t/font_2934132_i6b6k27scg.js"></script>
+<script src="https://at.alicdn.com/t/font_2934132_wrb3jmqumwr.js"></script>
 
 <body>
     <noscript>

+ 4 - 0
TEAMModelBI/ClientApp/src/api/index.js

@@ -132,6 +132,10 @@ export default {
     getschoolAuthorization(data) {
         return post('/product/get-school', data)
     },
+    //绑定和解绑教室序列号
+    operateClassroom(data) {
+        return post('/schoolroom/set-bind', data)
+    },
 
     //首页dashboard数据接口
     //获取各城市的学校数量(bar)

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

@@ -13,8 +13,15 @@ export default createStore({
             "sas": '',
         },
         createModel: '',
-        changbtnShow: false
+        changbtnShow: false,
+        schoolPower: {}
     },
+    // getters: {
+    //     //获取权限API返回的serials
+    //     getserials: state => {
+    //         return state.schoolPower.serials
+    //     }
+    // },
     mutations: {
         //修改组织架构
         ChangOrganization(state, data) {
@@ -53,6 +60,10 @@ export default createStore({
         //改变存储变更按钮显示
         changeShowbtn(state, value) {
             state.changbtnShow = value
+        },
+        //当前选中学校的权限内容
+        updateSchoolpower(state, value) {
+            state.schoolPower = value
         }
     },
     actions: {},

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

@@ -72,5 +72,18 @@ export default {
             }
         });
         return totaldays
+    },
+    timestampToTime(timestamp) {
+        var date = new Date(Number(timestamp) * 1000); //时间戳为10位需*1000,时间戳为13位的话不需乘1000
+        var Y = date.getFullYear() + '-';
+        var M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1) + '-';
+        var D = (date.getDate() < 10 ? '0' + date.getDate() : date.getDate()) + ' ';
+        // var h = (date.getHours() < 10 ? '0'+date.getHours() : date.getHours()) + ':';
+        // var m = (date.getMinutes() < 10 ? '0'+date.getMinutes() : date.getMinutes()) + ':';
+        // var s = (date.getSeconds() < 10 ? '0'+date.getSeconds() : date.getSeconds());
+
+        var strDate = Y + M + D;
+        return strDate; //2020-07-30 01:05:54
+
     }
 }

+ 567 - 0
TEAMModelBI/ClientApp/src/view/teachermanage/classpower.vue

@@ -0,0 +1,567 @@
+<template>
+    <div class="classpower">
+        <div class="classpower-top">
+            <div class="alonebox">
+                <p>HiTeach授权数</p>
+                <div class="alonebox-content">
+                    <span class="alonebox-content-type">大量</span>
+                    <span class="alonebox-content-name">授权</span>
+                    <span class="alonebox-content-num">
+                        <countTo :startVal='topData.startVal' :endVal='topData.largeNum.length' :duration='1000'></countTo>
+                    </span>
+                </div>
+            </div>
+            <div class="alonebox second">
+                <p></p>
+                <div class="alonebox-content">
+                    <span class="alonebox-content-type">单一</span>
+                    <span class="alonebox-content-name">授权</span>
+                    <span class="alonebox-content-num">
+                        <countTo :startVal='topData.startVal' :endVal='topData.singlenessNum.length' :duration='1000'></countTo>
+                    </span>
+                </div>
+            </div>
+            <div class="alonebox">
+                <p>可启用装置</p>
+                <div class="alonebox-content">
+                    <div class="alonebox-content-numtext">
+                        <countTo :startVal='topData.startVal' :endVal='topData.availableNum.length' :duration='1000'></countTo>
+                    </div>
+                </div>
+            </div>
+            <div class="alonebox">
+                <p>已启用装置</p>
+                <div class="alonebox-content">
+                    <div class="alonebox-content-numtext">
+                        <countTo :startVal='topData.startVal' :endVal='topData.enableNum.length' :duration='1000'></countTo>
+                    </div>
+                </div>
+            </div>
+        </div>
+        <div class="stretchbox">
+            <splitpanes class="default-theme" style="height: 600px">
+                <pane size="25" min-size="15" max-size="80">
+                    <div class="left-serialnum">
+                        <div class="left-serialnum-top">
+                            <span class="left-serialnum-top-title">序列号:</span>
+                            <el-dropdown>
+                                <span class="el-dropdown-link">
+                                    {{selectList.value}}
+                                    <svg class="unfoldicon" aria-hidden="true">
+                                        <use xlink:href="#icon-xuanzeqizhankai"></use>
+                                    </svg>
+                                </span>
+                                <template #dropdown>
+                                    <el-dropdown-menu>
+                                        <el-dropdown-item v-for="items in selectList.list" @click="filtrateData(items.value,items.name)">{{items.name}}</el-dropdown-item>
+                                        <!-- <el-dropdown-item>单一授权</el-dropdown-item>
+                                        <el-dropdown-item>大量授权</el-dropdown-item>
+                                        <el-dropdown-item>可使用</el-dropdown-item>
+                                        <el-dropdown-item>已到期</el-dropdown-item> -->
+                                    </el-dropdown-menu>
+                                </template>
+                            </el-dropdown>
+                        </div>
+                        <div class="serialnumList">
+                            <div class="serialnumList-item" v-for="(item,index) in powerList" :key="index" :class="{'active':pithCode.pithState===index}">
+                                <p><span :class="item.deviceMax > 1 ? 'serialnumList-item-tag':'serialnumList-item-taginfo'">{{item.typename}}</span><span class="serialnumList-item-num">{{item.serial}}</span></p>
+                                <div class="serialnumList-item-details">
+                                    <span class="details-endtime-title">到期日:</span><span class="details-endtime-content" :class="!item.state ? 'expiretext':''">{{item.etime}}</span><span class="expire" v-show="item.state===false">(已到期)</span>
+                                    <span class="details-use-title">使用情况:</span><span class="details-use-content">{{item.usedata}}/{{item.deviceMax}}</span>
+                                </div>
+                                <div class="serialnumList-item-details secondinfo">
+                                    <span>苏格拉底议课</span>
+                                    <span>远距教室服务</span>
+                                    <span>录播系统</span>
+                                </div>
+                                <div class="pitchon">
+                                    <span class="pitchon-affirm" v-if="pithCode.pithState ===-1 && item.state===true" @click="pitcheEcod(item,index)">选择</span>
+                                    <span class="pitchon-close" v-if="item.pitch ===true" @click="pithCode.pithState=-1,item.pitch=false">取消</span>
+                                    <span class="pitchon-confirmed" v-if="item.pitch ===true">已选择</span>
+                                </div>
+                            </div>
+                        </div>
+                    </div>
+                </pane>
+                <pane size="75" min-size="10" max-size="85">
+                    <div class="classgradeList">
+                        <div class="classgradeList-list">
+                            <div class="classgradeList-item" v-for="(item,index) in classRoom" :key="item.id" :class="item.serial !==null ? 'alreadyBind':''">
+                                <p class="classgradeList-item-name" :class="item.serial !==null ? 'alreadyBindc':''">{{item.name}}</p>
+                                <p class="classgradeList-item-type" v-if="item.openType==='1' ">常规教室</p>
+                                <p class="classgradeList-item-type" v-else-if="item.openType ==='2'">专设教室</p>
+                                <p class="classgradeList-item-bind" v-if="item.serial ===null">未绑定</p>
+                                <p class="classgradeList-item-alreadybind" v-else-if="item.serial !==null">{{item.serial}}</p>
+                                <div class="classgradeList-item-hover">
+                                    <span v-if="pithCode.pithState ===-1 && item.serial===null">请选择序列号</span>
+                                    <span v-if="pithCode.pithState !==-1 && pithCode.present && item.serial ===null" @click="bindClass(item,index)">绑定</span>
+                                    <span v-if="item.serial !== null" @click="relieveBind(item,index)">解绑</span>
+                                </div>
+                            </div>
+                        </div>
+                    </div>
+                </pane>
+            </splitpanes>
+        </div>
+    </div>
+</template>
+<script>
+import { ref, getCurrentInstance, watch, computed } from 'vue'
+import { CountTo } from 'vue3-count-to'
+import { Splitpanes, Pane } from 'splitpanes'
+import 'splitpanes/dist/splitpanes.css'
+import { useStore } from 'vuex'
+import { toRaw } from '@vue/reactivity'
+import { ElMessage, ElMessageBox } from 'element-plus'
+export default {
+    components: {
+        CountTo,
+        Splitpanes,
+        Pane,
+    },
+    props: {
+        schoolCode: {
+            type: String,
+            default: '',
+        },
+    },
+    setup(props) {
+        let { proxy } = getCurrentInstance()
+        const store = useStore()
+        let topData = ref({
+            startVal: 0,
+            largeNum: 15,
+            singlenessNum: 66,
+            availableNum: 158,
+            enableNum: 13,
+        })
+        //选中的识别码
+        let pithCode = ref({
+            pithState: -1,
+            present: {},
+        })
+        let powerList = ref()
+        let originalData = ref()
+        let classRoom = ref()
+        let selectList = ref({
+            value: '显示全部',
+            list: [
+                { id: 1, name: '显示全部', value: 'all' },
+                { id: 2, name: '单一授权', value: 'sole' },
+                { id: 3, name: '大量授权', value: 'large' },
+                { id: 4, name: '可使用', value: 'usable' },
+                { id: 5, name: '已到期', value: 'expire' },
+            ],
+        })
+        let ss = store.state.schoolPower
+        // let sa = toRaw(ss)
+        console.log(ss, '获取vuex的值')
+        //API获取数据
+        function dataInt(schoolId) {
+            console.log(schoolId, '调用到方法')
+            if (schoolId === undefined) {
+                ElMessage.error('学校ID异常')
+                return
+            }
+            proxy.$api.getschoolAuthorization({ schoolCode: schoolId }).then((res) => {
+                res.state === 200 ? (handleData(res.serials), (classRoom.value = res.rooms)) : ''
+            })
+        }
+        //处理数据
+        function handleData(value) {
+            let data = value
+            let ntime = Date.parse(new Date()) / 1000
+            for (let i in data) {
+                data[i].etime = proxy.$common.timestampToTime(data[i].endDate)
+                data[i].state = ''
+                data[i].typename = ''
+                data[i].usedata = 0
+                data[i].pitch = false
+                data[i].endDate < ntime ? (data[i].state = false) : (data[i].state = true)
+                data[i].deviceMax > 1 ? (data[i].typename = '大量') : (data[i].typename = '单一')
+                !data[i].deviceBound ? (data[i].usedata = 0) : (data[i].usedata = data[i].deviceBound.length)
+            }
+            powerList.value = data
+            originalData.value = data
+            let disposeData = JSON.parse(JSON.stringify(data))
+            topData.value.largeNum = disposeData.filter((item) => {
+                return item.deviceMax > 1
+            })
+            topData.value.singlenessNum = disposeData.filter((item) => {
+                return (item.deviceMax = 1)
+            })
+            topData.value.availableNum = disposeData.filter((item) => {
+                return item.deviceBound === null
+            })
+            topData.value.enableNum = disposeData.filter((item) => {
+                return item.deviceBound !== null
+            })
+            console.log(powerList.value, topData)
+        }
+        //点击选中某个识别码
+        function pitcheEcod(value, index) {
+            value.pitch = true
+            pithCode.value.pithState = index
+            pithCode.value.present = value
+            powerList.value[index] = value
+        }
+        //序列号筛选
+        function filtrateData(value, name) {
+            console.log(value, name)
+            console.log(originalData.value, '原始数据')
+            let original = JSON.parse(JSON.stringify(originalData.value))
+            let datas = []
+            value === 'all' ? (datas = originalData.value) : ''
+            value === 'sole'
+                ? ((datas = original.filter((item) => {
+                      return item.deviceMax === 1
+                  })),
+                  console.log(datas, 'sole'))
+                : ''
+            value === 'large'
+                ? ((datas = original.filter((item) => {
+                      return item.deviceMax > 1
+                  })),
+                  console.log(datas, 'large'))
+                : ''
+            value === 'usable'
+                ? ((datas = original.filter((item) => {
+                      return item.deviceBound === null
+                  })),
+                  console.log(datas, 'usable'))
+                : ''
+            value === 'expire'
+                ? ((datas = original.filter((item) => {
+                      return item.state === false
+                  })),
+                  console.log(datas, 'expire'))
+                : ''
+            selectList.value.value = name
+            powerList.value = []
+            powerList.value = datas
+            console.log(datas, '查看现在的数量')
+        }
+        //序列号绑定教室
+        function bindClass(value, index) {
+            console.log(classRoom.value, pithCode.value)
+            subsequentOperate(classRoom.value[index].id, classRoom.value[index].code, pithCode.value.present.serial)
+            classRoom.value[index].serial = pithCode.value.present.serial
+        }
+        function relieveBind(item, index) {
+            console.log(item)
+            ElMessageBox.confirm('确定解除当前教室的序列号吗?', '解绑序列号', {
+                confirmButtonText: '确定',
+                cancelButtonText: '取消',
+                type: 'info',
+                center: true,
+            }).then(() => {
+                subsequentOperate(item.id, item.code, '')
+                item.serial = null
+            })
+        }
+        //绑定和解绑后的API操作
+        function subsequentOperate(roomid, roomcode, serial) {
+            let data = { roomId: roomid, roomCode: roomcode, serial: serial }
+            proxy.$api
+                .operateClassroom(data)
+                .then((res) => {
+                    console.log(res, '绑定解绑成功')
+                    res.state === 200 ? ElMessage.success('操作成功') : ElMessage.error('操作失败')
+                })
+                .catch((res) => {
+                    ElMessage.error('API异常')
+                })
+        }
+        watch(
+            props,
+            (newdata) => {
+                newdata.schoolCode !== undefined ? dataInt(newdata.schoolCode) : ''
+            },
+            { immediate: true, deep: true }
+        )
+        return { topData, pithCode, powerList, dataInt, pitcheEcod, selectList, filtrateData, originalData, classRoom, bindClass, relieveBind, subsequentOperate }
+    },
+}
+</script>
+<style scoped>
+.classpower {
+    height: 80vh;
+}
+.classpower-top {
+    height: 140px;
+    width: 100%;
+    border-bottom: 1px solid #d5d5d5;
+    display: flex;
+    position: relative;
+}
+.alonebox {
+    width: 22%;
+    padding-left: 20px;
+    height: 120px;
+    line-height: 40px;
+    text-align: left;
+}
+.stretchbox {
+    width: 100%;
+    height: calc(100% - 190px);
+    background: rgb(255, 255, 255);
+}
+.alonebox p {
+    margin-top: 20px;
+    margin-bottom: 0px;
+    font-size: 14px;
+}
+.alonebox-content {
+    margin-top: 0px;
+    display: flex;
+    align-items: center;
+}
+.alonebox-content-type {
+    background: rgb(28, 192, 243);
+    color: rgb(255, 255, 255);
+    padding: 2px 8px;
+    margin-right: 5px;
+    font-size: 12px;
+    border-radius: 2px;
+    line-height: 20px;
+}
+.alonebox-content-name {
+    font-size: 14px;
+    color: #515a6e;
+}
+.alonebox-content-num {
+    font-size: 30px;
+    display: inline-block;
+    font-weight: 500;
+    margin-left: 15px;
+    color: #515a6e;
+}
+.alonebox-content-numtext {
+    font-size: 30px;
+    display: inline-block;
+    font-weight: 500;
+    color: #515a6e;
+}
+.alonebox-content-numtext span {
+    margin-left: 14px;
+}
+.second {
+    margin-top: 40px;
+}
+.example-showcase .el-dropdown-link {
+    cursor: pointer;
+    color: var(--el-color-primary);
+    display: flex;
+    align-items: center;
+}
+.left-serialnum {
+    line-height: 20px;
+    text-align: left;
+}
+.unfoldicon {
+    width: 1.1em;
+    height: 1.1em;
+    fill: currentColor;
+    overflow: hidden;
+    line-height: 1.1em;
+    margin-left: 5px;
+    margin-bottom: 1px;
+}
+.left-serialnum-top {
+    width: 100%;
+    padding: 1%;
+    border-bottom: 1px dashed #ddd;
+    position: sticky !important;
+    line-height: 30px;
+    height: 40px;
+    top: 0px;
+    background-color: #e9eef3;
+    z-index: 999;
+}
+.left-serialnum-top-title {
+    font-size: 14px;
+}
+.serialnumList {
+    position: relative;
+    box-sizing: border-box;
+    min-width: 100%;
+    min-height: 100%;
+    width: 100%;
+}
+.serialnumList-item {
+    padding: 20px 10px 20px 22px;
+    position: relative;
+}
+.serialnumList-item:hover {
+    background-color: #e1eff6;
+}
+.serialnumList-item:hover .pitchon-affirm {
+    opacity: 1;
+}
+.serialnumList-item-tag {
+    color: #fff;
+    width: -moz-fit-content;
+    width: fit-content;
+    padding: 3px 10px 3px 10px;
+    font-size: 14px;
+    white-space: nowrap;
+    background: rgb(25, 190, 107);
+}
+.serialnumList-item-taginfo {
+    color: #fff;
+    width: -moz-fit-content;
+    width: fit-content;
+    padding: 3px 10px 3px 10px;
+    font-size: 14px;
+    white-space: nowrap;
+    background: rgb(28, 192, 243);
+}
+.serialnumList-item-num {
+    border: 1px solid;
+    font-size: 14px;
+    padding: 2px 15px;
+    color: #000;
+    border-color: rgb(25, 190, 107);
+}
+.expire {
+    color: #ed4014;
+}
+.alreadyBind {
+    background: #19be6b;
+}
+.alreadyBindc {
+    color: #fff;
+}
+.serialnumList-item-details {
+    color: #515a6e;
+    font-size: 14px;
+}
+.details-endtime-content,
+.details-use-content {
+    font-weight: 600;
+}
+.details-use-title {
+    margin-left: 15px;
+}
+.secondinfo {
+    margin-top: 10px;
+}
+.secondinfo span {
+    border: 1px solid #d5d5d5;
+    margin-right: 15px;
+    padding: 2px 8px;
+    border-radius: 2px;
+    white-space: nowrap;
+    font-size: 12px;
+}
+.classgradeList {
+    position: relative;
+    box-sizing: border-box;
+    min-width: 100%;
+    min-height: 100%;
+    width: 100%;
+}
+.classgradeList-list {
+    display: flex;
+    flex-wrap: wrap;
+    padding: 10px;
+}
+.pitchon {
+    font-size: 14px;
+    position: absolute;
+    right: 7px;
+    top: 50%;
+    margin-top: -7px;
+}
+.pitchon-close {
+    margin-right: 10px;
+    cursor: pointer;
+    color: #2d8cf0;
+    /* display: none; */
+}
+.pitchon-affirm {
+    margin-right: 10px;
+    cursor: pointer;
+    color: #2d8cf0;
+    opacity: 0;
+}
+.pitchon-confirmed {
+    margin-right: 10px;
+    cursor: pointer;
+    color: #2d8cf0;
+}
+.classgradeList-item {
+    border: 1px solid #e0e0e0;
+    margin: 10px;
+    padding: 20px;
+    position: relative;
+    width: 180px;
+    min-height: 110px;
+    font-size: 14px;
+    line-height: 20px;
+    color: #515a6e;
+}
+.classgradeList-item-name {
+    font-size: 16px;
+    font-weight: 600;
+    text-align: center;
+}
+.classgradeList-item-type {
+    background: #508ac4;
+    color: #fff;
+    font-size: 12px;
+    padding: 1px 8px;
+    text-align: center;
+    width: -moz-fit-content;
+    width: fit-content;
+    margin: auto;
+    margin-top: 10px;
+}
+.classgradeList-item-bind {
+    margin-top: 10px;
+    text-align: center;
+    color: #ed4014;
+}
+.classgradeList-item-alreadybind {
+    margin-top: 10px;
+    text-align: center;
+    color: #fff;
+}
+.expiretext {
+    color: #ed4014;
+}
+.classgradeList-item:hover .classgradeList-item-hover {
+    opacity: 1;
+}
+.classgradeList-item-hover {
+    position: absolute;
+    left: 0;
+    top: 0;
+    width: 100%;
+    height: 100%;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    opacity: 0;
+    z-index: 99;
+    background: #f2f2f2;
+}
+.classgradeList-item-hover span {
+    cursor: pointer;
+    color: #2d8cf0;
+    user-select: none;
+}
+.active {
+    background-color: #e1eff6;
+}
+</style>
+<style>
+.left-serialnum-top .el-dropdown {
+    color: #1296db;
+}
+.stretchbox .splitpanes__pane {
+    background-color: #fff !important;
+    overflow: auto;
+}
+</style>
+

+ 5 - 3
TEAMModelBI/ClientApp/src/view/teachermanage/impower.vue

@@ -47,7 +47,7 @@
                     <svg class="producticon" aria-hidden="true" v-else>
                         <use xlink:href="#icon-jishufuwu-copy"></use>
                     </svg>
-                    <span class="productList-text">{{item.name}}</span>
+                    <span class="productList-text" :class="item.state <=0 ? 'notreach':''">{{item.name}}</span>
                     <svg class="producticon dependright" aria-hidden="true" v-show="item.state >0">
                         <use xlink:href="#icon-gouxuan"></use>
                     </svg>
@@ -60,7 +60,7 @@
                     <svg class="producticon" aria-hidden="true">
                         <use xlink:href="#icon-sharpicons_monitor-copy-copy"></use>
                     </svg>
-                    <span class="productList-text">{{item.name}}</span>
+                    <span class="productList-text" :class="item.state <=0 ? 'notreach':''">{{item.name}}</span>
                     <svg class="producticon dependright" aria-hidden="true" v-show="item.state >0">
                         <use xlink:href="#icon-gouxuan"></use>
                     </svg>
@@ -72,6 +72,7 @@
 <script>
 import { ref, getCurrentInstance, watch } from 'vue'
 import { ElMessage } from 'element-plus'
+import { useStore } from 'vuex'
 export default {
     props: {
         schoolCode: {
@@ -81,6 +82,7 @@ export default {
     },
     setup(props) {
         let { proxy } = getCurrentInstance()
+        const store = useStore()
         let activeNames = ref(['0'])
         let productData = ref([
             {
@@ -234,7 +236,7 @@ export default {
             }
             proxy.$api.getschoolAuthorization({ schoolCode: schoolId }).then((res) => {
                 console.log(res, '授权API返回')
-                res.state === 200 ? inquireData(res) : ''
+                res.state === 200 ? (inquireData(res), store.commit('updateSchoolpower', res)) : ''
             })
             // .catch((res) => {
             //     ElMessage.error('API数据异常')

+ 5 - 0
TEAMModelBI/ClientApp/src/view/teachermanage/school.vue

@@ -166,6 +166,9 @@
             <el-tab-pane label="服务授权管理">
                 <Impower :schoolCode="studyPhase"></Impower>
             </el-tab-pane>
+            <el-tab-pane label="智慧教室授权管理">
+                <Classpower :schoolCode="studyPhase"></Classpower>
+            </el-tab-pane>
         </el-tabs>
     </div>
     <!--编辑学校页面end-->
@@ -178,11 +181,13 @@ import { ElMessage } from 'element-plus'
 import { useRouter } from 'vue-router'
 import SetSchool from './setschool.vue'
 import Impower from './impower.vue'
+import Classpower from './classpower.vue'
 const optionsData = option
 export default {
     components: {
         SetSchool,
         Impower,
+        Classpower,
     },
     setup() {
         let { proxy } = getCurrentInstance()