Bladeren bron

Merge branch 'develop5.0-tmd' of http://106.12.23.251:10000/TEAMMODEL/TEAMModelOS into develop5.0-tmd

OnePsycho 3 jaren geleden
bovenliggende
commit
d20ada29e4
48 gewijzigde bestanden met toevoegingen van 2194 en 204 verwijderingen
  1. 44 42
      TEAMModeBI/ClientApp/package.json
  2. 12 6
      TEAMModeBI/ClientApp/public/index.html
  3. 5 16
      TEAMModeBI/ClientApp/src/App.vue
  4. BIN
      TEAMModeBI/ClientApp/src/assets/img/background1.png
  5. BIN
      TEAMModeBI/ClientApp/src/assets/img/background2.png
  6. BIN
      TEAMModeBI/ClientApp/src/assets/img/background3.png
  7. BIN
      TEAMModeBI/ClientApp/src/assets/img/ddlogin.png
  8. BIN
      TEAMModeBI/ClientApp/src/assets/img/erweima.png
  9. BIN
      TEAMModeBI/ClientApp/src/assets/img/login.png
  10. BIN
      TEAMModeBI/ClientApp/src/assets/img/mima.png
  11. 3 2
      TEAMModeBI/ClientApp/src/main.js
  12. 7 20
      TEAMModeBI/ClientApp/src/router/index.js
  13. 123 0
      TEAMModeBI/ClientApp/src/view/ddlogin.vue
  14. 161 0
      TEAMModeBI/ClientApp/src/view/login.vue
  15. 1080 0
      TEAMModeBI/Controllers/BIHome/StudyStatisController.cs
  16. 41 0
      TEAMModeBI/Controllers/BISchool/BatchAreaController.cs
  17. 117 0
      TEAMModeBI/Controllers/BISchool/BatchSchoolController.cs
  18. 168 0
      TEAMModeBI/Controllers/DingDingLogin/DDBindController.cs
  19. 278 16
      TEAMModeBI/Controllers/LoginController.cs
  20. 2 3
      TEAMModeBI/Properties/launchSettings.json
  21. 21 4
      TEAMModeBI/Startup.cs
  22. 1 4
      TEAMModeBI/TEAMModeBI.csproj
  23. 1 0
      TEAMModeBI/appsettings.Development.json
  24. 1 0
      TEAMModelOS.SDK/Models/Cosmos/Common/MQActivity.cs
  25. 2 0
      TEAMModelOS.SDK/Models/Cosmos/School/ExamInfo.cs
  26. 1 0
      TEAMModelOS.SDK/Models/Cosmos/School/Paper.cs
  27. 41 2
      TEAMModelOS.SDK/Models/Cosmos/Teacher/Teacher.cs
  28. 2 0
      TEAMModelOS.SDK/Models/Service/TriggerStuActivity.cs
  29. 2 2
      TEAMModelOS.SDK/TEAMModelOS.SDK.csproj
  30. 19 68
      TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContentTypeTemplate/LessonTestReport.vue
  31. 2 0
      TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContentTypeTemplate/PaperView.vue
  32. 3 0
      TEAMModelOS/ClientApp/src/components/student-web/EventView/EventList.vue
  33. 2 0
      TEAMModelOS/ClientApp/src/locale/lang/en-US/learnActivity.js
  34. 4 0
      TEAMModelOS/ClientApp/src/locale/lang/en-US/studentWeb.js
  35. 1 0
      TEAMModelOS/ClientApp/src/locale/lang/en-US/unit.js
  36. 2 0
      TEAMModelOS/ClientApp/src/locale/lang/zh-CN/learnActivity.js
  37. 4 0
      TEAMModelOS/ClientApp/src/locale/lang/zh-CN/studentWeb.js
  38. 1 0
      TEAMModelOS/ClientApp/src/locale/lang/zh-CN/unit.js
  39. 2 0
      TEAMModelOS/ClientApp/src/locale/lang/zh-TW/learnActivity.js
  40. 4 0
      TEAMModelOS/ClientApp/src/locale/lang/zh-TW/studentWeb.js
  41. 1 0
      TEAMModelOS/ClientApp/src/locale/lang/zh-TW/unit.js
  42. 5 0
      TEAMModelOS/ClientApp/src/view/learnactivity/CreateSchoolEva.less
  43. 11 1
      TEAMModelOS/ClientApp/src/view/learnactivity/CreateSchoolEva.vue
  44. 15 14
      TEAMModelOS/ClientApp/src/view/learnactivity/MgtSchoolEva.vue
  45. 2 2
      TEAMModelOS/ClientApp/src/view/learnactivity/markpaper/MarkData.vue
  46. 1 1
      TEAMModelOS/ClientApp/src/view/task/mark/ByQu.vue
  47. 1 1
      TEAMModelOS/Controllers/Common/ExamController.cs
  48. 1 0
      TEAMModelOS/Controllers/Third/ScController.cs

+ 44 - 42
TEAMModeBI/ClientApp/package.json

@@ -1,46 +1,48 @@
 {
-  "name": "clientapp",
-  "version": "0.1.0",
-  "private": true,
-  "scripts": {
-    "serve": "vue-cli-service serve",
-    "build": "vue-cli-service build",
-    "lint": "vue-cli-service lint"
-  },
-  "dependencies": {
-    "axios": "^0.20.0-0",
-    "bootstrap": "^4.5.3",
-    "core-js": "^3.7.0",
-    "vue": "^3.0.2",
-    "vue-loader-v16": "npm:vue-loader@^16.0.0-alpha.3",
-    "vue-router": "^4.0.0-rc.5"
-  },
-  "devDependencies": {
-    "@vue/cli-plugin-babel": "^4.5.9",
-    "@vue/cli-plugin-eslint": "^4.5.9",
-    "@vue/cli-service": "^4.5.9",
-    "@vue/compiler-sfc": "^3.0.2",
-    "babel-eslint": "^10.1.0",
-    "eslint": "^6.8.0",
-    "eslint-plugin-vue": "^7.1.0"
-  },
-  "eslintConfig": {
-    "root": true,
-    "env": {
-      "node": true
+    "name": "clientapp",
+    "version": "0.1.0",
+    "private": true,
+    "scripts": {
+        "serve": "vue-cli-service serve",
+        "build": "vue-cli-service build",
+        "lint": "vue-cli-service lint"
     },
-    "extends": [
-      "plugin:vue/vue3-essential",
-      "eslint:recommended"
-    ],
-    "parserOptions": {
-      "parser": "babel-eslint"
+    "dependencies": {
+        "axios": "^0.20.0-0",
+        "bootstrap": "^4.5.3",
+        "core-js": "^3.7.0",
+        "element-plus": "^1.1.0-beta.24",
+        "less": "^4.1.2",
+        "vue": "^3.0.2",
+        "vue-loader-v16": "npm:vue-loader@^16.0.0-alpha.3",
+        "vue-router": "^4.0.0-rc.5"
     },
-    "rules": {}
-  },
-  "browserslist": [
-    "> 1%",
-    "last 2 versions",
-    "not dead"
-  ]
+    "devDependencies": {
+        "@vue/cli-plugin-babel": "^4.5.9",
+        "@vue/cli-plugin-eslint": "^4.5.9",
+        "@vue/cli-service": "^4.5.9",
+        "@vue/compiler-sfc": "^3.0.2",
+        "babel-eslint": "^10.1.0",
+        "eslint": "^6.8.0",
+        "eslint-plugin-vue": "^7.1.0"
+    },
+    "eslintConfig": {
+        "root": true,
+        "env": {
+            "node": true
+        },
+        "extends": [
+            "plugin:vue/vue3-essential",
+            "eslint:recommended"
+        ],
+        "parserOptions": {
+            "parser": "babel-eslint"
+        },
+        "rules": {}
+    },
+    "browserslist": [
+        "> 1%",
+        "last 2 versions",
+        "not dead"
+    ]
 }

+ 12 - 6
TEAMModeBI/ClientApp/public/index.html

@@ -1,17 +1,23 @@
 <!DOCTYPE html>
 <html lang="en">
-  <head>
+
+<head>
     <meta charset="utf-8">
     <meta http-equiv="X-UA-Compatible" content="IE=edge">
     <meta name="viewport" content="width=device-width,initial-scale=1.0">
     <link rel="icon" href="<%= BASE_URL %>favicon.ico">
-    <title><%= htmlWebpackPlugin.options.title %></title>
-  </head>
-  <body>
+    <title>
+        <%= htmlWebpackPlugin.options.title %>
+    </title>
+</head>
+<script src="https://g.alicdn.com/dingding/dinglogin/0.0.5/ddLogin.js"></script>
+
+<body>
     <noscript>
       <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
     </noscript>
     <div id="app"></div>
     <!-- built files will be auto injected -->
-  </body>
-</html>
+</body>
+
+</html>

+ 5 - 16
TEAMModeBI/ClientApp/src/App.vue

@@ -1,26 +1,15 @@
 <template>
-  <nav-menu></nav-menu>
-  <router-view />
+    <router-view />
 </template>
 
 <script>
-    import NavMenu from './components/NavMenu.vue'
-
 export default {
-  name: 'App',
-  components: {
-      NavMenu
-  }
-}
+    name: "App",
+    components: {},
+};
 </script>
-
 <style>
 #app {
-  font-family: Avenir, Helvetica, Arial, sans-serif;
-  -webkit-font-smoothing: antialiased;
-  -moz-osx-font-smoothing: grayscale;
-  text-align: center;
-  color: #2c3e50;
-  margin-top: 60px;
+    min-width: 1280px;
 }
 </style>

