Преглед на файлове

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

OnePsycho преди 2 години
родител
ревизия
c5a77413dc

+ 152 - 96
TEAMModelBI/Controllers/BISchool/BatchSchoolController.cs

@@ -37,6 +37,8 @@ using DocumentFormat.OpenXml.Wordprocessing;
 using DocumentFormat.OpenXml.Bibliography;
 using HTEXLib;
 using TEAMModelOS.SDK.Models.Service.BI;
+using TEAMModelOS.SDK.Models.Cosmos.BI.BISchool;
+using DocumentFormat.OpenXml.Vml.Office;
 
 namespace TEAMModelBI.Controllers.BISchool
 {
@@ -1035,6 +1037,11 @@ namespace TEAMModelBI.Controllers.BISchool
                 jsonElement.TryGetProperty("dist", out JsonElement dist);
                 jsonElement.TryGetProperty("address", out JsonElement address);
 
+
+                jsonElement.TryGetProperty("assist", out JsonElement assist);
+                List<IdInfo> idInfos = assist.ToObject<List<IdInfo>>();
+
+
                 //jsonElement.TryGetProperty("site", out JsonElement site);//分开部署,就不需要,一站多用时,取消注释
 
                 var (_tmdId, _tmdName, pic, did, dname, dpic) = HttpJwtAnalysis.JwtXAuthBI(HttpContext.GetXAuth("AuthToken"), _option);
@@ -1079,6 +1086,9 @@ namespace TEAMModelBI.Controllers.BISchool
                     //修改学校
                     await cosmosClient.GetContainer(Constant.TEAMModelOS, "School").ReplaceItemAsync<School>(tempShool, tempShool.id, new PartitionKey("Base"));
 
+                    //创建学校信息中间件
+                    _ = _httpTrigger.RequestHttpTrigger(new { school = $"{tempShool}" }, _option.Location, "set-sc-birelation");
+
                     //修改学校教师关联的信息
                     string sql = $"SELECT distinct value(c) FROM c join A1 in c.schools where A1.schoolId='{tempShool.id}'";
                     List<Teacher> teachers = new();
@@ -1098,110 +1108,156 @@ namespace TEAMModelBI.Controllers.BISchool
                         await cosmosClient.GetContainer(Constant.TEAMModelOS, "Teacher").ReplaceItemAsync(item, item.id, new PartitionKey($"Base"));
                     }
 