BIN
TEAMModeBI/ClientApp/src/assets/img/background1.png


BIN
TEAMModeBI/ClientApp/src/assets/img/background2.png


BIN
TEAMModeBI/ClientApp/src/assets/img/background3.png


BIN
TEAMModeBI/ClientApp/src/assets/img/ddlogin.png


BIN
TEAMModeBI/ClientApp/src/assets/img/erweima.png


BIN
TEAMModeBI/ClientApp/src/assets/img/login.png


BIN
TEAMModeBI/ClientApp/src/assets/img/mima.png


+ 3 - 2
TEAMModeBI/ClientApp/src/main.js

@@ -2,5 +2,6 @@ import 'bootstrap/dist/css/bootstrap.css'
 import { createApp } from 'vue'
 import App from './App.vue'
 import router from './router'
-
-createApp(App).use(router).mount('#app')
+import ElementPlus from 'element-plus'
+import 'element-plus/dist/index.css'
+createApp(App).use(router).use(ElementPlus).mount('#app')

+ 7 - 20
TEAMModeBI/ClientApp/src/router/index.js

@@ -1,25 +1,12 @@
 import { createWebHistory, createRouter } from "vue-router";
-import Home from "@/components/Home.vue";
-import Counter from "@/components/Counter.vue";
-import FetchData from "@/components/FetchData.vue";
+import Login from "@/view/login.vue";
 