-                    if (assistId.Count > 0)
+                    ////需要重大修改后保留
+                    if (idInfos.Count > 0)
                     {
-                        //修改学校顾问
-                        string sqlTxt = $"SELECT value(c) From c WHERE ARRAY_CONTAINS(c.roles,'assist',true)";
-                        List<SchoolTeacher> schoolTeachers = new();
-                        await foreach (var item in cosmosClient.GetContainer("TEAMModelOS", "School").GetItemQueryIterator<SchoolTeacher>(queryText: sqlTxt, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Teacher-{tempShool.id}") }))
+                        BIRelation biRel = new();
+                        var respRel = await cosmosClient.GetContainer(Constant.TEAMModelOS, "School").ReadItemStreamAsync(tempShool.id,new PartitionKey("BIRel"));
+                        if (respRel.Status == 200)
                         {
-                            if (!assistId.Contains(item.id))
-                            {
-                                if (item.roles.Contains("assist"))
-                                {
-                                    item.roles.Remove("assist");
-                                    if (item.roles.Count > 0)
-                                    {
-                                        await cosmosClient.GetContainer("TEAMModelOS", "School").ReplaceItemAsync<SchoolTeacher>(item, item.id, new PartitionKey(item.code));
-                                    }
-                                    else
-                                    {
-                                        await cosmosClient.GetContainer("TEAMModelOS", "School").DeleteItemAsync<SchoolTeacher>(item.id, new PartitionKey(item.code));
-                                    }
-                                }
-                            }
+                            using var fileJson = await JsonDocument.ParseAsync(respRel.ContentStream);
+                            biRel = fileJson.ToObject<BIRelation>();
                         }
-
-                        foreach (var itemTeacher in assistId)
+                        else
+                            biRel.id = tempShool.id;
+
+                        string aName = null;
+                        if (!string.IsNullOrEmpty($"{tempShool.areaId}"))
+                            aName = await CosmosQueryHelper.GetStr(cosmosClient, "Normal", $"select value(c.name) from c where c.id='{tempShool.areaId}'", "Base-Area");
+
+                        biRel.name = tempShool.name;
+                        biRel.picture = tempShool.picture;
+                        biRel.region = tempShool.region;
+                        biRel.province = tempShool.province;
+                        biRel.city = tempShool.city;
+                        biRel.dist = tempShool.dist;
+                        biRel.address = tempShool.address;
+                        biRel.areaId = tempShool.areaId;
+                        biRel.size = tempShool.size;
+                        biRel.scale = tempShool.scale;
+                        biRel.upDate = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
+                        biRel.areaName = aName;
+                        foreach (var item in idInfos)
                         {
-                            Teacher tempTeacher = await cosmosClient.GetContainer(Constant.TEAMModelOS, "Teacher").ReadItemAsync<Teacher>($"{itemTeacher}", new PartitionKey("Base"));
-                            if (tempTeacher != null)
+                            var ser = biRel.assists.Find(f => f.Equals(item.id));
+                            if (string.IsNullOrEmpty(ser.id)) 
                             {
-                                var haveTeacher = tempTeacher.schools.Find(x => x.schoolId.Equals($"{_schoolId}"));
-                                if (haveTeacher == null)
-                                {
-                                    Teacher.TeacherSchool teacherSchool = new()
-                                    {
-                                        schoolId = tempShool.id,
-                                        name = tempShool.name,
-                                        status = "join",
-                                        time = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
-                                        picture = tempShool.picture,
-                                        areaId = tempShool.areaId
-                                    };
-                                    tempTeacher.schools.Add(teacherSchool);
-                                    //给醍摩豆顾问添加学校
-                                    await cosmosClient.GetContainer(Constant.TEAMModelOS, "Teacher").ReplaceItemAsync<Teacher>(tempTeacher, tempTeacher.id, new PartitionKey($"Base"));
-
-                                    ////不存在则在原来的基础上添加顾问角色
-                                    //SchoolTeacher addSchoolTeacher = new()
-                                    //{
-                                    //    id = itemTeacher,
-                                    //    code = $"Teacher-{tempShool.id}",
-                                    //    pk = "Teacher",
-                                    //    status = "join",
-                                    //    roles = new List<string>() { "assist" },
-                                    //    name = tempTeacher.name,
-                                    //    job = $"{tempShool.name}-顾问",
-                                    //    size = 0,
-                                    //    createTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()
-                                    //};
-
-                                    //var resScTch = await cosmosClient.GetContainer(Constant.TEAMModelOS, "School").ReadItemStreamAsync($"{itemTeacher}", new PartitionKey($"Teacher-{tempShool.id}"));
-                                    //if(resScTch.)
-
-                                    ////添加学校学校顾问
-                                    //await cosmosClient.GetContainer(Constant.TEAMModelOS, "School").CreateItemAsync<SchoolTeacher>(addSchoolTeacher, new PartitionKey($"Teacher-{tempShool.id}"));
-                                }
-
-                                //查询该教师是否存在该校
-                                SchoolTeacher schoolTeacher = null;
-                                var resScTch = await cosmosClient.GetContainer(Constant.TEAMModelOS, "School").ReadItemStreamAsync($"{itemTeacher}", new PartitionKey($"Teacher-{tempShool.id}"));
-                                if (resScTch.Status == 200)
-                                {
-                                    using var tchJson = await JsonDocument.ParseAsync(resScTch.ContentStream);
-                                    schoolTeacher = tchJson.ToObject<SchoolTeacher>();
-                                }
-
-                                if (schoolTeacher != null)
-                                {
-                                    if (!schoolTeacher.roles.Contains("assist"))
-                                    {
-                                        schoolTeacher.roles.Add("assist");
-                                        //添加顾问权限
-                                        await cosmosClient.GetContainer(Constant.TEAMModelOS, "School").ReplaceItemAsync<SchoolTeacher>(schoolTeacher, schoolTeacher.id, new PartitionKey($"Teacher-{tempShool.id}"));
-                                    }
-                                }
-                                else
-                                {
-                                    SchoolTeacher addSchoolTeacher = new()
-                                    {
-                                        id = itemTeacher,
-                                        code = $"Teacher-{tempShool.id}",
-                                        pk = "Teacher",
-                                        status = "join",
-                                        roles = new List<string>() { "assist" },
-                                        name = tempTeacher.name,
-                                        job = $"{tempShool.name}-顾问",
-                                        size = 0,
-                                        createTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()
-                                    };
-                                    //添加学校顾问
-                                    await cosmosClient.GetContainer(Constant.TEAMModelOS, "School").CreateItemAsync<SchoolTeacher>(addSchoolTeacher, new PartitionKey($"Teacher-{tempShool.id}"));
-                                }
+                                biRel.assists.Add(item);
                             }
                         }
+
+                        if (respRel.Status == 200)
+                            await cosmosClient.GetContainer(Constant.TEAMModelOS, "School").ReplaceItemAsync<BIRelation>(biRel, biRel.id, new PartitionKey("BIRel"));
+                        else
+                            await cosmosClient.GetContainer(Constant.TEAMModelOS, "School").CreateItemAsync<BIRelation>(biRel, new PartitionKey("BIRel"));
                     }
+
+                    ////需要重大修改后移除
+                    //if (assistId.Count > 0)
+                    //{
+                    //    //修改学校顾问
+                    //    string sqlTxt = $"SELECT value(c) From c WHERE ARRAY_CONTAINS(c.roles,'assist',true)";
+                    //    List<SchoolTeacher> schoolTeachers = new();
+                    //    await foreach (var item in cosmosClient.GetContainer("TEAMModelOS", "School").GetItemQueryIterator<SchoolTeacher>(queryText: sqlTxt, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Teacher-{tempShool.id}") }))
+                    //    {
+                    //        if (!assistId.Contains(item.id))
+                    //        {
+                    //            if (item.roles.Contains("assist"))
+                    //            {
+                    //                item.roles.Remove("assist");
+                    //                if (item.roles.Count > 0)
+                    //                {
+                    //                    await cosmosClient.GetContainer("TEAMModelOS", "School").ReplaceItemAsync<SchoolTeacher>(item, item.id, new PartitionKey(item.code));
+                    //                }
+                    //                else
+                    //                {
+                    //                    await cosmosClient.GetContainer("TEAMModelOS", "School").DeleteItemAsync<SchoolTeacher>(item.id, new PartitionKey(item.code));
+                    //                }
+                    //            }
+                    //        }
+                    //    }
+
+                    //    foreach (var itemTeacher in assistId)
+                    //    {
+                    //        Teacher tempTeacher = await cosmosClient.GetContainer(Constant.TEAMModelOS, "Teacher").ReadItemAsync<Teacher>($"{itemTeacher}", new PartitionKey("Base"));
+                    //        if (tempTeacher != null)
+                    //        {
+                    //            var haveTeacher = tempTeacher.schools.Find(x => x.schoolId.Equals($"{_schoolId}"));
+                    //            if (haveTeacher == null)
+                    //            {
+                    //                Teacher.TeacherSchool teacherSchool = new()
+                    //                {
+                    //                    schoolId = tempShool.id,
+                    //                    name = tempShool.name,
+                    //                    status = "join",
+                    //                    time = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
+                    //                    picture = tempShool.picture,
+                    //                    areaId = tempShool.areaId
+                    //                };
+                    //                tempTeacher.schools.Add(teacherSchool);
+                    //                //给醍摩豆顾问添加学校
+                    //                await cosmosClient.GetContainer(Constant.TEAMModelOS, "Teacher").ReplaceItemAsync<Teacher>(tempTeacher, tempTeacher.id, new PartitionKey($"Base"));
+
+                    //                ////不存在则在原来的基础上添加顾问角色
+                    //                //SchoolTeacher addSchoolTeacher = new()
+                    //                //{
+                    //                //    id = itemTeacher,
+                    //                //    code = $"Teacher-{tempShool.id}",
+                    //                //    pk = "Teacher",
+                    //                //    status = "join",
+                    //                //    roles = new List<string>() { "assist" },
+                    //                //    name = tempTeacher.name,
+                    //                //    job = $"{tempShool.name}-顾问",
+                    //                //    size = 0,
+                    //                //    createTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()
+                    //                //};
+
+                    //                //var resScTch = await cosmosClient.GetContainer(Constant.TEAMModelOS, "School").ReadItemStreamAsync($"{itemTeacher}", new PartitionKey($"Teacher-{tempShool.id}"));
+                    //                //if(resScTch.)
+
+                    //                ////添加学校学校顾问
+                    //                //await cosmosClient.GetContainer(Constant.TEAMModelOS, "School").CreateItemAsync<SchoolTeacher>(addSchoolTeacher, new PartitionKey($"Teacher-{tempShool.id}"));
+                    //            }
+
+                    //            //查询该教师是否存在该校
+                    //            SchoolTeacher schoolTeacher = null;
+                    //            var resScTch = await cosmosClient.GetContainer(Constant.TEAMModelOS, "School").ReadItemStreamAsync($"{itemTeacher}", new PartitionKey($"Teacher-{tempShool.id}"));
+                    //            if (resScTch.Status == 200)
+                    //            {
+                    //                using var tchJson = await JsonDocument.ParseAsync(resScTch.ContentStream);
+                    //                schoolTeacher = tchJson.ToObject<SchoolTeacher>();
+                    //            }
+
+                    //            if (schoolTeacher != null)
+                    //            {
+                    //                if (!schoolTeacher.roles.Contains("assist"))
+                    //                {
+                    //                    schoolTeacher.roles.Add("assist");
+                    //                    //添加顾问权限
+                    //                    await cosmosClient.GetContainer(Constant.TEAMModelOS, "School").ReplaceItemAsync<SchoolTeacher>(schoolTeacher, schoolTeacher.id, new PartitionKey($"Teacher-{tempShool.id}"));
+                    //                }
+                    //            }
+                    //            else
+                    //            {
+                    //                SchoolTeacher addSchoolTeacher = new()
+                    //                {
+                    //                    id = itemTeacher,
+                    //                    code = $"Teacher-{tempShool.id}",
+                    //                    pk = "Teacher",
+                    //                    status = "join",
+                    //                    roles = new List<string>() { "assist" },
+                    //                    name = tempTeacher.name,
+                    //                    job = $"{tempShool.name}-顾问",
+                    //                    size = 0,
+                    //                    createTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()
+                    //                };
+                    //                //添加学校顾问
+                    //                await cosmosClient.GetContainer(Constant.TEAMModelOS, "School").CreateItemAsync<SchoolTeacher>(addSchoolTeacher, new PartitionKey($"Teacher-{tempShool.id}"));
+                    //            }
+                    //        }
+                    //    }
+                    //}
+                
                 }
 
                 //保存操作记录

+ 6 - 1
TEAMModelBI/Controllers/RepairApi/SchoolRepController.cs

@@ -154,7 +154,12 @@ namespace TEAMModelBI.Controllers.RepairApi
 
                         school.period.Add(new Period
                         {
-
+                            id = Guid.NewGuid().ToString(),
+                            name = scName,
+                            grades = new List<string>() { name },
+                            campusId = campusId,
+                            subjects = subjects,
+                            semesters = new List<Semester>() { new Semester { name = schoolConfig.semester[0].term, start = schoolConfig.semester[0].start, month = schoolConfig.semester[0].month, day = schoolConfig.semester[0].day, id = Guid.NewGuid().ToString() } }
                         });
 
                         if (!school.campuses.Select(x => x.id).Contains(campusId))

+ 11 - 1
TEAMModelOS.FunctionV4/HttpTrigger/BIHttpTrigger.cs

@@ -18,6 +18,7 @@ using TEAMModelOS.SDK.Extension;
 using TEAMModelOS.SDK.Models;
 using TEAMModelOS.SDK.Models.Cosmos.BI;
 using TEAMModelOS.SDK.Models.Cosmos.BI.BISchool;
+using TEAMModelOS.SDK.Models.Service.BI;
 using TEAMModelOS.SDK.Models.Service.BIStatsWay;
 
 namespace TEAMModelOS.FunctionV4.HttpTrigger
@@ -39,6 +40,11 @@ namespace TEAMModelOS.FunctionV4.HttpTrigger
             _httpClient = httpClient;
         }
 
+        /// <summary>
+        /// 学校信息BI中间件更新
+        /// </summary>
+        /// <param name="req"></param>
+        /// <returns></returns>
         [Function("set-sc-birelation")]
         public async Task<HttpResponseData> upSchoolBIRelation([HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequestData req)
         {
@@ -69,8 +75,11 @@ namespace TEAMModelOS.FunctionV4.HttpTrigger
 
                 }
                 else
-                    bIRelation.id = school.name;
+                    bIRelation.id = school.id;
 
+                string aName = null;
+                if (!string.IsNullOrEmpty($"{school.areaId}"))
+                    aName = await CosmosQueryHelper.GetStr(cosmosClient, "Normal", $"select value(c.name) from c where c.id='{school.areaId}'", "Base-Area");
                 bIRelation.name = school.name;
                 bIRelation.picture = school.picture;
                 bIRelation.region = school.region;
@@ -82,6 +91,7 @@ namespace TEAMModelOS.FunctionV4.HttpTrigger
                 bIRelation.size = school.size;
                 bIRelation.scale = school.scale;
                 bIRelation.upDate = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
+                bIRelation.areaName = aName;
 
                 if (resBiRel.Status == 200)
                 {

+ 333 - 0
TEAMModelOS.SDK/Models/Service/BI/CosmosQueryHelper.cs

@@ -0,0 +1,333 @@
+using Azure.Cosmos;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Text.Json;
+using System.Threading.Tasks;
+
+namespace TEAMModelOS.SDK.Models.Service.BI
+{
+    public class CosmosQueryHelper
+    {
+        /// <summary>
+        /// 查询学校教师角色列表
+        /// </summary>
+        /// <param name="cosmosClient"></param>
+        /// <param name="schoolId">学校Id</param>
+        /// <param name="roles">查询的角色</param>
+        /// <returns>返回学校角色列表</returns>
+        public static async Task<List<SchoolTeacherRoles>> FindSchoolRoles(CosmosClient cosmosClient, string schoolId, string roles)
+        {
+            List<SchoolTeacherRoles> strs = new();
+            try
+            {
+                string managerSql = $"SELECT DISTINCT REPLACE(c.code, 'Teacher-', '') AS schoolId, c.id, c.name FROM c WHERE ARRAY_CONTAINS(c.roles, '{roles}', true) AND c.pk = 'Teacher' AND c.status = 'join' AND c.code = 'Teacher-{schoolId}'";
+                await foreach (var item in cosmosClient.GetContainer("TEAMModelOS", "School").GetItemQueryStreamIterator(queryText: managerSql, requestOptions: new QueryRequestOptions() { }))
+                {
+                    using var json = await JsonDocument.ParseAsync(item.ContentStream);
+                    foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
+                    {
+                        SchoolTeacherRoles str = new()
+                        {
+                            tmdId = obj.GetProperty("id").GetString(),
+                            tmdName = obj.GetProperty("name").GetString()
+                        };
+
+                        strs.Add(str);
+                    }
+                }
+                return strs;
+            }
+            catch
+            {
+                return strs;
+            }
+        }
+
+        /// <summary>
+        /// 通过醍摩豆账户查询关联学校ID
+        /// </summary>
+        /// <param name="cosmosClient">cosmosDB连接</param>
+        /// <param name="tmdId">醍摩豆账户</param>
+        /// <returns>返回顾问相关的学校ID集合</returns>
+        public static async Task<List<string>> FindSchoolIds(CosmosClient cosmosClient, string tmdId, string roles = "assist", bool isMany = false)
+        {
+            List<string> schoolIds = new();
+            //string schoolSql = $"SELECT DISTINCT REPLACE(c.code,'Teacher-','') AS schoolId,c.code,c.roles,c.id,c.name From c where ARRAY_CONTAINS(c.roles,'assist',true) AND c.status = 'join' AND c.id='{tmdId}'";
+            //await foreach (var item in cosmosClient.GetContainer("TEAMModelOS", "School").GetItemQueryStreamIterator(queryText: schoolSql, requestOptions: new QueryRequestOptions() { }))
+            //{
+            //    using var json = await JsonDocument.ParseAsync(item.ContentStream);
+            //    foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
+            //    {
+            //        schoolIds.Add(obj.GetProperty("schoolId").GetString());
+            //    }
+            //}
+
+            StringBuilder schoolSql = new($"SELECT value(REPLACE(c.code, 'Teacher-', '')) FROM c where c.pk='Teacher' and c.id='{tmdId}'");
+            if (isMany == true)
+                schoolSql.Append($" and (array_contains(c.roles,'assist',true) or array_contains(c.roles,'sales',true))");
+            else
+                schoolSql.Append($" and array_contains(c.roles,'{roles}',true)");
+            await foreach (var item in cosmosClient.GetContainer("TEAMModelOS", "School").GetItemQueryIterator<string>(queryText: schoolSql.ToString(), requestOptions: new QueryRequestOptions() { }))
+            {
+                schoolIds.Add(item);
+            }
+
+            return schoolIds;
+        }
+
+        /// <summary>
+        /// 通过sql语句查询单列集合
+        /// </summary>
+        /// <param name="cosmosClient">连接字符</param>
+        /// <param name="container">容器名称</param>
+        /// <param name="sqlTxt">sql语句 带value</param>
+        /// <param name="code"></param>
+        /// <returns></returns>
+        public static async Task<List<string>> GetValueSingle(CosmosClient cosmosClient, string container, string sqlTxt, string code = null)
+        {
+            List<string> ids = new();
+            await foreach (var item in cosmosClient.GetContainer("TEAMModelOS", container).GetItemQueryIterator<string>(queryText: sqlTxt, requestOptions: !string.IsNullOrEmpty(code) ? new QueryRequestOptions() { PartitionKey = new PartitionKey(code) } : new QueryRequestOptions() { }))
+            {
+                ids.Add(item);
+            }
+
+            return ids;
+        }
+
+        /// <summary>
+        /// 通过sql语句查询单列集合
+        /// </summary>
+        /// <param name="cosmosClient"></param>
+        /// <param name="container"></param>
+        /// <param name="sqlTxt"></param>
+        /// <param name="single"></param>
+        /// <param name="code"></param>
+        /// <returns></returns>
+        public async Task<List<string>> GetStreamSingle(CosmosClient cosmosClient, string container, string sqlTxt, string single = "id", string code = null)
+        {
+            List<string> ids = new();
+            await foreach (var item in cosmosClient.GetContainer("TEAMModelOS", container).GetItemQueryStreamIterator(queryText: sqlTxt, requestOptions: !string.IsNullOrEmpty(code) ? new QueryRequestOptions() { PartitionKey = new PartitionKey(code) } : new QueryRequestOptions() { }))
+            {
+                using var json = await JsonDocument.ParseAsync(item.ContentStream);
+                foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
+                {
+                    ids.Add(obj.GetProperty(single).GetString());
+                }
+            }
+
+            return ids;
+        }
+
+        /// <summary>
+        /// 通过语句查询学校ID
+        /// </summary>
+        /// <param name="cosmosClient">cosmosDB连接</param>
+        /// <param name="sqlTxt">sql语句</param>
+        /// <param name="code">数据分区键</param>
+        /// <returns>返回学校ID的集合</returns>
+        public static async Task<List<string>> FindScIds(CosmosClient cosmosClient, string sqlTxt, string code)
+        {
+            List<string> schoolIds = new();
+            await foreach (var item in cosmosClient.GetContainer("TEAMModelOS", "School").GetItemQueryIterator<string>(queryText: sqlTxt, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey(code) }))
+            {
+                schoolIds.Add(item);
+            }
+
+            //await foreach (var item in cosmosClient.GetContainer("TEAMModelOS", "School").GetItemQueryStreamIterator(queryText: sqlTxt, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey(code) }))
+            //{
+            //    using var json = await JsonDocument.ParseAsync(item.ContentStream);
+            //    foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
+            //    {
+            //        schoolIds.Add(obj.GetProperty("id").GetString());
+            //    }
+            //}
+
+            return schoolIds;
+        }
+
+        /// <summary>
+        /// 依据学校查询教师列表
+        /// </summary>
+        /// <param name="cosmosClient"></param>
+        /// <param name="schools">学校列表</param>
+        /// <param name="roles">不传默认教师角色</param>
+        /// <returns></returns>
+        public static async Task<List<string>> FindRolesId(CosmosClient cosmosClient, List<string> schools, string roles = null)
+        {
+            string rolesName = "teacher";
+            if (roles != null)
+            {
+                rolesName = roles;
+            }
+
+            List<string> teachers = new();
+            foreach (var school in schools)
+            {
+                string sqlTxt = $"select value(c.id) from c where ARRAY_CONTAINS(c.roles,'{rolesName}',true) and c.status = 'join'";
+                await foreach (var itemTeac in cosmosClient.GetContainer("TEAMModelOS", "School").GetItemQueryIterator<string>(queryText: sqlTxt, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Teacher-{school}") }))
+                {
+                    teachers.Add(itemTeac);
+                }
+                //string sqlTxt = $"select c.id from c where ARRAY_CONTAINS(c.roles,'{rolesName}',true) and c.status = 'join'";
+                //await foreach (var itemTeac in cosmosClient.GetContainer("TEAMModelOS", "School").GetItemQueryStreamIterator(queryText: sqlTxt, requestOptions: new QueryRequestOptions() {PartitionKey =new PartitionKey($"Teacher-{school}") }))
+                //{
+                //    using var json = await JsonDocument.ParseAsync(itemTeac.ContentStream);
+                //    foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
+                //    {
+                //        teachers.Add(obj.GetProperty("id").GetString());
+                //    }
+                //}
+            }
+            return teachers;
+        }
+
+        /// <summary>
+        /// 单个容器数据统计
+        /// </summary>
+        /// <param name="cosmosClient"></param>
+        /// <param name="container"></param>
+        /// <param name="SqlTxt"></param>
+        /// <param name="code"></param>
+        /// <returns></returns>
+        public static async Task<int> GetSqlValueCount(CosmosClient cosmosClient, string container, string SqlTxt, string code = null)
+        {
+            int totals = 0;
+            try
+            {
+                await foreach (var item in cosmosClient.GetContainer("TEAMModelOS", container).GetItemQueryIterator<int>(queryText: SqlTxt, requestOptions: string.IsNullOrEmpty(code) ? new QueryRequestOptions() { } : new QueryRequestOptions() { PartitionKey = new PartitionKey($"{code}") }))
+                {
+                    totals = item;
+                }
+            }
+            catch { }
+
+            return totals;
+        }
+
+        /// <summary>
+        /// 多个容器数据统计
+        /// </summary>
+        /// <param name="cosmosClient"></param>
+        /// <param name="containers"></param>
+        /// <param name="SqlTxt"></param>
+        /// <param name="code"></param>
+        /// <returns></returns>
+        public static async Task<int> GetSqlValueCount(CosmosClient cosmosClient, List<string> containers, string SqlTxt, string code = null)
+        {
+            int totals = 0;
+            foreach (var container in containers)
+            {
+                await foreach (var item in cosmosClient.GetContainer("TEAMModelOS", container).GetItemQueryIterator<int>(queryText: SqlTxt, requestOptions: string.IsNullOrEmpty(code) ? new QueryRequestOptions() { } : new QueryRequestOptions() { PartitionKey = new PartitionKey(code) }))
+                {
+                    totals += item;
+                }
+            }
+
+            return totals;
+        }
+
+        /// <summary>
+        /// 单个容器数据统计  double
+        /// </summary>
+        /// <param name="cosmosClient"></param>
+        /// <param name="container"></param>
+        /// <param name="SqlTxt"></param>
+        /// <param name="code"></param>
+        /// <returns></returns>
+        public static async Task<double> GetSqlValueDoubleCounnt(CosmosClient cosmosClient, string container, string SqlTxt, string code = null)
+        {
+            double totals = 0;
+            await foreach (var item in cosmosClient.GetContainer("TEAMModelOS", container).GetItemQueryIterator<double>(queryText: SqlTxt, requestOptions: string.IsNullOrEmpty(code) ? new QueryRequestOptions() { } : new QueryRequestOptions() { PartitionKey = new PartitionKey(code) }))
+            {
+                totals = item;
+            }
+
+            return totals;
+        }
+
+        /// <summary>
+        /// 多个容器数据统计  double
+        /// </summary>
+        /// <param name="cosmosClient"></param>
+        /// <param name="container"></param>
+        /// <param name="SqlTxt"></param>
+        /// <param name="code"></param>
+        /// <returns></returns>
+        public static async Task<double> GetSqlValueDoubleCounnt(CosmosClient cosmosClient, List<string> containers, string SqlTxt, string code = null)
+        {
+            double totals = 0;
+            foreach (var container in containers)
+            {
+                await foreach (var item in cosmosClient.GetContainer("TEAMModelOS", container).GetItemQueryIterator<double>(queryText: SqlTxt, requestOptions: string.IsNullOrEmpty(code) ? new QueryRequestOptions() { } : new QueryRequestOptions() { PartitionKey = new PartitionKey(code) }))
+                {
+                    totals += item;
+                }
+            }
+
+            return totals;
+        }
+
+        /// <summary>
+        /// 通过SQL 语句返回实体信息
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="cosmosClient"></param>
+        /// <param name="containers"></param>
+        /// <param name="sqlTxt"></param>
+        /// <param name="code"></param>
+        /// <returns></returns>
+        public static async Task<List<T>> GetObject<T>(CosmosClient cosmosClient, List<string> containers, string sqlTxt, string code = null)
+        {
+            List<T> temps = new();
+            foreach (var container in containers)
+            {
+                await foreach (var items in cosmosClient.GetContainer("TEAMModelOS", container).GetItemQueryIterator<T>(queryText: sqlTxt, requestOptions: string.IsNullOrEmpty(code) ? new QueryRequestOptions() { } : new QueryRequestOptions() { PartitionKey = new PartitionKey(code) }))
+                {
+                    temps.Add(items);
+                }
+            }
+            return temps;
+        }
+
+        /// <summary>
+        /// 通过SQL 语句返回实体信息
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="cosmosClient"></param>
+        /// <param name="containers"></param>
+        /// <param name="sqlTxt"></param>
+        /// <param name="code"></param>
+        /// <returns></returns>
+        public static async Task<List<T>> GetObject<T>(CosmosClient cosmosClient, string containers, string sqlTxt, string code = null)
+        {
+            List<T> temps = new();
+            await foreach (var items in cosmosClient.GetContainer("TEAMModelOS", containers).GetItemQueryIterator<T>(queryText: sqlTxt, requestOptions: string.IsNullOrEmpty(code) ? new QueryRequestOptions() { } : new QueryRequestOptions() { PartitionKey = new PartitionKey(code) }))
+            {
+                temps.Add(items);
+            }
+            return temps;
+        }
+
+        /// <summary>
+        /// 查询某个字段信息
+        /// </summary>
+        /// <param name="cosmosClient"></param>
+        /// <param name="containers"></param>
+        /// <param name="sqlTxt"></param>
+        /// <param name="code"></param>
+        /// <returns></returns>
+        public static async Task<string> GetStr(CosmosClient cosmosClient, string containers, string sqlTxt, string code = null)
+        {
+            string str = null;
+
+            await foreach (var item in cosmosClient.GetContainer("TEAMModelOS", containers).GetItemQueryIterator<string>(queryText: sqlTxt, requestOptions: string.IsNullOrEmpty(code) ? new QueryRequestOptions() { } : new QueryRequestOptions() { PartitionKey = new PartitionKey(code) }))
+            {
+                str = item;
+            }
+
+            return str;
+        }
+    }
+}

+ 2 - 2
TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContentTypeTemplate/PaperViewBox/LessonTestReport.vue

@@ -100,7 +100,7 @@
                         {{$t("studentWeb.exam.report.noScore")}}: {{rightAns.noAns}}
                     </Checkbox> -->
                 </CheckboxGroup>
-                <p style="float: right" v-if="!examInfo.qamode">
+                <p style="float: right">
                     <span style="margin-right: 10px">{{ $t("studentWeb.exam.report.wrongPractice") }}:</span>
                     <Dropdown @on-click="itemChange" style="margin-right: 30px;">
                         <a href="javascript:void(0)">
@@ -113,7 +113,7 @@
                             </DropdownItem>
                         </DropdownMenu>
                     </Dropdown>
-                    <Checkbox v-model="choiceQues" style="margin-right: 30px;">
+                    <Checkbox v-model="choiceQues" style="margin-right: 30px;" v-if="!examInfo.qamode">
                         {{ $t("studentWeb.exam.disorder") }}
                     </Checkbox>
                     <button :class="['wrong-exercises',]" @click="practiceQues">

+ 28 - 1
TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContentTypeTemplate/PaperViewBox/PaperTest.less

@@ -145,12 +145,39 @@
             display: none;
             text-align: center;
         }
+
+        .img-wrap {
+            width: 40%;
+            margin-left: 10px;
+            margin-top: 20px;
+
+            .left-img {
+                text-align: center;
+
+                &>img {
+                    max-width: 95%;
+                }
+            }
+
+            .page-footer {
+                text-align: center;
+                padding: 10px 0;
+            }
+        }
+    }
+
+    .qamode-area {
+        display: flex;
     }
 
     .questionContent {
         padding: 5px 50px;
     }
 
+    .qamode-box {
+        width: 58%;
+    }
+
     .questionType {
         border: 2px solid #979797;
         color: #6d7278;
@@ -359,7 +386,7 @@
             font-size: 17px;
 
             .explain-title {
-                width: 12%;
+                width: 13%;
                 max-width: 130px;
                 min-width: 100px;
                 display: inline-block;

+ 204 - 170
TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContentTypeTemplate/PaperViewBox/PaperTest.vue

@@ -120,204 +120,221 @@
         </div>
         <Row :gutter="30">
             <!-- 题干 -->
-            <i-col class="questionArea"
+            <i-col :class="['questionArea', {'qamode-area': paperData.qamode}]"
                    :xs="24"
                    :sm="24"
                    :md="18"
                    :lg="20"
             >
-                <!--切換頁-->
-                <div class="small-view">
-                    <div class="countDown" v-if="needCountDown">
-                        <div>{{$t("studentWeb.exam.examTime")}}:{{ surplus }}</div>
-                    </div>
-                    <div class="pageCtl2">
-                        <button @click="preQ()" :disabled="!queNo" :class="{disable: !queNo}">
-                            <Icon type="ios-arrow-back" />{{$t("studentWeb.exam.testpop.previous")}}
-                        </button>
-                        <button @click="showPage(true)">
-                            {{$t("studentWeb.exam.testpop.showPage")}}
-                        </button>
-                        <button @click="nextQ()"
-                                :disabled="queNo == (showExam.length-1)"
-                                :class="{ hintClick:queNo != (showExam.length-1) && checkers[queNo] != '',disable: queNo == (showExam.length-1)}">
-                            {{$t("studentWeb.exam.testpop.next")}}
-                            <Icon type="ios-arrow-forward" />
-                        </button>
-                    </div>
+                <div class="img-wrap" v-if="paperData.qamode">
+                    <Carousel v-model="CarouselIndex">
+                        <CarouselItem v-for="(img, indexs) in imgList" :key="indexs">
+                            <!-- 不是综合题 -->
+                            <div class="left-img" @click="$hevueImgPreview(img)">
+                                <img :src="img" alt="">
+                            </div>
+                        </CarouselItem>
+                    </Carousel>
+                    <p class="page-footer">
+                        {{ $t('answerSheet.tip2') }} {{ CarouselIndex + 1 }} {{ $t('answerSheet.tip21') }}
+                        /
+                        {{ $t('answerSheet.tip9') }} {{ imgList.length }} {{ $t('answerSheet.tip21') }}
+                    </p>
                 </div>
-                <div class="questionContent" ref="questionBox" v-if="showExam.length">
-                    <!-- <span class="hintwrap">
-                        <Tooltip :content="$t('studentWeb.exam.report.noSource')" theme="light" placement="left">
-                            <span class="hintIcon">
-                                <svg-icon icon-class="hint" />
-                            </span>
-                        </Tooltip>
-                    </span> -->
-                    <div class="questioDes">
-                        <div class="questionType" v-if="getQue(queNo).parent === undefined">
-                            <span>{{getTestType(getQue(queNo).type)}}</span>
-                        </div>
-                        <div class="que-item" v-if="getQue(queNo).type != 'compose' && getQue(queNo).parent === undefined">
-                            <span v-if="isWrong" @click="changeStar(!getQue(queNo).star, queNo)" style="margin-right: 10px; cursor: pointer;">
-                                <Icon custom="iconfont icon-shoucang1" size="25" v-show="!getQue(queNo).star" />
-                                <Icon custom="iconfont icon-shoucang2" size="25" color="#FF7A4E" v-show="getQue(queNo).star" />
-                            </span>
-                            <span>{{ queNo + 1 }}.</span>
-                            <!--题目渲染-->
-                            <div id="answer-box" v-html="getQue(queNo).question"></div>
+                <template>
+                    <!--切換頁-->
+                    <div class="small-view">
+                        <div class="countDown" v-if="needCountDown">
+                            <div>{{$t("studentWeb.exam.examTime")}}:{{ surplus }}</div>
                         </div>
-                        <!--综合题-->
-                        <div v-if="getQue(queNo).type === 'compose' || getQue(queNo).parent !== undefined">
-                            <div class="compose-content" v-if="getQue(queNo).parent !== undefined">
-                                <div class="questionType">
-                                    <span>{{getTestType(getQue(queNo).parentInfo.type)}}</span>
-                                </div>
-                                <div class="compose-box">
-                                    <span v-if="isWrong" @click="changeStar(!getQue(queNo).star, queNo, true)" style="cursor: pointer;">
-                                        <Icon custom="iconfont icon-shoucang1" size="25" v-show="!getQue(queNo).star" />
-                                        <Icon custom="iconfont icon-shoucang2" size="25" color="#FF7A4E" v-show="getQue(queNo).star" />
-                                    </span>
-                                    <span style="margin-left:10px;font-weight:800;font-size:15px">{{$t("studentWeb.exam.testpop.queNo")}}</span>
-                                    <br />
-                                    <div class="compose-item">
-                                        <!--<span w>{{ getQue(queNo).parent + 1}}.</span>-->
-                                        <!--题干渲染-->
-                                        <div v-html="getQue(queNo).parentInfo.question"></div>
-                                    </div>
-                                </div>
-                                <div class="que-content">
-                                    <span style="width:51px">{{ getQue(queNo).paperIndex}}:</span>
-                                    <!--题目渲染-->
-                                    <div class="que-items" id="answer-box" v-html="getQue(queNo).question"></div>
-                                </div>
-                            </div>
+                        <div class="pageCtl2">
+                            <button @click="preQ()" :disabled="!queNo" :class="{disable: !queNo}">
+                                <Icon type="ios-arrow-back" />{{$t("studentWeb.exam.testpop.previous")}}
+                            </button>
+                            <button @click="showPage(true)">
+                                {{$t("studentWeb.exam.testpop.showPage")}}
+                            </button>
+                            <button @click="nextQ()"
+                                    :disabled="queNo == (showExam.length-1)"
+                                    :class="{ hintClick:queNo != (showExam.length-1) && checkers[queNo] != '',disable: queNo == (showExam.length-1)}">
+                                {{$t("studentWeb.exam.testpop.next")}}
+                                <Icon type="ios-arrow-forward" />
+                            </button>
                         </div>
                     </div>
-                    <div class="answers">
-                        <div class="questionNo">{{$t("studentWeb.exam.testpop.myAns")}}</div>
-                        <div class="answers-box">
-                            <!--问答题-->
-                            <div class="compose-content" v-if="getQue(queNo).type != 'judge' && getQue(queNo).type != 'single'  && getQue(queNo).type != 'multiple'">
-                                <Compose ref="compose" :itemInfo="getQue(queNo)" :close="!closeTest" :textData="checkers[queNo]" :index="queNo" @dataGet="getComposeAns"></Compose>
+                    <div :class="['questionContent', {'qamode-box': paperData.qamode}]" ref="questionBox" v-if="showExam.length">
+                        <!-- <span class="hintwrap">
+                            <Tooltip :content="$t('studentWeb.exam.report.noSource')" theme="light" placement="left">
+                                <span class="hintIcon">
+                                    <svg-icon icon-class="hint" />
+                                </span>
+                            </Tooltip>
+                        </span> -->
+                        <div class="questioDes">
+                            <div class="questionType" v-if="getQue(queNo).parent === undefined">
+                                <span>{{getTestType(getQue(queNo).type)}}</span>
                             </div>
-                            <!--判断题选项-->
-                            <div v-if="getQue(queNo).type == 'judge'" align="center">
-                                <!-- <div class="testButn">
-                                    <Radio-group v-model="checkers[queNo][0]">
-                                        <Radio label="A"><Icon type="md-checkmark" /></Radio>
-                                        <Radio label="B"><Icon type="md-close" /></Radio>
-                                    </Radio-group>
-                                </div> -->
-                                <label class="testBtn yesNoBtn">
-                                    <input type="radio" value="A" v-model="checkers[queNo][0]" :disabled="!closeTest" />
-                                    <div class="testbg" :style="{'background-color': isWrong && showEnd && checkers[queNo][0] === 'A' ? (getQue(queNo).answer[0] === 'A' ? '' : 'red !important') : ''}">
-                                        <Icon type="ios-radio-button-off" />
-                                    </div>
-                                </label>
-                                <label class="testBtn yesNoBtn">
-                                    <input type="radio" value="B" v-model="checkers[queNo][0]" :disabled="!closeTest" />
-                                    <div class="testbg" :style="{'background-color': isWrong && showEnd && checkers[queNo][0] === 'B' ? (getQue(queNo).answer[0] === 'B' ? '' : 'red !important') : ''}">
-                                        <Icon type="md-close" />
-                                    </div>
-                                </label>
+                            <div class="que-item" v-if="getQue(queNo).type != 'compose' && getQue(queNo).parent === undefined">
+                                <span v-if="isWrong" @click="changeStar(!getQue(queNo).star, queNo)" style="margin-right: 10px; cursor: pointer;">
+                                    <Icon custom="iconfont icon-shoucang1" size="25" v-show="!getQue(queNo).star" />
+                                    <Icon custom="iconfont icon-shoucang2" size="25" color="#FF7A4E" v-show="getQue(queNo).star" />
+                                </span>
+                                <span>{{ queNo + 1 }}.</span>
+                                <!--题目渲染-->
+                                <div id="answer-box" v-html="getQue(queNo).question"></div>
                             </div>
-                            <!--选择题选项-->
-                            <div class="select-box" v-if="getQue(queNo).type != 'judge'">
-                                <label class="testBtn"
-                                       v-for="(item, index) in getQue(queNo).option"
-                                       :key="index">
-                                    <span v-show="showEnd">
-                                        <Icon type="md-checkmark-circle" color="#24B880" size="20"
-                                            v-if="getQue(queNo).answer.includes(item.code)"
-                                        />
-                                        <Icon type="md-close-circle" color="red" size="20"
-                                            v-if="checkers[queNo].includes(item.code) && !getQue(queNo).answer.includes(item.code)"
-                                        />
-                                    </span>
-                                    <input type="checkbox"
-                                           :value="getQue(queNo).option[index].code"
-                                           v-model="checkers[queNo]"
-                                           @click="getAns(queNo,index)"
-                                           :disabled="!closeTest" />
-                                    <div class="testbg">
-                                        <div style="display:flex">
-                                            <span>{{ getQue(queNo).option[index].code }}.</span>
-                                            <div v-html="item.value" @click="showImg($event)"></div>
+                            <!--综合题-->
+                            <div v-if="getQue(queNo).type === 'compose' || getQue(queNo).parent !== undefined">
+                                <div class="compose-content" v-if="getQue(queNo).parent !== undefined">
+                                    <div class="questionType">
+                                        <span>{{getTestType(getQue(queNo).parentInfo.type)}}</span>
+                                    </div>
+                                    <div class="compose-box">
+                                        <span v-if="isWrong" @click="changeStar(!getQue(queNo).star, queNo, true)" style="cursor: pointer;">
+                                            <Icon custom="iconfont icon-shoucang1" size="25" v-show="!getQue(queNo).star" />
+                                            <Icon custom="iconfont icon-shoucang2" size="25" color="#FF7A4E" v-show="getQue(queNo).star" />
+                                        </span>
+                                        <span style="margin-left:10px;font-weight:800;font-size:15px">{{$t("studentWeb.exam.testpop.queNo")}}</span>
+                                        <br />
+                                        <div class="compose-item">
+                                            <!--<span w>{{ getQue(queNo).parent + 1}}.</span>-->
+                                            <!--题干渲染-->
+                                            <div v-html="getQue(queNo).parentInfo.question"></div>
                                         </div>
-                                        <div style="clear:both"></div>
                                     </div>
-                                </label>
+                                    <div class="que-content">
+                                        <span style="width:51px">{{ getQue(queNo).paperIndex}}:</span>
+                                        <!--题目渲染-->
+                                        <div class="que-items" id="answer-box" v-html="getQue(queNo).question"></div>
+                                    </div>
+                                </div>
                             </div>
                         </div>
-                    </div>
-                    <!-- <div :style="isCheckAns ? 'height: 250px' : ''"></div> -->
-                    <!-- 答案解析 -->
-                    <div v-if="isWrong" :class="['analysis', this.showEnd ? 'active' : '']" @click="changeAnalysisType(true)">
-                        <div class="item-explain">
-                            <span class="explain-title">【{{ $t("studentWeb.exam.report.ansRes") }}】</span>
-                            <div class="item-explain-details">
-                                <span v-if="!checkers[queNo].length">{{ $t('studentWeb.exam.report.noAns') }}</span>
-                                <span v-else v-for="(item, index) in checkers[queNo]" :key="index" v-html="item"></span>
+                        <div class="answers">
+                            <div class="questionNo">{{$t("studentWeb.exam.testpop.myAns")}}</div>
+                            <div class="answers-box">
+                                <!--问答题-->
+                                <div class="compose-content" v-if="getQue(queNo).type != 'judge' && getQue(queNo).type != 'single'  && getQue(queNo).type != 'multiple'">
+                                    <Compose ref="compose" :itemInfo="getQue(queNo)" :close="!closeTest" :textData="checkers[queNo]" :index="queNo" @dataGet="getComposeAns"></Compose>
+                                </div>
+                                <!--判断题选项-->
+                                <div v-if="getQue(queNo).type == 'judge'" align="center">
+                                    <!-- <div class="testButn">
+                                        <Radio-group v-model="checkers[queNo][0]">
+                                            <Radio label="A"><Icon type="md-checkmark" /></Radio>
+                                            <Radio label="B"><Icon type="md-close" /></Radio>
+                                        </Radio-group>
+                                    </div> -->
+                                    <label class="testBtn yesNoBtn">
+                                        <input type="radio" value="A" v-model="checkers[queNo][0]" :disabled="!closeTest" />
+                                        <div class="testbg" :style="{'background-color': isWrong && showEnd && checkers[queNo][0] === 'A' ? (getQue(queNo).answer[0] === 'A' ? '' : 'red !important') : ''}">
+                                            <Icon type="ios-radio-button-off" />
+                                        </div>
+                                    </label>
+                                    <label class="testBtn yesNoBtn">
+                                        <input type="radio" value="B" v-model="checkers[queNo][0]" :disabled="!closeTest" />
+                                        <div class="testbg" :style="{'background-color': isWrong && showEnd && checkers[queNo][0] === 'B' ? (getQue(queNo).answer[0] === 'B' ? '' : 'red !important') : ''}">
+                                            <Icon type="md-close" />
+                                        </div>
+                                    </label>
+                                </div>
+                                <!--选择题选项-->
+                                <div class="select-box" v-if="getQue(queNo).type != 'judge'">
+                                    <label class="testBtn"
+                                        v-for="(item, index) in getQue(queNo).option"
+                                        :key="index">
+                                        <span v-show="showEnd">
+                                            <Icon type="md-checkmark-circle" color="#24B880" size="20"
+                                                v-if="getQue(queNo).answer.includes(item.code)"
+                                            />
+                                            <Icon type="md-close-circle" color="red" size="20"
+                                                v-if="checkers[queNo].includes(item.code) && !getQue(queNo).answer.includes(item.code)"
+                                            />
+                                        </span>
+                                        <input type="checkbox"
+                                            :value="getQue(queNo).option[index].code"
+                                            v-model="checkers[queNo]"
+                                            @click="getAns(queNo,index)"
+                                            :disabled="!closeTest" />
+                                        <div class="testbg">
+                                            <div style="display:flex">
+                                                <span>{{ getQue(queNo).option[index].code }}.</span>
+                                                <div v-html="item.value" @click="showImg($event)"></div>
+                                            </div>
+                                            <div style="clear:both"></div>
+                                        </div>
+                                    </label>
+                                </div>
                             </div>
                         </div>
-                        <div class="item-explain">
-                            <span class="explain-title">【{{ $t("studentWeb.exam.report.testAns") }}】</span>
-                            <div class="item-explain-details">
-                                <span v-if="!showExam[queNo].answer.length">{{ $t('studentWeb.exam.report.noAns') }}</span>
-                                <span v-else v-for="(item, index) in showExam[queNo].answer" :key="index" v-html="item"></span>
+                        <!-- <div :style="isCheckAns ? 'height: 250px' : ''"></div> -->
+                        <!-- 答案解析 -->
+                        <div v-if="isWrong" :class="['analysis', this.showEnd ? 'active' : '']" @click="changeAnalysisType(true)">
+                            <div class="item-explain">
+                                <span class="explain-title">【{{ $t("studentWeb.exam.report.ansRes") }}】</span>
+                                <div class="item-explain-details">
+                                    <span v-if="!checkers[queNo].length">{{ $t('studentWeb.exam.report.noAns') }}</span>
+                                    <span v-else v-for="(item, index) in checkers[queNo]" :key="index" v-html="item"></span>
+                                </div>
                             </div>
-                        </div>
-                        <div class="item-explain">
-                            <span class="explain-title">【{{ $t("studentWeb.exam.report.testAnalyse") }}】</span>
-                            <div class="item-explain-details">
-                                <div v-html="showExam[queNo].explain ? showExam[queNo].explain : $t('studentWeb.exam.report.noAnalyse')"></div>
+                            <div class="item-explain">
+                                <span class="explain-title">【{{ $t("studentWeb.exam.report.testAns") }}】</span>
+                                <div class="item-explain-details">
+                                    <span v-if="!showExam[queNo].answer.length">{{ $t('studentWeb.exam.report.noAns') }}</span>
+                                    <span v-else v-for="(item, index) in showExam[queNo].answer" :key="index" v-html="item"></span>
+                                </div>
                             </div>
-                        </div>
-                        <!-- 知识点 -->
-                        <div class="item-explain">
-                            <span class="explain-title">【{{ $t("studentWeb.exam.report.knowledge") }}】</span>
-                            <div v-if="showExam[queNo].knowledge && showExam[queNo].knowledge.length" class="item-explain-details">
-                                <span v-for="(knowledge, index) in showExam[queNo].knowledge" :key="index" class="knowledge-style">
-                                    {{ knowledge }}
-                                </span>
+                            <div class="item-explain">
+                                <span class="explain-title">【{{ $t("studentWeb.exam.report.testAnalyse") }}】</span>
+                                <div class="item-explain-details">
+                                    <div v-html="showExam[queNo].explain ? showExam[queNo].explain : $t('studentWeb.exam.report.noAnalyse')"></div>
+                                </div>
                             </div>
-                            <div v-else class="item-explain-details">
-                                {{ $t("studentWeb.exam.report.noKnowledge") }}
+                            <!-- 知识点 -->
+                            <div class="item-explain">
+                                <span class="explain-title">【{{ $t("studentWeb.exam.report.knowledge") }}】</span>
+                                <div v-if="showExam[queNo].knowledge && showExam[queNo].knowledge.length" class="item-explain-details">
+                                    <span v-for="(knowledge, index) in showExam[queNo].knowledge" :key="index" class="knowledge-style">
+                                        {{ knowledge }}
+                                    </span>
+                                </div>
+                                <div v-else class="item-explain-details">
+                                    {{ $t("studentWeb.exam.report.noKnowledge") }}
+                                </div>
                             </div>
-                        </div>
-                        <!-- 认知层次 -->
-                        <div class="item-explain">
-                            <span class="explain-title">【{{ $t("studentWeb.exam.report.filed") }}】</span>
-                            <div class="item-explain-details">
-                                <div>{{ levelList[showExam[queNo].field - 1] }}</div>
+                            <!-- 认知层次 -->
+                            <div class="item-explain">
+                                <span class="explain-title">【{{ $t("studentWeb.exam.report.filed") }}】</span>
+                                <div class="item-explain-details">
+                                    <div>{{ levelList[showExam[queNo].field - 1] }}</div>
+                                </div>
                             </div>
-                        </div>
-                        <div class="item-explain">
-                            <span class="explain-title">【{{ $t("studentWeb.exam.report.repairSource") }}】</span>
-                            <div class="item-explain-details-repair">
-                                <div v-if="showExam[queNo].repair.length != 0">
-                                    <div v-for="(repairSource, normalIndex) in showExam[queNo].repair" :key="normalIndex" class="repair-link-wrap-item-box">
-                                        <div class="file-icon">
-                                            <img :src="$tools.getFileThum(repairSource.type, repairSource.name)"/>
-                                        </div>
-                                        <div class="file-info">
-                                            <p class="file-name">{{ repairSource.name }}</p>
-                                            <div>
-                                                <span @click.stop="onPreview(repairSource)" v-if="repairSource.type !== 'other'">{{ $t('ability.review.preview')}}</span>
-                                                <span @click.stop="onDownload(repairSource)" v-if="repairSource.type !== 'link'">{{ $t('ability.review.download')}}</span>
+                            <div class="item-explain">
+                                <span class="explain-title">【{{ $t("studentWeb.exam.report.repairSource") }}】</span>
+                                <div class="item-explain-details-repair">
+                                    <div v-if="showExam[queNo].repair.length != 0">
+                                        <div v-for="(repairSource, normalIndex) in showExam[queNo].repair" :key="normalIndex" class="repair-link-wrap-item-box">
+                                            <div class="file-icon">
+                                                <img :src="$tools.getFileThum(repairSource.type, repairSource.name)"/>
+                                            </div>
+                                            <div class="file-info">
+                                                <p class="file-name">{{ repairSource.name }}</p>
+                                                <div>
+                                                    <span @click.stop="onPreview(repairSource)" v-if="repairSource.type !== 'other'">{{ $t('ability.review.preview')}}</span>
+                                                    <span @click.stop="onDownload(repairSource)" v-if="repairSource.type !== 'link'">{{ $t('ability.review.download')}}</span>
+                                                </div>
                                             </div>
                                         </div>
                                     </div>
-                                </div>
-                                <div v-else-if="!showExam[queNo].repair.length">
-                                    {{ $t('studentWeb.exam.report.noSource') }}
+                                    <div v-else-if="!showExam[queNo].repair.length">
+                                        {{ $t('studentWeb.exam.report.noSource') }}
+                                    </div>
                                 </div>
                             </div>
                         </div>
                     </div>
-                </div>
-                <div v-else style="text-align: center; margin-top: 25%; font-size: 20px;">{{ $t("studentWeb.exam.testpop.noExam") }}</div>
+                    <div v-else style="text-align: center; margin-top: 25%; font-size: 20px;">{{ $t("studentWeb.exam.testpop.noExam") }}</div>
+                </template>
             </i-col>
             <!--答题卡-->
             <i-col class="ansArea" v-show="widthLimit || (!widthLimit && isShow)"
@@ -527,7 +544,7 @@
                 interval: undefined, //计时器
                 showExam: [],
                 totalScore: 0, //练习总分
-                showEnd: false,
+                showEnd: false, //结束练习
                 repeatPractice: false, //再次练习
                 levelList: [ //层次
                     this.$t("evaluation.level1"),
@@ -539,6 +556,8 @@
                 ],
                 isShow: false,
                 widthLimit: false,
+                imgList: [],
+                CarouselIndex: 0,
             };
         },
         methods: {
@@ -688,6 +707,18 @@
                 }
                 this.showExam = this.examInfo
             },