-const routes = [
-    {
-        path: "/",
-        name: "Home",
-        component: Home,
-    },
-    {
-        path: "/Counter",
-        name: "Counter",
-        component: Counter,
-    },
-    {
-        path: "/FetchData",
-        name: "FetchData",
-        component: FetchData,
-    }
-];
+
+const routes = [{
+    path: "/",
+    name: "Login",
+    component: Login,
+}, ];
 
 const router = createRouter({
     history: createWebHistory(),

+ 123 - 0
TEAMModeBI/ClientApp/src/view/ddlogin.vue

@@ -0,0 +1,123 @@
+
+<template>
+    <div style="width:100%">
+        <div id="login_container" style="margin-bottom:5%;"></div>
+    </div>
+</template>
+ 
+<script>
+import axios from "axios";
+export default {
+    name: "App",
+    components: {},
+    data() {
+        return {
+            appid: "dingrucgsnt8p13rfbgd",
+            redirectUrl: "https://localhost:5001",
+            apiUrl: "https://localhost:5001/common/login/DingLogin",
+            dingCodeConfig: {
+                id: "login_container",
+                style: "background-color:#FFFFFF;",
+                width: "300",
+                height: "350",
+            },
+        };
+    },
+    computed: {
+        getRedirectUrl() {
+            return encodeURIComponent(this.redirectUrl);
+        },
+        getAuthUrl() {
+            return `https://oapi.dingtalk.com/connect/oauth2/sns_authorize?appid=${this.appid}&response_type=code&scope=snsapi_login&state=STATE&redirect_uri=${this.getRedirectUrl}`;
+        },
+        getGoto() {
+            return encodeURIComponent(this.getAuthUrl);
+        },
+        getDingCodeConfig() {
+            return { ...this.dingCodeConfig, goto: this.getGoto };
+        },
+    },
+    created() {
+        this.initDingJs();
+    },
+    mounted() {
+        this.addDingListener();
+        this.initDingLogin();
+        this.getUser();
+    },
+    methods: {
+        initDingJs() {
+            !(function (window, document) {
+                function d(a) {
+                    var e,
+                        c = document.createElement("iframe"),
+                        d =
+                            "https://login.dingtalk.com/login/qrcode.htm?goto=" +
+                            a.goto;
+                    (d += a.style
+                        ? "&style=" + encodeURIComponent(a.style)
+                        : ""),
+                        (d += a.href ? "&href=" + a.href : ""),
+                        (c.src = d),
+                        (c.frameBorder = "0"),
+                        (c.allowTransparency = "true"),
+                        (c.scrolling = "no"),
+                        (c.width = a.width ? a.width + "px" : "365px"),
+                        (c.height = a.height ? a.height + "px" : "400px"),
+                        (e = document.getElementById(a.id)),
+                        (e.innerHTML = ""),
+                        e.appendChild(c);
+                }
+
+                window.DDLogin = d;
+            })(window, document);
+        },
+        addDingListener() {
+            let self = this;
+
+            let handleLoginTmpCode = function (loginTmpCode) {
+                window.location.href =
+                    self.getAuthUrl + `&loginTmpCode=${loginTmpCode}`;
+            };
+
+            let handleMessage = function (event) {
+                if (event.origin == "https://login.dingtalk.com") {
+                    handleLoginTmpCode(event.data);
+                }
+            };
+
+            if (typeof window.addEventListener != "undefined") {
+                window.addEventListener("message", handleMessage, false);
+            } else if (typeof window.attachEvent != "undefined") {
+                window.attachEvent("onmessage", handleMessage);
+            }
+        },
+        initDingLogin() {
+            window.DDLogin(this.getDingCodeConfig);
+        },
+        getUser() {
+            let getQueryString = function (name) {
+                var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)", "i");
+                var r = window.location.search.substr(1).match(reg);
+                if (r != null) {
+                    return unescape(r[2]);
+                }
+                return null;
+            };
+
+            let code = getQueryString("code");
+
+            if (code !== null) {
+                axios
+                    .get(`${this.apiUrl}?code=${code}`)
+                    .then((response) => {
+                        console.log(response);
+                    })
+                    .catch((error) => {
+                        console.log(error);
+                    });
+            }
+        },
+    },
+};
+</script>

+ 161 - 0
TEAMModeBI/ClientApp/src/view/login.vue

@@ -0,0 +1,161 @@
+<template>
+    <div class="backgorundbox">
+        <div class="loginbox">
+            <div class="logintitle">醍摩豆账号登录</div>
+            <div class="usrpwd" v-if="loginModel">
+                <div class='userbox' style="margin-bottom:10%">
+                    <el-input v-model="user" placeholder="醍摩豆ID/手机号码" prefix-icon="el-icon-user" />
+                </div>
+                <div class='userbox'>
+                    <el-input v-model="pwd" placeholder="密码" type="password" prefix-icon="el-icon-key" />
+                    <div class="loginbtn" v-show="user !='' && pwd !='' "><img src="@/assets/img/login.png"></div>
+                </div>
+                <div class="not_has_more">
+                    或下列方式登录
+                    <div class="ddlogin" @click="loginModel=false"><img src="../assets/img/ddlogin.png"></div>
+                </div>
+            </div>
+            <div class="usrpwd" v-else-if="loginModel ==false">
+                <ddlogin></ddlogin>
+            </div>
+            <div class="cut" @click="loginImginfo">
+                <svg width="52" height="52" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" style="border-top-right-radius:10px">
+                    <mask id="id-3757926926-a" width="52" height="52" x="0" y="0" maskUnits="userSpaceOnUse">
+                        <path fill="#fff" d="M0 0l52 52V0H0z"></path>
+                    </mask>
+                    <g mask="url(#id-3757926926-a)">
+                        <path fill="#ccF" d="M0 0h48a4 4 0 014 4v48L0 0z"></path>
+                        <path fill="url(#pattern0)" d="M0 0h52v52H0z"></path>
+                    </g>
+                    <defs>
+                        <pattern id="pattern0" width="1" height="1" patternContentUnits="objectBoundingBox">
+                            <use transform="scale(.00216)" xlink:href="#image0"></use>
+                        </pattern>
+                        <image id="image0" width="463" height="463" :xlink:href="loginImg"></image>
+                    </defs>
+                </svg>
+            </div>
+        </div>
+        <!-- <div id="login_container" style="transform: scale(.8);">123456</div> -->
+    </div>
+</template>
+<script>
+import { ref, watch } from "vue";
+import ddlogin from "./ddlogin.vue";
+export default {
+    components: {
+        ddlogin,
+    },
+    setup() {
+        let user = ref("");
+        let pwd = ref("");
+        let loginModel = ref(true);
+        let loginImg = ref(require("../assets/img/erweima.png"));
+        //处理登录icon变化
+        watch(loginModel, () => {
+            loginImg.value =
+                loginModel.value == true
+                    ? (loginImg.value = require("../assets/img/erweima.png"))
+                    : (loginImg.value = require("../assets/img/mima.png"));
+        });
+        function loginImginfo() {
+            loginModel.value = !loginModel.value;
+        }
+        return {
+            user,
+            pwd,
+            loginModel,
+            loginImg,
+            loginImginfo,
+        };
+    },
+};
+</script>
+<style scoped>
+.backgorundbox {
+    background: url("../assets/img/background2.png") no-repeat;
+    background-size: cover;
+    width: 100vw;
+    height: 100vh;
+    position: relative;
+    /* filter: blur(2px); */
+}
+.loginbox {
+    position: absolute;
+    width: 550px;
+    height: auto;
+    top: 200px;
+    right: 110px;
+    border-radius: 10px;
+    box-shadow: 1px 1px 5px #ccc;
+}
+.usrpwd {
+    height: 330px;
+    text-align: center;
+}
+.logintitle {
+    text-align: center;
+    font-size: 20px;
+    color: #fff;
+    margin-bottom: 8%;
+    margin-top: 5%;
+}
+.userbox {
+    text-align: center;
+    width: 60%;
+    margin: 0 auto;
+    position: relative;
+}
+.none {
+    display: none;
+}
+.loginbtn {
+    position: absolute;
+    display: block;
+    right: 0px;
+    top: 0px;
+    width: 44px;
+    height: 44px;
+    line-height: 43px;
+}
+.not_has_more {
+    margin: 30px 0px 20px 0px;
+    line-height: 50px;
+    text-align: center;
+    position: relative;
+    height: 95px;
+    font-size: 14px;
+    font-family: PingFangSC-Regular, PingFang SC;
+    font-weight: 400;
+    color: rgba(140, 142, 165, 1);
+    line-height: 30px;
+}
+.not_has_more::after,
+.not_has_more::before {
+    position: absolute;
+    width: 54px;
+    height: 0.5px;
+    background: #dadada;
+    content: "";
+    top: 15px;
+    right: 28%;
+}
+.not_has_more::after {
+    left: 28%;
+}
+.ddlogin {
+    margin-top: 3%;
+}
+.cut {
+    position: absolute;
+    top: 0px;
+    right: 0px;
+}
+</style>
+<style>
+.userbox .el-input__inner {
+    height: 45px;
+    border-radius: 30px;
+}
+</style>
+

File diff suppressed because it is too large
+ 1080 - 0
TEAMModeBI/Controllers/BIHome/StudyStatisController.cs


+ 41 - 0
TEAMModeBI/Controllers/BISchool/BatchAreaController.cs

@@ -0,0 +1,41 @@
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using TEAMModelOS.Models;
+using TEAMModelOS.SDK.DI;
+using TEAMModelOS.SDK.Models;
+
+namespace TEAMModeBI.Controllers.BISchool
+{
+    [Route("batcharea")]
+    [ApiController]
+    public class BatchAreaController : ControllerBase
+    {
+
+        private readonly AzureCosmosFactory _azureCosmos;
+        private readonly DingDing _dingDing;
+        private readonly Option _option;
+        private readonly AzureStorageFactory _azureStorage;
+
+        public BatchAreaController(AzureCosmosFactory azureCosmos, DingDing dingDing, AzureStorageFactory azureStorage, Option option)
+        {
+            _azureCosmos = azureCosmos;
+            _dingDing = dingDing;
+            _azureStorage = azureStorage;
+            _option = option;
+        }
+
+        //public async Task<IActionResult> BatchArea(Area area) 
+        //{
+            
+        //}
+
+
+
+
+
+    }
+}

+ 117 - 0
TEAMModeBI/Controllers/BISchool/BatchSchoolController.cs

@@ -0,0 +1,117 @@
+using Azure.Cosmos;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using TEAMModelOS.Models;
+using TEAMModelOS.SDK.DI;
+using TEAMModelOS.SDK.Models;
+using static TEAMModelOS.SDK.Models.Teacher;
+
+namespace TEAMModeBI.Controllers.BISchool
+{
+    [Route("batchchool")]
+    [ApiController]
+    public class BatchSchoolController : ControllerBase
+    {
+        private readonly AzureCosmosFactory _azureCosmos;
+        private readonly DingDing _dingDing;
+        private readonly Option _option;
+        private readonly AzureStorageFactory _azureStorage;
+
+        public BatchSchoolController(AzureCosmosFactory azureCosmos, DingDing dingDing, AzureStorageFactory azureStorage, Option option)
+        {
+            _azureCosmos = azureCosmos;
+            _dingDing = dingDing;
+            _azureStorage = azureStorage;
+            _option = option;
+        }
+
+        /// <summary>
+        /// 批量创校  --完成带测验
+        /// </summary>
+        /// <param name="school"></param>
+        /// <returns></returns>
+        [ProducesDefaultResponseType]
+        [HttpPost("batchUpsert")]
+        public async Task<IActionResult> batchUpsert(School school,string number)
+        {
+            List<School> school_list = new List<School>();
+            //创建多个学校
+            for (int i = 0; i < int.Parse(number); i++)
+            {
+                string id = $"{school.id}_{i}";
+                var client = _azureCosmos.GetCosmosClient();
+                var schoolContainer = client.GetContainer(Constant.TEAMModelOS, "School");
+                var response = await schoolContainer.ReadItemStreamAsync(id, new PartitionKey($"Base"));
+                if (response.Status == 200)
+                {
+                    string sql = $"select distinct value(c) from c jion A1 in c.schools where A1.schoolID='{id}'";
+                    List<Teacher> teacher_list = new List<Teacher>();
+                    await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "Teacher").GetItemQueryIterator<Teacher>(sql, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Base") }))
+                    {
+                        teacher_list.Add(item);
+                    }
+
+                    foreach (var item in teacher_list)
+                    {
+                        TeacherSchool teacherSchool = item.schools.Find(x => x.schoolId.Equals(id));
+                        if (teacherSchool != null)
+                        {
+                            teacherSchool.name = $"{school.name}_{i}";
+                            teacherSchool.picture = school.picture;
+                            teacherSchool.areaId = school.areaId;
+                        }
+                        await client.GetContainer(Constant.TEAMModelOS, "Teacher").ReplaceItemAsync(item, item.id, new PartitionKey($"Base"));
+                    }
+                    school.id = id;
+                    school = await schoolContainer.UpsertItemAsync(school, new PartitionKey($"Base"));
+                }
+                else
+                {
+                    school.code = "Base";
+                    school.id = $"{school.id}_{i}";
+                    school = await schoolContainer.CreateItemAsync(school,new PartitionKey($"Base"));
+                }
+            }
+
+            ////创一个学校案例
+            //var client = _azureCosmos.GetCosmosClient();
+            //var schoolContainer = client.GetContainer(Constant.TEAMModelOS, "School");
+            //var response = await schoolContainer.ReadItemStreamAsync(school.id, new PartitionKey($"Base"));
+            //if (response.Status == 200)
+            //{
+            //    string sql = $"select distinct value(c) from c jion A1 in c.schools where A1.schoolID='{school.id}'";
+            //    List<Teacher> teacher_list = new List<Teacher>();
+            //    await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "Teacher").GetItemQueryIterator<Teacher>(sql, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("Base") })) 
+            //    {
+            //        teacher_list.Add(item);
+            //    }
+
+            //    foreach (var item in teacher_list) 
+            //    {
+            //        TeacherSchool teacherSchool = item.schools.Find(x => x.schoolId.Equals(school.id));
+            //        if (teacherSchool != null) 
+            //        {
+            //            teacherSchool.name = school.name;
+            //            teacherSchool.picture = school.picture;
+            //            teacherSchool.areaId = school.areaId;
+            //        }
+            //        await client.GetContainer(Constant.TEAMModelOS, "Teacher").ReplaceItemAsync(item, item.id, new PartitionKey($"Base"));
+            //    }
+            //    school = await schoolContainer.UpsertItemAsync(school, new PartitionKey($"Base"));
+            //}
+            //else 
+            //{
+            //    school.code = "Base";
+            //    school = await schoolContainer.CreateItemAsync(school,new PartitionKey($"Base"));
+            //}
+
+            return Ok(new { school_list });
+        }
+
+
+    }
+}

+ 168 - 0
TEAMModeBI/Controllers/DingDingLogin/DDBindController.cs

@@ -0,0 +1,168 @@
+using Azure.Cosmos;
+using Azure.Storage.Blobs.Models;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+using System;
+using System.Collections.Generic;
+using System.IdentityModel.Tokens.Jwt;
+using System.Linq;
+using System.Threading.Tasks;
+using TEAMModelOS.Models;
+using TEAMModelOS.SDK.DI;
+using TEAMModelOS.SDK.Models;
+
+namespace TEAMModeBI.Controllers.DingDingLogin
+{
+    [Route("ddbind")]
+    [ApiController]
+    public class DDBindController : ControllerBase
+    {
+        private readonly AzureCosmosFactory _azureCosmos;
+        private readonly DingDing _dingDing;
+        private readonly Option _option;
+        private readonly AzureStorageFactory _azureStorage;
+        public readonly string type = "ddteammodel";
+
+        public DDBindController(AzureCosmosFactory azureCosmos, DingDing dingDing, AzureStorageFactory azureStorage, Option option)
+        {
+            _azureCosmos = azureCosmos;
+            _dingDing = dingDing;
+            _azureStorage = azureStorage;
+            _option = option;
+        }
+
+        /// <summary>
+        /// 钉钉绑定醍摩豆教师信息
+        /// </summary>
+        /// <param name="ddrcord"></param>
+        /// <returns></returns>
+        [ProducesDefaultResponseType]
+        [HttpGet("bind")]
+        [AllowAnonymous]
+        public async Task<IActionResult> Bind(ddrcord ddrcord)
+        {
+            try
+            {
+                Teacher teacher = null;
+                var jwt = new JwtSecurityToken(ddrcord.id_token);
+                if (!jwt.Payload.Iss.Equals("account.teammodel", StringComparison.OrdinalIgnoreCase)) return BadRequest();
+                var id = jwt.Payload.Sub;
+                jwt.Payload.TryGetValue("name", out object name);
+                jwt.Payload.TryGetValue("picture", out object picture);
+                var client = _azureCosmos.GetCosmosClient();
+                teacher = await client.GetContainer(Constant.TEAMModelOS, "Teacher").ReadItemAsync<Teacher>(id, new PartitionKey("Base"));
+                string sql = $"SELECT distinct value(c) FROM c join A1 in  c.ddbinds where  A1.userid='{1}' and A1.loginid='{1}'";
+                await foreach (var item in _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "Teacher").GetItemQueryIterator<Teacher>(queryText: sql,
+                    requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Base") }))
+                {
+                    teacher = item;
+                    break;
+                }
+
+                if (teacher != null)
+                {
+                    if (teacher.id.Equals(id))
+                    {
+                        var ddbind = teacher.ddbinds.Find(x => x.userid.Equals($"{ddrcord.userid}") && x.loginid.Equals($"{ddrcord.loginid}"));
+                        if (ddbind == null)
+                        {
+                            teacher.ddbinds = new List<Teacher.DingDingBind> { new Teacher.DingDingBind { type = type, loginid = $"{ddrcord.loginid}", userid = $"{ddrcord.userid}", userName = $"{ddrcord.userName}", Mobile = $"{ddrcord.mobile}", email = $"{ddrcord.email}", sourceid = ddrcord.sourceid } };
+                            await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "Teacher").ReplaceItemAsync<Teacher>(teacher, teacher.id, new PartitionKey(teacher.code));
+                        }
+                    }
+                    else
+                    {
+                        return Ok(new
+                        {
+                            location = _option.Location,
+                            //账号已被别的醍摩豆id绑定
+                            status = 3,
+                            tmdid = teacher.id,
+                            name = teacher.name,
+                            userid = ddrcord.userid,
+                            ddname = ddrcord.userName
+                        });
+                    }
+                }
+                else
+                {
+                    teacher = new Teacher
+                    {
+                        id = id,
+                        pk = "Base",
+                        code = "Base",
+                        name = name?.ToString(),
+                        picture = picture?.ToString(),
+                        //创建账号并第一次登录IES5则默认赠送1G
+                        size = 1,
+                        defaultSchool = null,
+                        schools = new List<Teacher.TeacherSchool>(),
+                        ddbinds = new List<Teacher.DingDingBind> { new Teacher.DingDingBind { type = type, loginid = $"{ddrcord.loginid.ToString()}", userid = $"{ddrcord.userid.ToString()}", userName = $"{ddrcord.userName}", Mobile = $"{ddrcord.mobile}", email = $"{ddrcord.email}", sourceid = ddrcord.sourceid } }
+                    };
+
+                    var container = _azureStorage.GetBlobContainerClient(id);
+                    await container.CreateIfNotExistsAsync(PublicAccessType.None); //嘗試創建Teacher私有容器,如存在則不做任何事,保障容器一定存在
+                    teacher = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "Teacher").CreateItemAsync<Teacher>(teacher, new PartitionKey("Base"));
+                }
+                return Ok(new
+                {
+                    location = _option.Location,
+                    status = 200,
+                });
+            }
+            catch (Exception)
+            {
+                return Ok(new
+                {
+                    location = _option.Location,
+                    status = 2 
+                });
+            }
+        }
+        public record ddrcord
+        {
+            /// <summary>
+            /// 绑定类型  ddteammodel
+            /// </summary>
+            public string type { get; set; }
+
+            /// <summary>
+            /// 用户来源
+            /// </summary>
+            public string loginid { get; set; }
+
+            /// <summary>
+            /// 钉钉ID
+            /// </summary>
+            public string userid { get; set; }
+
+            /// <summary>
+            /// 钉钉用户名
+            /// </summary>
+            public string userName { get; set; }
+
+            /// <summary>
+            /// 钉钉手机号
+            /// </summary>
+            public string mobile { get; set; }
+
+            /// <summary>
+            /// 邮箱
+            /// </summary>
+            public string email { get; set; }
+
+
+            public HashSet<string> sourceid { get; set; } = new HashSet<string>();
+
+            /// <summary>
+            /// 登录编号
+            /// </summary>
+            public string id_token { get; set; }
+
+        }
+
+
+
+    }
+}

+ 278 - 16
TEAMModeBI/Controllers/LoginController.cs

@@ -12,33 +12,47 @@ using System.Text.Json;
 using System.Threading.Tasks;
 using TEAMModelOS.SDK.DI;
 using TEAMModelOS.SDK.Models;
+using HTEXLib.COMM.Helpers;
+using TEAMModelOS.Models;
+using static TEAMModelOS.SDK.Models.Teacher;
 
 namespace TEAMModeBI.Controllers
 {
-    [ProducesResponseType(StatusCodes.Status200OK)]
-    [ProducesResponseType(StatusCodes.Status400BadRequest)]
+    //[ProducesResponseType(StatusCodes.Status200OK)]
+    //[ProducesResponseType(StatusCodes.Status400BadRequest)]
     [Route("common/login")]
     [ApiController]
     public class LoginController : ControllerBase
     {
         private readonly IConfiguration _configuration;
-        public LoginController(IConfiguration configuration)
+        //数据容器
+        private readonly AzureCosmosFactory _azureCosmos;
+        //文件容器
+        private readonly AzureStorageFactory _azureStorage;
+        //钉钉提示信息
+        private readonly DingDing _dingDing;
+        private readonly Option _option;
+        public LoginController(IConfiguration configuration, AzureCosmosFactory azureCosmos, AzureStorageFactory azureStorage, DingDing dingDing, Option option)
         {
-            _configuration = configuration;     
+            _configuration = configuration;
+            _azureCosmos = azureCosmos;
+            _azureStorage = azureStorage;
+            _dingDing = dingDing;
+            _option = option;
         }
 
-       /// <summary>
-       /// 钉钉扫描登录
-       /// </summary>
-       /// <param name="loginTmpCode"></param>
-       /// <returns></returns>
+        /// <summary>
+        /// 钉钉扫描登录
+        /// </summary>
+        /// <param name="loginTmpCode"></param>
+        /// <returns>Json结果</returns>
         [ProducesDefaultResponseType]
         [HttpGet("dingding")]
-        public IActionResult DingDingLogin(string loginTmpCode) 
+        public IActionResult DingDingLogin(string loginTmpCode)
         {
-            string appKey = _configuration["appKey"];
-            string appSecret = _configuration["appSecret"];
-            string getuserinfo_bycode = _configuration["getuserinfo_bycode"];
+            string appKey = _configuration["DingDingAuth:appKey"];
+            string appSecret = _configuration["DingDingAuth:appSecret"];
+            string getuserinfo_bycode = _configuration["DingDingAuth:getuserinfo_bycode"];
             //判断参数是否为空
             if (string.IsNullOrEmpty(loginTmpCode))
             {
@@ -86,16 +100,264 @@ namespace TEAMModeBI.Controllers
             OapiV2UserGetRequest getRequest = new OapiV2UserGetRequest()
             {
                 Userid = userid,
-                Language="zh_CN"
+                Language = "zh_CN"
             };
             getRequest.SetHttpMethod("Get");
             OapiV2UserGetResponse getResponse = clientDingTalkClient2.Execute(getRequest, access_token);
-            if (getResponse.IsError) 
+            if (getResponse.IsError)
             {
                 return BadRequest();
             }
-            return Ok(getResponse);
+            return Ok(getResponse.Body);
         }
 
+        /// <summary>
+        /// 钉钉扫码登录
+        /// </summary>
+        /// <param name="requert"></param>
+        /// <returns>Json结果</returns>
+        [ProducesDefaultResponseType]
+        [HttpGet("DingLogin")]
+        public async Task<IActionResult> DingLogin(JsonElement jsonElement)
+        {
+            string temp_mess = null;
+            //state 是前端传入的,钉钉并不会修改,比如有多种登录方式的时候,一个登录方法判断登录方式可以进行不同的处理。
+            try
+            {
+                string str_appKey = _configuration["DingDingAuth:appKey"];
+                string str_appSecret = _configuration["DingDingAuth:appSecret"];
+                string str_agentld = "1290158212";
+                if (string.IsNullOrWhiteSpace(str_appKey) || string.IsNullOrWhiteSpace(str_appSecret))
+                {
+                    throw new Exception("请先配置钉钉扫码登录信息!");
+                }
+                //自己传的code
+                if (jsonElement.TryGetProperty("tempCode", out JsonElement LoginTempCode)) return BadRequest();
+                string accreCode = LoginTempCode.ToString();
+                //判断参数是否为空
+                if (string.IsNullOrEmpty(LoginTempCode.ToString()))
+                {
+                    return BadRequest("temp code error");
+                }
+
+                //获取企业内部应用的accessToken
+                DefaultDingTalkClient Iclient = new DefaultDingTalkClient("https://oapi.dingtalk.com/gettoken");
+                OapiGettokenRequest request = new OapiGettokenRequest();
+                request.Appkey = str_appKey;
+                request.Appsecret = str_appSecret;
+                request.SetHttpMethod("GET");
+                OapiGettokenResponse tokenResponse = Iclient.Execute(request);
+                if (tokenResponse.IsError)
+                {
+                    return BadRequest();
+                }
+
+                //temp_mess = tokenResponse.Body;
+                //获取引用后台免登录凭证
+                DefaultDingTalkClient NoVoucher = new DefaultDingTalkClient("https://oapi.dingtalk.com/sso/gettoken");
+                OapiSsoGettokenRequest ssoRequest = new OapiSsoGettokenRequest();
+                ssoRequest.Corpid = str_agentld;
+                ssoRequest.Corpsecret = str_appSecret;
+                ssoRequest.SetHttpMethod("GET");
+                OapiSsoGettokenResponse ssoResponse = new OapiSsoGettokenResponse();
+                ssoResponse = NoVoucher.Execute(ssoRequest);
+
+                //temp_mess += "=====" + ssoResponse.Body;
+                ////return Ok(tokenResponse.Body);
+                ////自己传的code
+                //if (!jsonElement.TryGetProperty("accreCode", out JsonElement jsaccreCode)) return BadRequest();
+                //string accreCode = jsaccreCode.ToString();
+
+                //自己获取code
+                //string accreCode = tokenResponse.AccessToken;
+                //temp_mess += "====="+accreCode;
+
+                DefaultDingTalkClient clientinfo = new DefaultDingTalkClient("https://oapi.dingtalk.com/sns/getuserinfo_bycode");
+                OapiSnsGetuserinfoBycodeRequest req = new OapiSnsGetuserinfoBycodeRequest() { TmpAuthCode = accreCode };
+                //req.TmpAuthCode = code;
+                OapiSnsGetuserinfoBycodeResponse response = clientinfo.Execute(req, str_appKey, str_appSecret);
+                //temp_mess += "====="+ response.Body;
+                //return Ok(temp_mess);
+                ////return Ok(response.Body); //用户信息代检验;
+                ////获取到response后就可以进行自己的登录业务处理了
+                ////xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
+
+                if (response.IsError)
+                {
+                    return BadRequest();
+                }
+
+                //temp_mess += response.UserInfo;
+                //根据unionid获取userid
+                string unionid = response.UserInfo.Unionid;
+                IDingTalkClient client2 = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/user/getbyunionid"); //userid地址
+                OapiUserGetbyunionidRequest byunionidRequest = new OapiUserGetbyunionidRequest() { Unionid = unionid };
+                OapiUserGetbyunionidResponse byunionidResponse = client2.Execute(byunionidRequest, accreCode);
+                if (byunionidResponse.IsError)
+                {
+                    return BadRequest();
+                }
+                // 根据userId获取用户信息
+                string userid = byunionidResponse.Result.Userid;
+                IDingTalkClient client3 = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/v2/user/get");
+                OapiV2UserGetRequest v2GetRequest = new OapiV2UserGetRequest()
+                {
+                    Userid = userid,
+                    Language = "zh_CN"
+                };
+                v2GetRequest.SetHttpMethod("POST");
+                OapiV2UserGetResponse v2GetResponse = client3.Execute(v2GetRequest, accreCode);
+                if (v2GetResponse.IsError)
+                {
+                    return BadRequest();
+                }
+
+                var DDbind = v2GetResponse.Result;
+
+                DingDingBind dingDingBind = new()
+                {
+                    type = "ddteammodel",
+                    loginid = DDbind.LoginId,
+                    userid = DDbind.Userid,
+                    userName = DDbind.Name,
+                    Mobile = DDbind.Mobile,
+                    email = DDbind.Email,
+                    sourceid = new HashSet<string> { DDbind.LoginId }
+                };
+                
+                Teacher teacher = null;
+                string sql = $"select distinct value(c) c join A1 in c.ddbinds where A1.userid={dingDingBind.userid} AND A1.loginid = {dingDingBind.loginid}";
+                await foreach (var item in _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "Teacher").GetItemQueryIterator<Teacher>(queryText: sql, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Base") })) 
+                {
+                    teacher = item;
+                    break;
+                }
+                if (teacher == null)
+                {
+                    return Ok(new { status = 0, msg = "没有绑定!" , dingDingBind });
+                }
+                else 
+                {
+                    var ddbind = teacher.ddbinds.FindAll(x => x.userid.Equals($"{dingDingBind.userid}") && x.loginid.Equals($"{dingDingBind.loginid}"));
+                    if (ddbind != null)
+                    {
+                        return Ok(new { teacher, dingDingBind });
+                    }
+                    else 
+                    {
+                        teacher.ddbinds.Add(dingDingBind);
+                        await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "Teacher").ReplaceItemAsync<Teacher>(teacher, teacher.id, new PartitionKey(teacher.code));
+                        return Ok(new { teacher, dingDingBind });
+                    }
+                }
+            }
+            catch (Exception e)
+            {
+                return BadRequest(temp_mess + "======" + e.Message);
+            }
+
+        }
+
+
+
+
+        /// <summary>
+        /// 钉钉扫码登录返回String
+        /// </summary>
+        /// <param name="accreCode"></param>
+        /// <param name="state"></param>
+        /// <returns></returns>
+        [ProducesDefaultResponseType]
+        [HttpGet("DLogin")]
+        public string DLogin(string accreCode, string state)
+        {
+            //state 是前端传入的,钉钉并不会修改,比如有多种登录方式的时候,一个登录方法判断登录方式可以进行不同的处理。
+            OapiSnsGetuserinfoBycodeResponse response = new OapiSnsGetuserinfoBycodeResponse();
+            try
+            {
+                string qrAppId = _configuration["DingDingAuth:appKey"];
+                string qrAppSecret = _configuration["DingDingAuth:appSecret"];
+                if (string.IsNullOrWhiteSpace(qrAppId) || string.IsNullOrWhiteSpace(qrAppSecret))
+                {
+                    throw new Exception("请先配置钉钉扫码登录信息!");
+                }
+
+                DefaultDingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/sns/getuserinfo_bycode");
+                OapiSnsGetuserinfoBycodeRequest req = new OapiSnsGetuserinfoBycodeRequest();
+                req.TmpAuthCode = accreCode;
+                response = client.Execute(req, qrAppId, qrAppSecret);
+
+                //获取到response后就可以进行自己的登录业务处理了
+                //xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx               
+
+                if (response.IsError)
+                {
+                    return "unionid读取失败";
+                }
+
+                //根据unionid获取userid
+                string unionid = response.UserInfo.Unionid;
+                IDingTalkClient client2 = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/user/getbyunionid"); //userid地址
+                OapiUserGetbyunionidRequest byunionidRequest = new OapiUserGetbyunionidRequest() { Unionid = unionid };
+                OapiUserGetbyunionidResponse byunionidResponse = client2.Execute(byunionidRequest, accreCode);
+                if (byunionidResponse.IsError)
+                {
+                    return "userid读取失败";
+                }
+                // 根据userId获取用户信息
+                string userid = byunionidResponse.Result.Userid;
+                IDingTalkClient client3 = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/v2/user/get");
+                OapiV2UserGetRequest v2GetRequest = new OapiV2UserGetRequest()
+                {
+                    Userid = userid,
+                    Language = "zh_CN"
+                };
+                v2GetRequest.SetHttpMethod("GET");
+                OapiV2UserGetResponse v2GetResponse = client3.Execute(v2GetRequest, accreCode);
+                if (v2GetResponse.IsError)
+                {
+                    return "用户信息读取错误";
+                }
+
+                return response.Body;
+
+            }
+            catch (Exception e)
+            {
+                return response.Errmsg = e.Message;
+            }
+
+        }
+
+
+        public async Task<IActionResult> TeamModeBILogin(JsonElement jsonElement)
+        {
+            try
+            {
+                if (!jsonElement.TryGetProperty("id", out JsonElement id)) return BadRequest();
+                if (!jsonElement.TryGetProperty("pw", out JsonElement pw)) return BadRequest();
+
+                var client = _azureCosmos.GetCosmosClient();
+
+                var response = await client.GetContainer(Constant.TEAMModelOS, "Teacher").ReadItemStreamAsync(id.GetString(), new PartitionKey($"Base"));
+
+
+                return Ok(new { });
+
+
+            }
+            catch (Exception ex)
+            {
+                await _dingDing.SendBotMsg($"IES5,{_option.Location},LoginController/TeamModeBILogin\n Error Message{ex.Message}  Error sting:{ex.StackTrace}", GroupNames.醍摩豆服務運維群組);
+                throw;
+            }
+
+        }
+
+
+
+       
+
     }
+
 }

+ 2 - 3
TEAMModeBI/Properties/launchSettings.json

@@ -3,8 +3,8 @@
     "windowsAuthentication": false,
     "anonymousAuthentication": true,
     "iisExpress": {
-      "applicationUrl": "http://localhost:50598",
-      "sslPort": 0
+      "applicationUrl": "http://localhost:61874/",
+      "sslPort": 44335
     }
   },
   "$schema": "http://json.schemastore.org/launchsettings.json",