+            async getImgList() {
+                if(!this.getPaperInfo.attachments) {
+                    return
+                }
+                let codes = this.getItemTitle.scope === 'school' ? this.getItemTitle.school : this.getItemTitle.creatorId
+                let sas = await this.$tools.getBlobSas(codes)
+                let blob = this.paperData.blob
+                this.imgList = this.getPaperInfo.attachments.map(item => {
+                    let url = sas.url + "/" + codes + blob + '/' + item + '?' + sas.sas
+                    return url
+                })
+            },
             //獲取富文本返回數據
             getComposeAns(data, index) {
                 // index可能为0
@@ -704,6 +735,9 @@
                 this.paperData = this._.cloneDeep(this.getCurrentSubject)
                 this.isWrong = this.paperData.stuAns[0] ? true : false
                 this.setTime = this.paperData.time > 0 ? true : false
+                if(this.paperData.qamode) {
+                    this.getImgList()
+                }
                 if(this.setTime && !this.isWrong) {
                     this.diffSeconds = JSON.parse(localStorage.getItem("time")) || this.paperData.time * 60
                     this.interval = setInterval(() => {

+ 31 - 1
TEAMModelOS/Controllers/Both/ItemController.cs

@@ -833,7 +833,37 @@ namespace TEAMModelOS.Controllers
                 return BadRequest();
             }
         }
-
+        /// <summary>
+        /// 查找题目
+        /// </summary>
+        /// <param name="request"></param>
+        /// <returns></returns>
+        [ProducesDefaultResponseType]
+        //[AuthToken(Roles = "teacher")]
+        [HttpPost("find-ids")]
+        [Authorize(Roles = "IES")]
+        public async Task<IActionResult> FindIds(JsonElement request) {
+            var client = _azureCosmos.GetCosmosClient();
+            if (!request.TryGetProperty("scope", out JsonElement scope)) return BadRequest();
+            if (!request.TryGetProperty("code", out JsonElement code)) return BadRequest();
+            if (!request.TryGetProperty("ids", out JsonElement _ids)) return BadRequest();
+            string tbname = $"{scope}".Equals("school") ? Constant.School : Constant.Teacher;
+            List<ItemInfo> items= new List<ItemInfo>();
+            List<string> ids = _ids.ToObject<List<string>>();
+            if (ids.IsNotEmpty())
+            {
+                string sql = $"select value c from c where c.id in ({string.Join(",", ids.Select(z => $"'{z}'"))})";
+                await  foreach (var item in client.GetContainer(Constant.TEAMModelOS, tbname).GetItemQueryIterator<ItemInfo>(sql, requestOptions: new QueryRequestOptions { PartitionKey = new PartitionKey($"Item-{code}") })) 
+                {
+                    items.Add(item);
+                }
+                return Ok(new { items });
+            }
+            else {
+                return Ok(new { items });
+            }
+            
+        }
 
         /// <summary>
         /// 手动挑题

+ 40 - 16
TEAMModelOS/Controllers/School/SchoolController.cs

@@ -367,19 +367,20 @@ namespace TEAMModelOS.Controllers
                     using var json = await JsonDocument.ParseAsync(itemsr.ContentStream);
                     if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
                     {
+                        deviceForCoreService uuidForCore;
                         foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
                         {
                             serial.Add(obj.ToObject<SchoolProductSerial>());
                             if (obj.TryGetProperty("deviceBound", out JsonElement deviceBoundJobj) && !string.IsNullOrWhiteSpace(deviceBoundJobj.ToString()))
                             {
-                                foreach (var deviceBoundRow in deviceBoundJobj.EnumerateArray())
+                                foreach (var deviceBoundTmpRow in deviceBoundJobj.EnumerateArray())
                                 {
-                                    deviceForCoreService uuidForCore = new deviceForCoreService();
+                                    uuidForCore = new deviceForCoreService();
                                     uuidForCore.sn = (!string.IsNullOrWhiteSpace(Convert.ToString(obj.GetProperty("serial")))) ? Convert.ToString(obj.GetProperty("serial")) : null;
-                                    uuidForCore.uuid1 = (!string.IsNullOrWhiteSpace(Convert.ToString(deviceBoundRow.GetProperty("uuid")))) ? Convert.ToString(deviceBoundRow.GetProperty("uuid")) : null;
-                                    uuidForCore.uuid2 = (!string.IsNullOrWhiteSpace(Convert.ToString(deviceBoundRow.GetProperty("uuid2")))) ? Convert.ToString(deviceBoundRow.GetProperty("uuid2")) : null;
-                                    uuidForCore.device_id = (!string.IsNullOrWhiteSpace(Convert.ToString(deviceBoundRow.GetProperty("deviceId")))) ? Convert.ToString(deviceBoundRow.GetProperty("deviceId")) : null;
-                                    uuidForCore.class_id = (!string.IsNullOrWhiteSpace(Convert.ToString(deviceBoundRow.GetProperty("classId")))) ? Convert.ToString(deviceBoundRow.GetProperty("classId")) : null;
+                                    uuidForCore.uuid1 = (!string.IsNullOrWhiteSpace(Convert.ToString(deviceBoundTmpRow.GetProperty("uuid")))) ? Convert.ToString(deviceBoundTmpRow.GetProperty("uuid")) : null;
+                                    uuidForCore.uuid2 = (!string.IsNullOrWhiteSpace(Convert.ToString(deviceBoundTmpRow.GetProperty("uuid2")))) ? Convert.ToString(deviceBoundTmpRow.GetProperty("uuid2")) : null;
+                                    uuidForCore.device_id = (!string.IsNullOrWhiteSpace(Convert.ToString(deviceBoundTmpRow.GetProperty("deviceId")))) ? Convert.ToString(deviceBoundTmpRow.GetProperty("deviceId")) : null;
+                                    uuidForCore.class_id = (!string.IsNullOrWhiteSpace(Convert.ToString(deviceBoundTmpRow.GetProperty("classId")))) ? Convert.ToString(deviceBoundTmpRow.GetProperty("classId")) : null;
                                     uuidList.Add(uuidForCore);
                                 }
                             }
@@ -388,10 +389,17 @@ namespace TEAMModelOS.Controllers
                 }
                 ////取得DeviceInfo From Core 並更新序號資料
                 List<deviceFromCoreService> coreUuidList = (List<deviceFromCoreService>)GetDeviceFromCoreAsync(uuidList).GetAwaiter().GetResult();
+                List<deviceBoundExt> deviceBoundArray;
+                List<deviceFromCoreService> coreUuid;
+                deviceForCoreService deviceBoundRow;
+                deviceBoundExt deviceBoundExt;
+                deviceBound serialDeviceBoundRow;
+                SerialInfoBaseWithdeviceBoundExt serialResultRow;
+                List<SchoolProductSerial> updSchoolProductSerialList = new List<SchoolProductSerial>();
                 foreach (SchoolProductSerial serialRow in serial)
                 {
-                    var deviceBoundArray = new List<deviceBoundExt>();
-                    List<deviceFromCoreService> coreUuid = coreUuidList
+                    deviceBoundArray = new List<deviceBoundExt>();
+                    coreUuid = coreUuidList
                            .Where((deviceFromCoreService x) => x.sn == serialRow.serial)
                            .ToList();
                     foreach (deviceFromCoreService deviceRow in coreUuid)
@@ -399,8 +407,8 @@ namespace TEAMModelOS.Controllers
                         if (!string.IsNullOrWhiteSpace(deviceRow.uuid1) || !string.IsNullOrWhiteSpace(deviceRow.uuid2) || !string.IsNullOrWhiteSpace(deviceRow.device_id)) //uuid1、uuid2、device_id 任一欄有值
                         {
                             //前端顯示用
-                            deviceForCoreService deviceBoundRow = uuidList.Where(u => u.sn == deviceRow.sn && u.uuid1 == deviceRow.uuid1 && u.uuid2 == deviceRow.uuid2).FirstOrDefault();
-                            deviceBoundExt deviceBoundExt = new deviceBoundExt();
+                            deviceBoundRow = uuidList.Where(u => u.sn == deviceRow.sn && u.uuid1 == deviceRow.uuid1 && u.uuid2 == deviceRow.uuid2).FirstOrDefault();
+                            deviceBoundExt = new deviceBoundExt();
                             deviceBoundExt.uuid = deviceBoundRow.uuid1;
                             deviceBoundExt.uuid2 = deviceBoundRow.uuid2;
                             deviceBoundExt.classId = deviceBoundRow.class_id;
@@ -412,7 +420,7 @@ namespace TEAMModelOS.Controllers
                             deviceBoundExt.osver = deviceRow.os_ver;
                             deviceBoundArray.Add(deviceBoundExt);
                             //DB更新用
-                            deviceBound serialDeviceBoundRow = serialRow.deviceBound.Where(d => (d.uuid == deviceBoundRow.uuid1 && d.uuid2 == deviceBoundRow.uuid2) || (d.uuid == deviceBoundRow.uuid2 && d.uuid2 == deviceBoundRow.uuid1)).FirstOrDefault();
+                            serialDeviceBoundRow = serialRow.deviceBound.Where(d => (d.uuid == deviceBoundRow.uuid1 && d.uuid2 == deviceBoundRow.uuid2) || (d.uuid == deviceBoundRow.uuid2 && d.uuid2 == deviceBoundRow.uuid1)).FirstOrDefault();
                             if (serialDeviceBoundRow != null)
                             {
                                 serialDeviceBoundRow.deviceId = deviceRow.device_id;
@@ -427,7 +435,7 @@ namespace TEAMModelOS.Controllers
                             {
                                 if (!string.IsNullOrEmpty(serialRowDeviceBound.uuid) || !string.IsNullOrEmpty(serialRowDeviceBound.uuid2))
                                 {
-                                    deviceBoundExt deviceBoundExt = new deviceBoundExt();
+                                    deviceBoundExt = new deviceBoundExt();
                                     deviceBoundExt.uuid = serialRowDeviceBound.uuid;
                                     deviceBoundExt.uuid2 = serialRowDeviceBound.uuid2;
                                     deviceBoundExt.classId = serialRowDeviceBound.classId;
@@ -438,9 +446,11 @@ namespace TEAMModelOS.Controllers
                         }
                     }
                     //序號更新
-                    await db.ReplaceItemAsync<SchoolProductSerial>(serialRow, serialRow.id, new PartitionKey($"Product-{school_code}"));
+                    updSchoolProductSerialList.Add(serialRow);
+                    //await db.ReplaceItemAsync<SchoolProductSerial>(serialRow, serialRow.id, new PartitionKey($"Product-{school_code}"));
+                    
                     //回傳值
-                    SerialInfoBaseWithdeviceBoundExt serialResultRow = new SerialInfoBaseWithdeviceBoundExt();
+                    serialResultRow = new SerialInfoBaseWithdeviceBoundExt();
                     serialResultRow.code = serialRow.code;
                     serialResultRow.id = serialRow.id;
                     serialResultRow.dataType = serialRow.dataType;
@@ -467,6 +477,7 @@ namespace TEAMModelOS.Controllers
 
                 //服務
                 List<SchoolProductSumDataService> service = new List<SchoolProductSumDataService>();
+                SchoolProductSum schoolProductSum;
                 await foreach (var items in db.GetItemQueryStreamIterator(queryText: $"SELECT * FROM c WHERE c.id = '{school_code}'", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"ProductSum") }))
                 {
                     using var json = await JsonDocument.ParseAsync(items.ContentStream);
@@ -474,16 +485,17 @@ namespace TEAMModelOS.Controllers
                     {
                         foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
                         {
-                            SchoolProductSum schoolProductSum = obj.ToObject<SchoolProductSum>();
+                            schoolProductSum = obj.ToObject<SchoolProductSum>();
                             service = schoolProductSum.service;
                         }
                     }
                 }
                 if (service.Count > 0)
                 {
+                    SchoolProductOrder spOrderRow;
                     foreach (SchoolProductSumDataService serviceRow in service)
                     {
-                        SchoolProductOrder spOrderRow = new SchoolProductOrder();
+                        spOrderRow = new SchoolProductOrder();
                         spOrderRow.prodCode = serviceRow.prodCode;
                         spOrderRow.avaliable = serviceRow.avaliable;
                         spOrderRow.avaliableStartDate = serviceRow.startDate;
@@ -616,6 +628,9 @@ namespace TEAMModelOS.Controllers
                 //取得CC授權使用狀態
                 var hashs = await r8.HashGetAllAsync($"CC:License:{school_code.GetString()}");
                 var ccuser = hashs.Select(x => JsonDocument.Parse(x.Value.ToString())).ToList();
+
+                //更新學校產品序號
+                _ = UpdupdSchoolProductSerialListAsync(updSchoolProductSerialList, school_code.GetString());
                 return Ok(new { serial = serialResult, service = serviceOrder, hard, space, order , ccuser });
             }
             catch (Exception ex)
@@ -623,6 +638,15 @@ namespace TEAMModelOS.Controllers
                 return BadRequest();
             }
         }
+        //集體變更學校序號產品 (效能增進對策)
+        private async Task UpdupdSchoolProductSerialListAsync(List<SchoolProductSerial> serialList, string schoolCode)
+        {
+            var db = _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "School");
+            foreach (SchoolProductSerial serialRow in serialList)
+            {
+                await db.ReplaceItemAsync<SchoolProductSerial>(serialRow, serialRow.id, new PartitionKey($"Product-{schoolCode}"));
+            }
+        }
 
         /// <summary>
         /// 取得CoreDevice資訊