@@ -19,7 +19,6 @@
     "TEAMModeBI": {
       "commandName": "Project",
       "launchBrowser": true,
-
       "environmentVariables": {
         "ASPNETCORE_ENVIRONMENT": "Development"
       },

+ 21 - 4
TEAMModeBI/Startup.cs

@@ -15,6 +15,7 @@ using System.Collections.Generic;
 using System.IdentityModel.Tokens.Jwt;
 using System.Linq;
 using System.Threading.Tasks;
+using TEAMModelOS.Filter;
 using TEAMModelOS.Models;
 using TEAMModelOS.SDK.Context.Configuration;
 using TEAMModelOS.SDK.DI;
@@ -133,6 +134,8 @@ namespace TEAMModeBI
                     await client.Client.SendEventAsync(new { sid = client.Client.Id.ToString() }.ToJsonString());
                 };
             });
+            //等保安全性验证。
+            services.AddScoped<SecurityHeadersAttribute>();
         }
 
         // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
@@ -154,10 +157,10 @@ namespace TEAMModeBI
             app.UseAuthentication();
             app.UseAuthorization();
 
-            app.UseEndpoints(endpoints =>
-            {
-                endpoints.MapControllers();
-            });
+            //app.UseEndpoints(endpoints =>
+            //{
+            //    endpoints.MapControllers();
+            //});
 
             //app.UseSpa(spa =>
             //{
@@ -172,6 +175,20 @@ namespace TEAMModeBI
             //    }
 
             //});
+
+            app.UseSpa(spa =>
+            {
+                spa.Options.SourcePath = "ClientApp/";
+                if (env.IsDevelopment())
+                {
+                    //spa.UseProxyToSpaDevelopmentServer("https://localhost:5001");
+                    //spa.Options.StartupTimeout = new TimeSpan(0, 0, 80);
+                    //spa.UseVueCli(npmScript: "serve"); 
+                    //spa.UseProxyToSpaDevelopmentServer("https://localhost:5001");
+                    spa.UseVueCli(npmScript: "serve");
+                }
+            });
+
             app.UseEndpoints(endpoints =>
             {
                 endpoints.MapControllers();

+ 1 - 4
TEAMModeBI/TEAMModeBI.csproj

@@ -24,10 +24,7 @@
 
   <ItemGroup>
     <ProjectReference Include="..\TEAMModelOS.SDK\TEAMModelOS.SDK.csproj" />
-  </ItemGroup>
-
-  <ItemGroup>
-    <Folder Include="Lib\" />
+    <ProjectReference Include="..\TEAMModelOS\TEAMModelOS.csproj" />
   </ItemGroup>
 
   <ItemGroup>

+ 1 - 0
TEAMModeBI/appsettings.Development.json

@@ -8,6 +8,7 @@
   },
   "AllowedHosts": "*",
   "DingDingAuth": {
+    "Agentld": "1290158212",
     "appKey": "dingrucgsnt8p13rfbgd",
     "appSecret": "Gyx_N57yZslhQOAhAPlvmCwOp_qTm1DScKbd5OoOE0URAW4eViYA2Sk_ZxKb-8WG",
     "getuserinfo_bycode": "https://oapi.dingtalk.com/sns/getuserinfo_bycode?accessKey=xxx&timestamp=xxx&signature=xxx"

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

@@ -24,6 +24,7 @@ namespace TEAMModelOS.SDK.Models
         public string blob { get; set; }
         public long startTime { get; set; }
         public long endTime { get; set; }
+        public string source { get; set; }
 
     }
     public class Sub { 

+ 2 - 0
TEAMModelOS.SDK/Models/Cosmos/School/ExamInfo.cs

@@ -138,6 +138,8 @@ namespace TEAMModelOS.SDK.Models
         public List<int> field { get; set; } = new List<int>();
         public  string  sheet { get; set; }        //public long sequenceNumber { get; set; }
         public string sheetNo { get; set; }        //public long sequenceNumber { get; set; }
+        //记录试卷作答时间
+        public int time { get; set; } = 0;
 
         //public List<Dictionary<string, int>> record { get; set; } = new List<Dictionary<string, int>>();
 

+ 1 - 0
TEAMModelOS.SDK/Models/Cosmos/School/Paper.cs

@@ -64,6 +64,7 @@ namespace TEAMModelOS.SDK.Models
         public string sheetNo { get; set; }
         //记录试卷大小
         public long? size { get; set; } = 0;
+
         
         /// <summary>
         /// type:{

+ 41 - 2
TEAMModelOS.SDK/Models/Cosmos/Teacher/Teacher.cs

@@ -11,9 +11,11 @@ namespace TEAMModelOS.SDK.Models
         public string picture { get; set; }
         public int size { get; set; }
         public string defaultSchool { get; set; }
-        public List<TeacherSchool> schools { get; set; }= new List<TeacherSchool>();
+        public List<TeacherSchool> schools { get; set; } = new List<TeacherSchool>();
         public List<TeacherArea> areas { get; set; } = new List<TeacherArea>();
         public List<ThirdBind> binds { get; set; } = new List<ThirdBind>();
+        public List<DingDingBind> ddbinds { get; set; } = new List<DingDingBind>();
+
         public class TeacherSchool
         {
             public string schoolId { get; set; }
@@ -39,10 +41,47 @@ namespace TEAMModelOS.SDK.Models
             /// 用户来源
             /// </summary>
             public string source { get; set; }
-          
+
             public string userid { get; set; }
             public HashSet<string> pxid { get; set; } = new HashSet<string>();
 
         }
+
+        public class DingDingBind
+        {
+            /// <summary>
+            /// 绑定类型  ddteammodel
+            /// </summary>
+            public string type { get; set; }
+
+            /// <summary>
+            /// 用户来源
+            /// </summary>
+            public string loginid { get; set; }
+
+            /// <summary>
+            /// 钉钉ID
+            /// </summary>
+            public string userid { get; set; }
+
+            /// <summary>
+            /// 钉钉用户名
+            /// </summary>
+            public string userName { get; set; }
+
+            /// <summary>
+            /// 钉钉手机号
+            /// </summary>
+            public string Mobile { get; set; }
+
+            /// <summary>
+            /// 邮箱
+            /// </summary>
+            public string email { get; set; }
+
+
+            public HashSet<string> sourceid { get; set; } = new HashSet<string>();
+
+        }
     }
 }

+ 2 - 0
TEAMModelOS.SDK/Models/Service/TriggerStuActivity.cs

@@ -46,6 +46,7 @@ namespace TEAMModelOS.SDK
                             scode = activity.code,
                             name = activity.name,
                             code = $"Activity-{tmdid.id}",
+                            source = activity.source,
                             scope = activity.scope,
                             school = activity.school,
                             creatorId = activity.creatorId,
@@ -74,6 +75,7 @@ namespace TEAMModelOS.SDK
                             name = activity.name,
                             code = $"Activity-{activity.school}-{student.id}",
                             scope = activity.scope,
+                            source = activity.source,
                             school = activity.school,
                             creatorId = activity.creatorId,
                             pk = "Activity",

+ 2 - 2
TEAMModelOS.SDK/TEAMModelOS.SDK.csproj

@@ -11,7 +11,7 @@
 
 
   <ItemGroup>
-    <PackageReference Include="HTEXLib" Version="5.2109.6" />
+    <PackageReference Include="HTEXLib" Version="5.2110.271" />
     <PackageReference Include="AspectCore.Extensions.Reflection" Version="2.2.0" />
     <PackageReference Include="Azure.Cosmos" Version="4.0.0-preview3" />
     <PackageReference Include="Azure.Identity" Version="1.4.0" />
@@ -20,7 +20,7 @@
     <PackageReference Include="Azure.Storage.Blobs.Batch" Version="12.6.0" />
     <PackageReference Include="Azure.Storage.Queues" Version="12.6.1" />
     <PackageReference Include="ClouDASLibx" Version="1.2.7" />
-    <PackageReference Include="DocumentFormat.OpenXml" Version="2.12.3" />
+    <PackageReference Include="DocumentFormat.OpenXml" Version="2.13.1" />
     <PackageReference Include="HtmlAgilityPack" Version="1.11.32" />
     <PackageReference Include="Lib.AspNetCore.ServerSentEvents" Version="6.0.0" />
     <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="3.1.10" />

+ 19 - 68
TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContentTypeTemplate/LessonTestReport.vue

@@ -2,77 +2,28 @@
     <div class="lesson-test-report">
         <div class="scoreboard">
             <div v-show="getItemTitle.progress == 'finish' && testState == 1">
-                <svg-icon icon-class="handonHint" class="warm-icon" />
-                <span class="warm-hint">{{ $t("studentWeb.exam.timeoutHint") }}</span>
+                <!-- 线上 -->
+                <div v-if="examInfo.source === '0' || !examInfo.source">
+                    <svg-icon icon-class="handonHint" class="warm-icon" />
+                    <span class="warm-hint">{{ $t("studentWeb.exam.timeoutHint") }}</span>
+                </div>
+                <!-- 课中 -->
+                <div v-else-if="examInfo.source === '1'">
+                    <svg-icon icon-class="handonHint" class="warm-icon" />
+                    <span class="warm-hint">{{ $t("studentWeb.exam.timeoutHint1") }}</span>
+                </div>
+                <!-- 阅卷 -->
+                <div v-else-if="examInfo.source === '2'">
+                    <svg-icon icon-class="handonHint" class="warm-icon" />
+                    <span class="warm-hint">{{ $t("studentWeb.exam.timeoutHint2") }}</span>
+                </div>
             </div>
-            <span v-show="getItemTitle.progress != 'finish' && testState == 1" @click="showTest" style="color: #03966a;width: 100%;cursor: pointer;font-size:18px;font-weight: 800;text-align: center">
-                {{$t("studentWeb.exam.report.anwser")}}
+            <span v-show="getItemTitle.progress != 'finish' && testState == 1" style="width: 100%;font-size:18px;font-weight: 800;text-align: center">
+                <span v-if="examInfo.source === '0' || !examInfo.source" @click="showTest" style="color: #03966a;cursor: pointer;">{{ $t("studentWeb.exam.report.anwser") }}</span>
+                <span v-else-if="examInfo.source === '1'">{{ $t("studentWeb.exam.report.anwser1") }}</span>
+                <span v-else-if="examInfo.source === '2'">{{ $t("studentWeb.exam.report.anwser2") }}</span>
             </span>
             <h4 v-show='testState == 2'>{{$t("studentWeb.exam.report.noRes")}}</h4>
-            <!-- <Row :gutter="20" v-if='testState == 3'>
-                <i-col :xs="24" :sm="24" :md="24" :lg="12">
-                    <Row :gutter="20">
-                        得分
-                        <i-col :xs="24" :sm="24" :md="12" :lg="12">
-                            <Card class="score-card">
-                                <p class="card-title">{{$t('studentWeb.exam.score')}}</p>
-                                <div class="card-content">
-                                    <span class="myscore">{{testScore}}</span> / {{ paperInfo.score }}
-                                </div>
-                            </Card>
-                        </i-col>
-                        难易度
-                        <i-col :xs="24" :sm="24" :md="12" :lg="12">
-                            <Card class="score-card">
-                                <p class="card-title">{{$t('studentWeb.exam.difficulty')}}</p>
-                                <div class="difficulty-rate">
-                                    <Rate class allow-half disabled v-model="difficultyRate" />
-                                    <span class="difficulty-rate-num">{{difficultyRate }}</span>
-                                </div>
-                            </Card>
-                        </i-col>
-                    </Row>
-                    <Row :gutter="20">
-                        班平均分
-                        <i-col :xs="24" :sm="24" :md="12" :lg="12">
-                            <Card class="score-card">
-                                <p class="card-title">{{$t('studentWeb.exam.average')}}</p>
-                                <div class="card-content">
-                                    <span class="myscore">--</span>
-                                </div>
-                            </Card>
-                        </i-col>
-                        得分题目数
-                        <i-col :xs="24" :sm="24" :md="12" :lg="12">
-                            <Card class="score-card">
-                                <p class="card-title">{{$t("studentWeb.exam.report.getScore")}}</p>
-                                <div class="card-content">
-                                    <span class="myscore">{{rightAns.right}}</span> / {{rightAns.all}}
-                                </div>
-                            </Card>
-                        </i-col>
-                        知识点
-                        <i-col :xs="24" :sm="24" :md="24" :lg="24" style="display:none">
-                            <Card class="comment-card">
-                                <p class="card-title">{{$t('studentWeb.exam.smartComment')}}</p>
-                                <div class="card-comment">
-                                    <h4>学习相对稳定</h4>
-                                    <br />
-                                    <h4>
-                                        {{$t('studentWeb.exam.keypoint')}}
-                                        <span class="keypoint">   三角函数 二元一次方程 三元一次方程 傅里叶函数</span>
-                                    </h4>
-                                </div>
-                            </Card>
-                        </i-col>
-                    </Row>
-                </i-col>
-                <i-col :xs="24" :sm="24" :md="24" :lg="12">
-                    <Card class="chart-card">
-                        <LessonTestReportCharts></LessonTestReportCharts>
-                    </Card>
-                </i-col>
-            </Row> -->
             <Row :gutter="20" v-if='testState == 3'>
                 <i-col :xs="24" :sm="24" :md="24" :lg="24">
                     <Row :gutter="20">

+ 2 - 0
TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContentTypeTemplate/PaperView.vue

@@ -144,6 +144,8 @@
                             let resData = res
                             for (let item of resData.papers) {
                                 if (item.scope) {
+                                    item.source = null
+                                    item.source = this.getItemTitle.source
                                     this.paperData.push(item)
                                 }
                             }

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

@@ -303,6 +303,9 @@ import { mapGetters, mapState } from 'vuex';
                 }
                 this.$api.studentWeb.getActivityInfo(params).then(res => {
                     if (res) {
+                        /* 
+                            source: 0(线上评量) / 1(课中评量) / 2(阅卷评量)
+                        */
                         let data = []
                         for (let item of res.datas) {
                             item.eventType = item.type

+ 2 - 0
TEAMModelOS/ClientApp/src/locale/lang/en-US/learnActivity.js

@@ -107,6 +107,8 @@ export default {
         delOk: 'Delete successfully',
         pdTips: 'Please select the school system!',
         defaultPaper: '(Please pick or import exam files)',
+        ansTime:'作答時間:',
+        ansTimeTips:'(默認為0,代表不限時)'
     },
     // ManualPaper.vue
     manual: {

+ 4 - 0
TEAMModelOS/ClientApp/src/locale/lang/en-US/studentWeb.js

@@ -442,6 +442,8 @@ export default {
         },
         report: {
             anwser: 'Start Answering',
+            anwser1: '该评量属于课中评量,无需线上作答',
+            anwser2: '该评量属于阅卷评量,无需线上作答',
             noRes: 'Results are not yet calculated.',
             getScore: 'Number of scored questions',
             answerBack: 'Answering Review',
@@ -466,6 +468,8 @@ export default {
             wrongPractice: 'Incorrectly Answered Questions Practice',
         },
         timeoutHint: 'The assessment activity has ended. Overdue will be calculated as 0 points, or wait for the teacher to allow make-ups.',
+        timeoutHint1: '评量活动时间已结束,正在等待HiTeach返回作答数据。',
+        timeoutHint2: '评量活动时间已结束,正在等待老师阅卷。',
         contentPage: 'Assessment Content',
         scorePage: 'Grade Report',
         practiceHint: 'The assessment activity  has ended, you can still click on the exam file link to continue practicing.',

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

@@ -13,4 +13,5 @@ export default {
     text12: 'total',
     text13:'No. Of People',
     gradeYear:'',
+    minute:'分鐘'
   }

+ 2 - 0
TEAMModelOS/ClientApp/src/locale/lang/zh-CN/learnActivity.js

@@ -107,6 +107,8 @@ export default {
         delOk: '删除成功',
         pdTips: '请先选择测试学段!',
         defaultPaper: '请先挑选试卷',
+        ansTime:'作答时间:',
+        ansTimeTips:'(默认为0,代表不限时)'
     },
     // ManualPaper.vue
     manual: {

+ 4 - 0
TEAMModelOS/ClientApp/src/locale/lang/zh-CN/studentWeb.js

@@ -442,6 +442,8 @@ export default {
         },
         report: {
             anwser: '前往作答',
+            anwser1: '该评量属于课中评量,无需线上作答',
+            anwser2: '该评量属于阅卷评量,无需线上作答',
             noRes: '成绩尚未结算',
             getScore: '得分题目数',
             answerBack: '评测作答回顾',
@@ -466,6 +468,8 @@ export default {
             wrongPractice: '错题练习',
         },
         timeoutHint: '评量活动时间已结束,逾时将以0分计算,或等待教师开放补考。',
+        timeoutHint1: '评量活动时间已结束,正在等待HiTeach返回作答数据。',
+        timeoutHint2: '评量活动时间已结束,正在等待老师阅卷。',
         contentPage: '评量内容',
         scorePage: '成绩报告',
         practiceHint: '评量活动时间已结束,仍可点击试卷连结,持续练习。',

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

@@ -13,4 +13,5 @@ export default {
   text12:'共',
   text13:'人数',
   gradeYear:'级',
+  minute:'分钟'
 }

+ 2 - 0
TEAMModelOS/ClientApp/src/locale/lang/zh-TW/learnActivity.js

@@ -107,6 +107,8 @@ export default {
         delOk: '刪除成功',
         pdTips: '請先選擇施測學制!',
         defaultPaper: '(請先挑選或匯入試卷)',
+        ansTime:'作答時間:',
+        ansTimeTips:'(默認為0,代表不限時)'
     },
     //ManualPaper.vue
     manual: {

+ 4 - 0
TEAMModelOS/ClientApp/src/locale/lang/zh-TW/studentWeb.js

@@ -442,6 +442,8 @@ export default {
         },
         report: {
             anwser: '前往作答',
+            anwser1: '該評量屬於課中評量,無需線上作答',
+            anwser2: '該評量屬於閱卷評量,無需線上作答',
             noRes: '成績尚未結算',
             getScore: '得分題數',
             answerBack: '評量作答回顧',
@@ -466,6 +468,8 @@ export default {
             wrongPractice: '錯題練習',
         },
         timeoutHint: '評量活動時間已結束,逾時將以0分計算,或等待教師開放補考。 ',
+        timeoutHint1: '評量活動時間已結束,正在等待HiTeach返回作答數據。',
+        timeoutHint2: '評量活動時間已結束,正在等待老師閱卷。',
         contentPage: '評量內容',
         scorePage: '成績報告',
         practiceHint: '評量活動時間已結束,仍可點擊試卷連結,持續練習。 ',

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

@@ -13,4 +13,5 @@ export default {
   text12: '共',
   text13:'人數',
   gradeYear: '級',
+  minute:'分鐘'
 }

+ 5 - 0
TEAMModelOS/ClientApp/src/view/learnactivity/CreateSchoolEva.less

@@ -144,6 +144,11 @@
         color: white;
         margin-right:40px;
     }
+
+    .time-tips{
+        margin-left: 10px;
+        color: #ff9900;
+    }
 }
 
 .add-subject-icon {

+ 11 - 1
TEAMModelOS/ClientApp/src/view/learnactivity/CreateSchoolEva.vue

@@ -94,6 +94,12 @@
                 <div class="evaluation-question-main">
                     <EmptyData :top="0" v-if="evaluationInfo.paperInfo.length == 0" :textContent="$t('learnActivity.createEv.noSubject')"></EmptyData>
                     <Tabs v-model="activeTab" type="card" class="question-main-tabs" v-if="evaluationInfo.paperInfo.length > 0 || mode == 'class' " name="createTest">
+                        <div slot="extra" style="margin-right:40px" v-show="activeTab == 'preview' && evaluationInfo.source === '0'">
+                            {{$t('learnActivity.createEv.ansTime')}}
+                            <InputNumber size="small" :min="0" type="number" v-model="evaluationInfo.paperInfo[curSubIndex].time" placeholder="0" style="width: 60px" />
+                            {{$t('unit.minute')}}
+                            <span class="time-tips">{{$t('learnActivity.createEv.ansTimeTips')}}</span>
+                        </div>
                         <TabPane :label="$t('learnActivity.createEv.papersLabel')" name="manualPaper" v-if="evaluationInfo.paperInfo[curSubIndex].createType == 'manualPaper'" :index="1" tab="createTest">
                             <ManualPaper :periodId="evaluationInfo.period.id" :gradesObj="evaluationInfo.grades" :subjectId="evaluationInfo.paperInfo[curSubIndex].subjectId" @selectPaper="selectPaper" :selectedId="evaluationInfo.paperInfo[curSubIndex].id" :source="$store.state.userInfo.schoolCode"></ManualPaper>
                         </TabPane>
@@ -121,7 +127,7 @@
         <Modal v-model="addSubjectStatus" :title="$t('learnActivity.createEv.addSubject')" @on-ok="confirmAddSubject">
             <div style="padding:20px 0px;">
                 <CheckboxGroup v-model="evaluationInfo.subjectIds">
-                    <Checkbox v-for="(subjectItem,index) in curSubjects" :key="index" :label="subjectItem.id">
+                    <Checkbox v-for="(subjectItem,index) in curSubjects" :key="index" :label="subjectItem.id" style="margin-bottom:10px;margin-right:15px">
                         <span>{{subjectItem.name}}</span>
                     </Checkbox>
                 </CheckboxGroup>
@@ -448,6 +454,7 @@ export default {
             console.log('挑选试卷', arguments)
             let curSubName = this.evaluationInfo.paperInfo[this.curSubIndex].subjectName
             let curSubId = this.evaluationInfo.paperInfo[this.curSubIndex].subjectId
+            let time = this.evaluationInfo.paperInfo[this.curSubIndex].time
             this.evaluationInfo.paperInfo[this.curSubIndex] = fullPaper
             this.evaluationInfo.papers[this.curSubIndex] = simplePaper
             //解决学科被覆盖
@@ -455,6 +462,7 @@ export default {
             this.evaluationInfo.paperInfo[this.curSubIndex].subjectName = curSubName
             this.evaluationInfo.paperInfo[this.curSubIndex].examScope = this.mode
             this.evaluationInfo.paperInfo[this.curSubIndex].examId = ""
+            this.evaluationInfo.paperInfo[this.curSubIndex].time = time
             this.evaluationInfo.paperInfo[this.curSubIndex].examCode = this.$store.state.userInfo.schoolCode
             this.evaluationInfo.papers[this.curSubIndex].subjectId = curSubId
             this.evaluationInfo.papers[this.curSubIndex].subjectName = curSubName
@@ -558,6 +566,7 @@ export default {
                         item: [],
                         filter: {},
                         name: this.$t('learnActivity.createEv.defaultPaper'),
+                        time: 0 //作答时间
                         // examScope: this.mode,
                         // examCode: this.$store.state.userInfo.schoolCode
                     })
@@ -823,6 +832,7 @@ export default {
                         paper.point = []
                         paper.knowledge = []
                         paper.field = []
+                        paper.time = rule[i].time
                         paper.type = []//后面新增字段, 保存每个题目类型
                         for (let k = 0; k < rule[i].slides.length; k++) {
                             if (rule[i].slides[k].type !== 'compose') {

+ 15 - 14
TEAMModelOS/ClientApp/src/view/learnactivity/MgtSchoolEva.vue

@@ -238,6 +238,8 @@ export default {
             }
             if (this.evaListShow.length) {
                 this.selectEvaluation(0)
+            }else{
+                this.isLoading = false
             }
         },
 
@@ -459,9 +461,9 @@ export default {
 
         //查询评测列表
         findEvaluation() {
+            this.isLoading = true
             let requestData = {
-                code: this.scope == 'school' ? this.$store.state.userInfo.schoolCode : this.$store.state.userInfo.TEAMModelId,
-                // classIds: undefined
+                code: this.scope == 'school' ? this.$store.state.userInfo.schoolCode : this.$store.state.userInfo.TEAMModelId
             }
             this.$api.learnActivity.FindExamInfo(requestData).then(
                 res => {
@@ -473,20 +475,20 @@ export default {
                         this.evaListShow = res.examInfo
                         if (this.scope == 'school') {
                             this.filterByPeriod()
-                            //如果当前学段没有评测则自动切换下一个学段
-                            // if (!this.evaListShow.length) {
-                            //     if (this.schoolBase.period && this.schoolBase.period.length > 1) {
-                            //         this.filterPeriod = this.schoolBase.period[1].id
-                            //         this.filterByPeriod()
-                            //     }
-                            // }
                         } else {
-                            if (res.examInfo.length) this.selectEvaluation(0)
+                            if (res.examInfo.length) {
+                                this.selectEvaluation(0)
+                            }else{
+                                this.isLoading = false
+                            }
                         }
-
                     } else {
                         this.$Message.error('API ERROR!')
+                        this.isLoading = false
                     }
+                },
+                err=>{
+                    this.isLoading = false
                 }
             )
         },
@@ -578,9 +580,8 @@ export default {
                     this.schoolBase = res.school_base
                 }
             }
-        ).finally(() => {
-            this.findEvaluation()
-        })
+        )
+        this.findEvaluation()
     },
     watch: {
         $route: {

+ 2 - 2
TEAMModelOS/ClientApp/src/view/learnactivity/markpaper/MarkData.vue

@@ -86,8 +86,8 @@
                         </template>
                         <template slot-scope="{ row }" slot="progress">
                             <div style="display: flex;justify-content: center;">
-                                <i-circle :percent="row.percent" :size="40" :stroke-width="8" :trail-width="7" stroke-color="#5cb85c">
-                                    <span v-if="row.percent != 100" style="font-size:12px">{{row.percent}}%</span>
+                                <i-circle :percent="row.percent || 0" :size="40" :stroke-width="8" :trail-width="7" stroke-color="#5cb85c">
+                                    <span v-if="row.percent != 100" style="font-size:12px">{{row.percent || 0}}%</span>
                                     <Icon v-else type="ios-checkmark" size="30" style="color:#5cb85c"></Icon>
                                 </i-circle>
                             </div>

+ 1 - 1
TEAMModelOS/ClientApp/src/view/task/mark/ByQu.vue

@@ -16,7 +16,7 @@
             <span class="info-label">{{$t('learnActivity.mark.stuId')}}: </span>
             <span class="info-value cur-stu-id">{{stusInfo[stuIndex] ? stusInfo[stuIndex].stuId : ''}}</span>
             <div class="btn-wrap">
-                <span class="action-btn" @click="toggleStatus = !toggleStatus">
+                <span class="action-btn" @click="toggleStatus = true">
                     <Icon type="md-shuffle" class="action-btn-icon" />
                     {{$t('learnActivity.mark.toggleQu')}}
                 </span>

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

@@ -1255,7 +1255,7 @@ namespace TEAMModelOS.Controllers
                 if (!requert.TryGetProperty("code", out JsonElement school)) return BadRequest();
                 if (!requert.TryGetProperty("scode", out JsonElement scode)) return BadRequest();
                 var client = _azureCosmos.GetCosmosClient();
-                var query = $"select c.id,c.code,c.school,c.creatorId,c.progress,A0.id paperId,A0.code paperCode,A0.name paperName,A0.knowledge,A0.point,A0.field,A0.multipleRule,A0.scope,A0.blob from c join A0 in c.papers where c.id ='{id}'";
+                var query = $"select c.id,c.code,c.school,c.creatorId,c.progress,A0.id paperId,A0.code paperCode,A0.name paperName,A0.knowledge,A0.point,A0.field,A0.multipleRule,A0.scope,A0.blob,A0.time from c join A0 in c.papers where c.id ='{id}'";
                 List<PaperSimple> papers = new List<PaperSimple>();
                 List<object> subjects = new List<object>();
                 List<string> classIds = new List<string>();

+ 1 - 0
TEAMModelOS/Controllers/Third/ScController.cs

@@ -95,6 +95,7 @@ namespace TEAMModelOS.Controllers.Third
         [HttpPost("get-list")]
         [AllowAnonymous]
         public async Task<IActionResult> GetProjectList(JsonElement json) {
+
             // 5.3.1.1获取项目列表
             string trainComID = _sc_trainComID;
             string Code = "GetProjectInfoByTrainComID";