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

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

CrazyIter_Bin преди 3 години
родител
ревизия
a93a71559c

+ 48 - 37
TEAMModelBI/Controllers/BIHome/OnLineController.cs

@@ -103,19 +103,24 @@ namespace TEAMModelBI.Controllers.BIHome
         /// </summary>
         /// <returns></returns>
         [HttpPost("get-trend")]
-        public async Task<IActionResult> GetTrend() 
+        public async Task<IActionResult> GetTrend(JsonElement jsonElement) 
         {
+            jsonElement.TryGetProperty("hour", out JsonElement hour);
             var table = _azureStorage.GetCloudTableClient().GetTableReference("IESLogin");
-            DateTimeOffset dateTime = DateTimeOffset.UtcNow;
+            DateTimeOffset dateTime =  DateTimeOffset.UtcNow;
+            if (!string.IsNullOrEmpty($"{hour}")) 
+            {
+                DateTimeOffset.UtcNow.AddHours(hour.GetInt32());
+            }
             var (daySt, dayEt) = TimeHelper.GetStartOrEnd(dateTime);  //今天开始时间    13位
             var (strDaySt, strDayEt) = TimeHelper.GetUnixToDate(daySt, dayEt, "yyyyMMddHH");
             var dateDay = dateTime.ToString("yyyyMMdd"); //获取当天的日期
 
-            daySt.ToString("yyyyMMddHH");
-            Dictionary<int, int> allDays = new();  //所有在线人数
-            Dictionary<int, int> tchDays = new();  //教师在线人数
-            Dictionary<int, int> stuDays = new();  //学生在线人数
-            Dictionary<int, int> tmdDays = new();  //醍摩豆账户学生
+            //daySt.ToString("yyyyMMddHH");
+            Dictionary<long, int> allDays = new();  //所有在线人数
+            Dictionary<long, int> tchDays = new();  //教师在线人数
+            Dictionary<long, int> stuDays = new();  //学生在线人数
+            Dictionary<long, int> tmdDays = new();  //醍摩豆账户学生
 
             SortedSetEntry[] tchDay = _azureRedis.GetRedisClient(8).SortedSetRangeByScoreWithScores($"Login:IES:teacher:{dateDay}");
             if (tchDay.Length > 0)
@@ -124,12 +129,13 @@ namespace TEAMModelBI.Controllers.BIHome
                 {
                     int val = ((int)item.Score);
                     int key = ((int)item.Element);
-                    var hour = int.Parse(DateTime.SpecifyKind(Convert.ToDateTime($"{dateTime.Year}/{dateTime.Month}/{ dateTime.Day} {key}:00:00"), DateTimeKind.Utc).ToLocalTime().ToString("HH"));                
-                    tchDays.Add(hour, val);
-                    if (allDays.ContainsKey(hour))
-                        allDays[hour] = (allDays[hour] + val);
+                    var utcTo = new DateTimeOffset(new DateTime(dateTime.Year, dateTime.Month, dateTime.Day, hour.GetInt32() + key, 0, 0)).Hour;
+                    //var hour = int.Parse(DateTime.SpecifyKind(Convert.ToDateTime($"{dateTime.Year}/{dateTime.Month}/{ dateTime.Day} {key}:00:00"), DateTimeKind.Utc).ToLocalTime().ToString("HH"));
+                    tchDays.Add(utcTo, val);
+                    if (allDays.ContainsKey(utcTo))
+                        allDays[utcTo] = (allDays[utcTo] + val);
                     else
-                        allDays.Add(hour, val);
+                        allDays.Add(utcTo, val);
                 }
             }
             else
@@ -142,12 +148,13 @@ namespace TEAMModelBI.Controllers.BIHome
                     foreach (var item in hourLoginsTch)
                     {
                         await _azureRedis.GetRedisClient(8).SortedSetIncrementAsync($"Login:IES:teacher:{dateDay}", $"{item.Hour}", item.Teacher);//存一天24小时
-                        var hour = int.Parse(DateTime.SpecifyKind(Convert.ToDateTime($"{dateTime.Year}/{dateTime.Month}/{ dateTime.Day} {item.Hour}:00:00"), DateTimeKind.Utc).ToLocalTime().ToString("HH"));
-                        tchDays.Add(hour, item.Teacher);
-                        if (allDays.ContainsKey(hour))
-                            allDays[hour] = (allDays[hour] + item.Teacher);
+                        var utcTo = new DateTimeOffset(new DateTime(dateTime.Year, dateTime.Month, dateTime.Day, hour.GetInt32() + item.Hour, 0, 0)).Hour;
+                        //var hour = int.Parse(DateTime.SpecifyKind(Convert.ToDateTime($"{dateTime.Year}/{dateTime.Month}/{ dateTime.Day} {item.Hour}:00:00"), DateTimeKind.Utc).ToLocalTime().ToString("HH"));
+                        tchDays.Add(utcTo, item.Teacher);
+                        if (allDays.ContainsKey(utcTo))
+                            allDays[utcTo] = (allDays[utcTo] + item.Teacher);
                         else
-                            allDays.Add(hour, item.Teacher);
+                            allDays.Add(utcTo, item.Teacher);
                     }
                 }
             }
@@ -159,12 +166,13 @@ namespace TEAMModelBI.Controllers.BIHome
                 {
                     int val = (int)item.Score;
                     int key = (int)item.Element;
-                    var hour = int.Parse(DateTime.SpecifyKind(Convert.ToDateTime($"{dateTime.Year}/{dateTime.Month}/{ dateTime.Day} {key}:00:00"), DateTimeKind.Utc).ToLocalTime().ToString("HH"));
-                    stuDays.Add(hour, val);
-                    if (allDays.ContainsKey(hour))
-                        allDays[hour] = (allDays[hour] + val);
+                    var utcTo = new DateTimeOffset(new DateTime(dateTime.Year, dateTime.Month, dateTime.Day, hour.GetInt32() + key, 0, 0)).Hour;
+                    //var hour = int.Parse(DateTime.SpecifyKind(Convert.ToDateTime($"{dateTime.Year}/{dateTime.Month}/{ dateTime.Day} {key}:00:00"), DateTimeKind.Utc).ToLocalTime().ToString("HH"));
+                    stuDays.Add(utcTo, val);
+                    if (allDays.ContainsKey(utcTo))
+                        allDays[utcTo] = (allDays[utcTo] + val);
                     else
-                        allDays.Add(hour, val);
+                        allDays.Add(utcTo, val);
                 }
             }
             else
@@ -178,12 +186,13 @@ namespace TEAMModelBI.Controllers.BIHome
                     foreach (var item in hourLoginsStu)
                     {
                         await _azureRedis.GetRedisClient(8).SortedSetIncrementAsync($"Login:IES:student:{dateDay}", $"{item.Hour}", item.Student);//存一天24小时
-                        var hour = int.Parse(DateTime.SpecifyKind(Convert.ToDateTime($"{dateTime.Year}/{dateTime.Month}/{ dateTime.Day} {item.Hour}:00:00"), DateTimeKind.Utc).ToLocalTime().ToString("HH"));
-                        stuDays.Add(hour, item.Student);
-                        if (allDays.ContainsKey(hour))
-                            allDays[hour] = (allDays[hour] + item.Student);
+                        var utcTo = new DateTimeOffset(new DateTime(dateTime.Year, dateTime.Month, dateTime.Day, hour.GetInt32() + item.Hour, 0, 0)).Hour;
+                        //var hour = int.Parse(DateTime.SpecifyKind(Convert.ToDateTime($"{dateTime.Year}/{dateTime.Month}/{ dateTime.Day} {item.Hour}:00:00"), DateTimeKind.Utc).ToLocalTime().ToString("HH"));
+                        stuDays.Add(utcTo, item.Student);
+                        if (allDays.ContainsKey(utcTo))
+                            allDays[utcTo] = (allDays[utcTo] + item.Student);
                         else
-                            allDays.Add(hour, item.Student);
+                            allDays.Add(utcTo, item.Student);
                     }
                 }
             }
@@ -195,12 +204,13 @@ namespace TEAMModelBI.Controllers.BIHome
                 {
                     int val = (int)item.Score;
                     int key = (int)item.Element;
-                    var hour = int.Parse(DateTime.SpecifyKind(Convert.ToDateTime($"{dateTime.Year}/{dateTime.Month}/{ dateTime.Day} {key}:00:00"), DateTimeKind.Utc).ToLocalTime().ToString("HH"));
-                    tmdDays.Add(hour, val);
-                    if (allDays.ContainsKey(hour))
-                        allDays[hour] = (allDays[hour] + val);
+                    var utcTo = new DateTimeOffset(new DateTime(dateTime.Year, dateTime.Month, dateTime.Day, hour.GetInt32() + key, 00, 00)).Hour;
+                    //var hour = int.Parse(DateTime.SpecifyKind(Convert.ToDateTime($"{dateTime.Year}/{dateTime.Month}/{ dateTime.Day} {key}:00:00"), DateTimeKind.Utc).ToLocalTime().ToString("HH"));
+                    tmdDays.Add(utcTo, val);
+                    if (allDays.ContainsKey(utcTo))
+                        allDays[utcTo] = (allDays[utcTo] + val);
                     else
-                        allDays.Add(hour, val);
+                        allDays.Add(utcTo, val);
                 }
             }
             else
@@ -214,12 +224,13 @@ namespace TEAMModelBI.Controllers.BIHome
                     foreach (var item in hourLoginsTmd)
                     {
                         await _azureRedis.GetRedisClient(8).SortedSetIncrementAsync($"Login:IES:tmduser:{dateDay}", $"{item.Hour}", item.TmdUser);//存一天24小时
-                        var hour = int.Parse(DateTime.SpecifyKind(Convert.ToDateTime($"{dateTime.Year}/{dateTime.Month}/{ dateTime.Day} {item.Hour}:00:00"), DateTimeKind.Utc).ToLocalTime().ToString("HH"));
-                        tmdDays.Add(hour, item.TmdUser);
-                        if (allDays.ContainsKey(hour))
-                            allDays[hour] = (allDays[hour] + item.TmdUser);
+                        var utcTo = new DateTimeOffset(new DateTime(dateTime.Year, dateTime.Month, dateTime.Day, hour.GetInt32() + item.Hour, 00, 00)).Hour;
+                        //var hour = int.Parse(DateTime.SpecifyKind(Convert.ToDateTime($"{dateTime.Year}/{dateTime.Month}/{ dateTime.Day} {item.Hour}:00:00"), DateTimeKind.Utc).ToLocalTime().ToString("HH"));
+                        tmdDays.Add(utcTo, item.TmdUser);
+                        if (allDays.ContainsKey(utcTo))
+                            allDays[utcTo] = (allDays[utcTo] + item.TmdUser);
                         else
-                            allDays.Add(hour, item.TmdUser);
+                            allDays.Add(utcTo, item.TmdUser);
                     }
                 }
             }

+ 32 - 6
TEAMModelBI/Controllers/BISchool/AreaRelevantController.cs

@@ -47,10 +47,17 @@ namespace TEAMModelBI.Controllers.BISchool
         {
             try
             {
-                if (!jsonElement.TryGetProperty("areaId", out JsonElement _areaId)) return BadRequest();
+                jsonElement.TryGetProperty("areaId", out JsonElement _areaId);
+                jsonElement.TryGetProperty("isManyArea", out JsonElement isManyArea);
                 var cosmosClient = _azureCosmos.GetCosmosClient();
                 List<JoinAreaSchool> joinAreaSchools = new List<JoinAreaSchool>();
                 string slqtxt = $"SELECT c.id,c.name,c.schoolCode,c.province,c.city,c.dist,c.picture,c.period FROM c WHERE c.areaId='{_areaId}'";
+
+                if (!string.IsNullOrEmpty($"{isManyArea}"))
+                {
+                    slqtxt = $"SELECT c.id,c.name,c.schoolCode,c.province,c.city,c.dist,c.picture,c.period FROM c join m in c.manyAreas where m.areaId='{_areaId}' or c.areaId='{_areaId}'";
+                }
+
                 await foreach (var item in cosmosClient.GetContainer(Constant.TEAMModelOS, "School").GetItemQueryStreamIterator(queryText: slqtxt,requestOptions:new QueryRequestOptions() { PartitionKey = new PartitionKey("Base")})) 
                 {
                     using var json = await JsonDocument.ParseAsync(item.ContentStream);
@@ -69,6 +76,12 @@ namespace TEAMModelBI.Controllers.BISchool
                                 picture = obj.GetProperty("picture").GetString(),
                                 period = obj.GetProperty("period").ToObject<List<Period>>().Select(x => x.name).ToList()
                             };
+
+                            try
+                            {
+                                joinAreaSchool.manyAreas = obj.GetProperty("manyAreas").ToObject<List<ManyArea>>();
+                            }
+                            catch { }
                             joinAreaSchools.Add(joinAreaSchool);
                         }                    
                     }
@@ -95,16 +108,29 @@ namespace TEAMModelBI.Controllers.BISchool
         {
             try
             {
-                //if (!jsonElement.TryGetProperty("areaId", out JsonElement areaId)) return BadRequest();
                 if (!jsonElement.TryGetProperty("schoolId", out JsonElement schoolId)) return BadRequest();
+                jsonElement.TryGetProperty("areaId", out JsonElement areaId);
+                jsonElement.TryGetProperty("standard", out JsonElement standard);
+                jsonElement.TryGetProperty("isDefault", out JsonElement isDefault);
 
                 var (_tmdId, _tmdName, pic, did, dname, dpic) = HttpJwtAnalysis.JwtXAuthBI(HttpContext.GetXAuth("AuthToken"), _option);
 
                 var cosmosClient = _azureCosmos.GetCosmosClient();
                 School tempSchool = await cosmosClient.GetContainer(Constant.TEAMModelOS, "School").ReadItemAsync<School>($"{schoolId}", new PartitionKey("Base"));
-
-                tempSchool.areaId = null;
-                tempSchool.standard = null;
+                if (string.IsNullOrEmpty($"{isDefault}"))
+                {
+                    tempSchool.areaId = null;
+                    tempSchool.standard = null;
+                    var temp = tempSchool.manyAreas.Find(ma => ma.areaId == $"{areaId}");
+                    if (temp != null)
+                        tempSchool.manyAreas.Remove(temp);
+                }
+                else
+                {
+                    var temp = tempSchool.manyAreas.Find(ma => ma.areaId == $"{areaId}");
+                    if (temp == null)
+                        tempSchool.manyAreas.Add(new ManyArea { areaId = $"{areaId}", standard = $"{standard}" });
+                }
 
                 School school = await cosmosClient.GetContainer(Constant.TEAMModelOS, "School").ReplaceItemAsync<School>(tempSchool, tempSchool.id, new PartitionKey("Base"));
 
@@ -141,7 +167,7 @@ namespace TEAMModelBI.Controllers.BISchool
 
             public string dist { get; set; }
 
-
+            public List<ManyArea> manyAreas { get; set; } = new List<ManyArea>();
         }
 
     }

+ 30 - 16
TEAMModelBI/Controllers/BISchool/SchoolController.cs

@@ -57,12 +57,16 @@ namespace TEAMModelBI.Controllers.BISchool
         /// <returns></returns>
         [ProducesDefaultResponseType]
         [HttpPost("get-notarea")]
-        public async Task<IActionResult> GetNotAreaSchool()
+        public async Task<IActionResult> GetNotAreaSchool(JsonElement jsonElement)
         {
             try
             {
+                jsonElement.TryGetProperty("areaId", out JsonElement areaId);
                 var cosmosClient = _azureCosmos.GetCosmosClient();
-                string sqltxt = $"SELECT c.id,c.name,c.schoolCode,c.province,c.city,c.dist,c.picture,c.period FROM c WHERE c.standard=null";
+                string sqltxt = "SELECT c.id,c.name,c.schoolCode,c.province,c.city,c.dist,c.picture,c.period,c.manyAreas FROM c WHERE c.standard=null or c.areaId=null";
+                if (!string.IsNullOrEmpty($"{areaId}"))
+                    sqltxt = $"SELECT c.id,c.name,c.schoolCode,c.province,c.city,c.dist,c.picture,c.period,c.manyAreas FROM c join m in c.manyAreas WHERE c.areaId!='{areaId}' or m.areaId!='{areaId}'";
+
                 List<NotAreaSchool> notAreaSchools = new List<NotAreaSchool>();
                 await foreach (var item in cosmosClient.GetContainer(Constant.TEAMModelOS, "School").GetItemQueryStreamIterator(queryText: sqltxt, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("Base") }))
                 {
@@ -81,12 +85,19 @@ namespace TEAMModelBI.Controllers.BISchool
                                 period = obj.GetProperty("period").ToObject<List<Period>>().Select(x => x.name).ToList(),
                                 province = obj.GetProperty("province").GetString(),
                                 city = obj.GetProperty("city").GetString(),
-                                dist = obj.GetProperty("dist").GetString(),
+                                dist = obj.GetProperty("dist").GetString()
                             };
+                            try
+                            {
+                                notAreaSchool.manyAreas = obj.GetProperty("manyAreas").ToObject<List<ManyArea>>();
+                            }
+                            catch { }
                             notAreaSchools.Add(notAreaSchool);
                         }
                     }
                 }
+                //if (!string.IsNullOrEmpty($"{areaId}"))                    
+                //    notAreaSchools = notAreaSchools.Select(na => new NotAreaSchool { id = na.id, name = na.name, schoolCode = na.schoolCode, picture = na.picture, period = na.period, province = na.province, city = na.city = na.city, dist = na.dist,  manyAreas =  new List<ManyArea> { na.manyAreas.Find(m => m.areaId != $"{areaId}") } }).ToList();
 
                 return Ok(new { state = 200, notAreaSchools });
             }
@@ -112,6 +123,7 @@ namespace TEAMModelBI.Controllers.BISchool
                 if (!jsonElement.TryGetProperty("standard", out JsonElement standard)) return BadRequest();
                 if (!jsonElement.TryGetProperty("schoolCode", out JsonElement _schoolCode)) return BadRequest();
                 if (!jsonElement.TryGetProperty("areaId", out JsonElement _areaId)) return BadRequest();
+                jsonElement.TryGetProperty("isDefault", out JsonElement isDefault);
 
                 var (_tmdId, _tmdName, pic, did, dname, dpic) = HttpJwtAnalysis.JwtXAuthBI(HttpContext.GetXAuth("AuthToken"), _option);
 
@@ -126,8 +138,20 @@ namespace TEAMModelBI.Controllers.BISchool
                         School school = await cosmosCliet.GetContainer(Constant.TEAMModelOS, "School").ReadItemAsync<School>(tempCode, new PartitionKey("Base"));
                         if (school != null)
                         {
-                            school.standard = $"{standard}";
-                            school.areaId = $"{_areaId}";
+                            if (string.IsNullOrEmpty($"{isDefault}"))
+                            {
+                                school.standard = $"{standard}";
+                                school.areaId = $"{_areaId}";
+                                var marea = school.manyAreas.Find(ma => ma.areaId == $"{_areaId}" && ma.standard == $"{standard}");
+                                if (marea == null)
+                                    school.manyAreas.Add(new ManyArea { areaId = $"{_areaId}", standard = $"{standard}" });
+                            }
+                            else 
+                            {
+                                var marea = school.manyAreas.Find(ma => ma.areaId == $"{_areaId}" && ma.standard == $"{standard}");
+                                if (marea == null)
+                                    school.manyAreas.Add(new ManyArea { areaId = $"{_areaId}", standard = $"{standard}" });
+                            }
                             await cosmosCliet.GetContainer(Constant.TEAMModelOS, "School").ReplaceItemAsync<School>(school, school.id, new PartitionKey(school.code));
                         }
                     }
@@ -877,20 +901,14 @@ namespace TEAMModelBI.Controllers.BISchool
         public record NotAreaSchool
         {
             public string id { get; set; }
-
             public string name { get; set; }
-
             public string schoolCode { get; set; }
-
             public string picture { get; set; }
-
             public List<string> period { get; set; }
-
             public string province { get; set; }
-
             public string city { get; set; }
-
             public string dist { get; set; }
+            public List<ManyArea> manyAreas { get; set; } = new List<ManyArea>();
         }
 
         /// <summary>
@@ -899,9 +917,7 @@ namespace TEAMModelBI.Controllers.BISchool
         public record SchoolSpace
         {
             public string id { get; set; }
-
             public string name { get; set; }
-
             public Space space { get; set; }
 
         }
@@ -914,12 +930,10 @@ namespace TEAMModelBI.Controllers.BISchool
             /// 已使用空间
             /// </summary>
             public long useSize { get; set; }
-
             /// <summary>
             /// 分配教师空间
             /// </summary>
             public long tSize { get; set; }
-
             /// <summary>
             /// 空间类型
             /// </summary>

+ 12 - 0
TEAMModelOS.SDK/Models/Cosmos/School/School.cs

@@ -85,6 +85,10 @@ namespace TEAMModelOS.SDK.Models
         /// 创建时间  十位 时间戳
         /// </summary>
         public long createTime { get; set; }
+        /// <summary>
+        /// 存多个区域
+        /// </summary>
+        public List<ManyArea> manyAreas { get; set; }
     }
     /// <summary>
     /// 课表计划
@@ -118,4 +122,12 @@ namespace TEAMModelOS.SDK.Models
         public string value { get; set; } = null;
 
     }
+    /// <summary>
+    /// 存区域
+    /// </summary>
+    public class ManyArea 
+    {
+        public string areaId { get; set; }
+        public string standard { get; set; }
+    }
 }

+ 1 - 0
TEAMModelOS/ClientApp/public/lang/en-US.js

@@ -5465,6 +5465,7 @@ const LANG_EN_US = {
         impTd3: 'Email',
         impTd4: 'User ID',
         impTd5: 'Note',
+        expTd:'狀態',
         joinTips1: 'Note: If a teacher is imported into the school by name only, the teacher can be bound/modified to join the school after comparing with the current teacher account in the school.',
         joinTips2: 'Binding teachers who have not yet joined school:',
         hasLeader: 'This teacher is already a section chief',

+ 1 - 0
TEAMModelOS/ClientApp/public/lang/zh-CN.js

@@ -5465,6 +5465,7 @@ const LANG_ZH_CN = {
         impTd3: '邮箱',
         impTd4: '醍摩豆ID',
         impTd5: '备注',
+        expTd:'状态',
         joinTips1: '温馨提示:学校存在仅导入名字的老师,如果对应当前老师账号,可以进行绑定修改教师加入学校的状态。如果当前老师账号不为学校导入对应的老师可以不用进行绑定。',
         joinTips2: '绑定学校尚未加入的老师:',
         hasLeader: '当前老师已经是组长了',

+ 1 - 0
TEAMModelOS/ClientApp/public/lang/zh-TW.js

@@ -5465,6 +5465,7 @@ const LANG_ZH_TW = {
         impTd3: '電子信箱',
         impTd4: '用戶編號',
         impTd5: '備註',
+        expTd:'狀態',
         joinTips1: '溫馨提示:學校存在僅匯入名字的老師,如果對應當前老師帳號,可以進行綁定修改教師加入學校的狀態。如果當前老師帳號不為學校匯入對應的老師可以不用進行綁定。',
         joinTips2: '綁定學校尚未加入的老師:',
         hasLeader: '當前老師已經是組長了',

+ 2 - 2
TEAMModelOS/ClientApp/src/common/BaseLayout.vue

@@ -132,7 +132,7 @@ export default {
         return {
             isLoading: false,
             systemLevel: this.$t('system.basic'),
-            isLock: false,
+            isLock: true,
             isShowAreaSelect: true,
             openNames: [],
             activeName: '',
@@ -144,7 +144,7 @@ export default {
                     opacity: 0
                 }
             },
-            isCollapsed: true,
+            isCollapsed: false,
             menuTree: [],
             isShowLogo: true,
             hasAnalysisAuth: false

+ 266 - 221
TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContentTypeTemplate/composePaper.vue

@@ -1,254 +1,299 @@
 <template>
-    <div class="content">
-        <div class="editor-wrap" v-show="!isConnector">
-            <div id="textArea"></div>
-        </div>
-        <div class="connector-wrap" v-if="isConnector">
-            <div v-if="answerImg">
-                <!-- <img :src="answerImg" alt=""> -->
-                <span v-html="answerImg"></span>
-            </div>
-            <p v-if="answerImg" style="font-weight: bold; margin-bottom: 20px">
-                {{ $t("studentWeb.exam.testpop.reAns") }}
-            </p>
-            <BaseCanvas :vm="vm" :bgImg="markBg" :isStudent="markStatus" @onCloseModal="closeModal" @onSaveCanvas="saveMark"></BaseCanvas>
-        </div>
-        <Modal v-model="markStatus" fullscreen :title="$t('studentWeb.exam.answer')" footer-hide ref="compose">
-            <BaseCanvas v-if="markStatus" :vm="vm" :bgImg="markBg" :isStudent="markStatus" @onCloseModal="closeModal" @onSaveCanvas="saveMark"></BaseCanvas>
-        </Modal>
-        <iframe class="frame" id="answerIframe" :srcdoc="itemInfo.question"></iframe>
+  <div class="content">
+    <div class="editor-wrap" v-show="!isConnector">
+      <div id="textArea"></div>
     </div>
+    <div class="connector-wrap" v-if="isConnector">
+      <div v-if="answerImg">
+        <!-- <img :src="answerImg" alt=""> -->
+        <span v-html="answerImg"></span>
+      </div>
+      <p v-if="answerImg" style="font-weight: bold; margin-bottom: 20px">
+        {{ $t("studentWeb.exam.testpop.reAns") }}
+      </p>
+      <BaseCanvas :vm="vm" :bgImg="markBg" :isStudent="markStatus" @onCloseModal="closeModal" @onSaveCanvas="saveMark"></BaseCanvas>
+    </div>
+    <Modal v-model="markStatus" fullscreen :title="$t('studentWeb.exam.answer')" footer-hide ref="compose">
+      <BaseCanvas v-if="markStatus" :vm="vm" :bgImg="markBg" :isStudent="markStatus" @onCloseModal="closeModal" @onSaveCanvas="saveMark"></BaseCanvas>
+    </Modal>
+    <iframe class="frame" id="answerIframe" :srcdoc="itemInfo.question"></iframe>
+  </div>
 </template>
 <script>
+import BlobTool from "@/utils/blobTool.js"
+import { mapGetters } from 'vuex';
 import html2canvas from "html2canvas"
 import E from "wangeditor"
 import { editor_tw_config } from "@/utils/editorLangTw.js"
 import { editor_en_config } from "@/utils/editorLangEn.js"
 import i18next from "i18next"
 export default {
-    components: {},
-    props: {
-        index: {
-            type: Number,
-            default: -1,
-        },
-        textData: {
-            type: Array,
-            default: () => {
-                return []
-            },
-        },
-        itemInfo: {
-            type: Object,
-            default: () => {
-                return {}
-            },
-        },
-        close: {
-            type: Boolean,
-            default: false,
-        },
+  components: {},
+  props: {
+    index: {
+      type: Number,
+      default: -1,
+    },
+    textData: {
+      type: Array,
+      default: () => {
+        return []
+      },
+    },
+    itemInfo: {
+      type: Object,
+      default: () => {
+        return {}
+      },
     },
-    data(vm) {
-        return {
-            answerImg: null,
-            vm: vm,
-            tabName: "exercise",
-            editorContent: "",
-            examInfo: [],
-            editor: null,
-            markStatus: false,
-            markBg: "",
+    close: {
+      type: Boolean,
+      default: false,
+    },
+  },
+  data(vm) {
+    return {
+      answerImg: null,
+      vm: vm,
+      tabName: "exercise",
+      editorContent: "",
+      examInfo: [],
+      editor: null,
+      markStatus: false,
+      markBg: "",
+    }
+  },
+  methods: {
+    async getInfo() {
+      // console.error(this.markBg)
+      this.markBg = ""
+      this.examInfo = []
+      this.initEditor()
+      // console.error(this.textData)
+      this.answerImg = null
+      if (this.textData.length > 0) {
+        this.examInfo = [...this.textData]
+        if (this.isConnector) {
+          this.answerImg = this.textData[0]
+        } else {
+          this.answerImg = null
+          this.editor.txt.html(this.examInfo[0])
+        }
+      }
+      setTimeout(() => {
+        if (this.isConnector) {
+          this.markStuAnswer()
         }
+      }, 100)
     },
-    methods: {
-        async getInfo() {
-            // console.error(this.markBg)
-            this.markBg = ""
-            this.examInfo = []
-            this.initEditor()
-            // console.error(this.textData)
-            this.answerImg = null
-            if (this.textData.length > 0) {
-                this.examInfo = [...this.textData]
-                if (this.isConnector) {
-                    this.answerImg = this.textData[0]
-                } else {
-                    this.answerImg = null
-                    this.editor.txt.html(this.examInfo[0])
-                }
-            }
-            setTimeout(() => {
-                if (this.isConnector) {
-                    this.markStuAnswer()
-                }
-            }, 100)
-        },
-        closeModal() {
-            this.markStatus = false
-            this.markBg = ""
-        },
-        saveMark(data) {
-            if (data) {
-                data.height = 400
-                data.width = 400
-                let params = `<img src="${data.base64}" />`
-                let img = document.createElement("img")
-                img.src = data.base64
-                if (this.isConnector) {
-                    this.answerImg = params
-                    this.markBg = ""
-                    setTimeout(() => {
-                        this.markStuAnswer()
-                    }, 100)
-                    this.$emit("dataGet", params, this.index)
-                    this.$Message.success(this.$t("settings.submitSucTips"))
-                } else {
-                    this.editor.txt.html(img.outerHTML)
-                }
-                this.markStatus = false
-            }
-        },
-        initEditor() {
-            this.editorContent = ""
-            this.editor = new E("#textArea")
-            this.editor.config.onchange = (html) => {
-                this.editorContent = html
-            }
-            this.editor.config.showFullScreen = false
-            this.editor.config.menus = [
-                "link", // 插入链接
-                "justify", // 对齐方式
-                "image", // 插入图片
-            ]
-            this.editor.config.zIndex = 1
-            this.editor.config.placeholder = this.$t(
-                "studentWeb.exam.inputAnswers"
-            )
-            this.editor.config.height = 300
-            this.editor.config.showLinkImg = false
-            this.editor.config.uploadImgShowBase64 = true // 使用 base64 保存图片不建议使用这种,我只是图个方便
-            this.$editorTools.addCanvas(this, this.editor)
-            this.editor.config.menus.push("connector")
-            this.$editorTools.addStuBgBtn(this, this.editor)
-            // if (this.itemInfo.type == 'correct' || this.itemInfo.type == 'connector') {
-            //     this.editor.config.menus.push('connector')
-            //     this.$editorTools.addStuBgBtn(this, this.editor)
-            // } else {
-            //     this.$editorTools.addCanvas(this, this.editor)
-            // }
-            let curLang = localStorage.getItem("local") || "zh-cn"
-            if (curLang === "zh-tw") {
-                // 自定义语言
-                this.editor.config.languages["tw"] = editor_tw_config
-                // 选择语言
-                this.editor.config.lang = "tw"
-                // 引入 i18next 插件
-                this.editor.i18next = i18next
-            }
-            if (curLang === "en-us") {
-                // 自定义语言
-                this.editor.config.languages["en"] = editor_en_config
-                // 选择语言
-                this.editor.config.lang = "en"
-                // 引入 i18next 插件
-                this.editor.i18next = i18next
-            }
-            this.editor.create()
-            if (this.close) {
-                this.editor.disable()
-            }
-            this.editor.txt.clear()
-            if (this.examInfo.length > 0) {
-                this.editor.txt.html(this.examInfo[0])
-            }
-        },
-        markStuAnswer() {
-            let answerIframe = ""
-            answerIframe = document.getElementById("answerIframe")
-            answerIframe.contentWindow.document.body.style.width = "fit-content"
-            answerIframe.contentWindow.document.body.style.minWidth = "600px"
-            answerIframe.contentWindow.document.body.style.backgroundColor =
-                "#f5f5f5"
-            console.log(
-                document.getElementById("answerIframe").contentWindow.document
-            )
-            let iframe = document
-                .getElementById("answerIframe")
-                .contentWindow.document.getElementsByTagName("p")
-
-            if (iframe.length > 0) {
-                for (let i = 0; i < iframe.length - 1; i++) {
-                    iframe[i].style.lineHeight = "50px"
-                    iframe[i].style.paddingBottom = "30px"
-                }
-                iframe[iframe.length - 1].style.paddingBottom = "100px"
-                iframe[iframe.length - 1].style.lineHeight = "50px"
-            }
-            html2canvas(answerIframe.contentWindow.document.body).then(
-                (canvas) => {
-                    if (!this.isConnector) {
-                        this.markStatus = true
-                    }
-                    this.markBg = canvas.toDataURL("image/png")
-                }
-            )
-        },
+    closeModal() {
+      this.markStatus = false
+      this.markBg = ""
     },
-    mounted() {
-        this.initEditor()
-        this.getInfo()
-        // 监听富文本画板功能
-        this.$EventBus.$off("onStuCanvas")
-        this.$EventBus.$on("onStuCanvas", (editor) => {
-            this.markBg = ""
+    saveMark(data) {
+      if (data) {
+        data.height = 400
+        data.width = 400
+        let params = `<img src="${data.base64}" />`
+        let img = document.createElement("img")
+        img.src = data.base64
+        if (this.isConnector) {
+          this.answerImg = params
+          this.markBg = ""
+          setTimeout(() => {
             this.markStuAnswer()
+          }, 100)
+          this.$emit("dataGet", params, this.index)
+          this.$Message.success(this.$t("settings.submitSucTips"))
+        } else {
+          this.editor.txt.html(img.outerHTML)
+        }
+        this.markStatus = false
+      }
+    },
+    initEditor() {
+      this.editorContent = ""
+      this.editor = new E("#textArea")
+      this.editor.config.onchange = (html) => {
+        this.editorContent = html
+      }
+      this.editor.config.showFullScreen = false
+      this.editor.config.menus = [
+        "link", // 插入链接
+        "justify", // 对齐方式
+        "image", // 插入图片
+        "video"
+      ]
+      this.editor.config.zIndex = 1
+      this.editor.config.placeholder = this.$t(
+        "studentWeb.exam.inputAnswers"
+      )
+      this.editor.config.height = 300
+      this.editor.config.showLinkImg = false
+      this.editor.config.uploadImgShowBase64 = true // 使用 base64 保存图片不建议使用这种,我只是图个方便
+      this.$editorTools.addAudio(this, this.editor, 'simple')
+      this.$editorTools.addVideoUpload(this, this.editor, 'simple')
+      this.$editorTools.addCanvas(this, this.editor)
+      this.$editorTools.addStuBgBtn(this, this.editor)
+      // if (this.itemInfo.type == 'correct' || this.itemInfo.type == 'connector') {
+      //     this.editor.config.menus.push('connector')
+      //     this.$editorTools.addStuBgBtn(this, this.editor)
+      // } else {
+      //     this.$editorTools.addCanvas(this, this.editor)
+      // }
+      let curLang = localStorage.getItem("local") || "zh-cn"
+      if (curLang === "zh-tw") {
+        // 自定义语言
+        this.editor.config.languages["tw"] = editor_tw_config
+        // 选择语言
+        this.editor.config.lang = "tw"
+        // 引入 i18next 插件
+        this.editor.i18next = i18next
+      }
+      if (curLang === "en-us") {
+        // 自定义语言
+        this.editor.config.languages["en"] = editor_en_config
+        // 选择语言
+        this.editor.config.lang = "en"
+        // 引入 i18next 插件
+        this.editor.i18next = i18next
+      }
+      this.editor.create()
+      if (this.close) {
+        this.editor.disable()
+      }
+      this.editor.txt.clear()
+      if (this.examInfo.length > 0) {
+        this.editor.txt.html(this.examInfo[0])
+      }
+    },
+    markStuAnswer() {
+      let answerIframe = ""
+      answerIframe = document.getElementById("answerIframe")
+      answerIframe.contentWindow.document.body.style.width = "fit-content"
+      answerIframe.contentWindow.document.body.style.minWidth = "600px"
+      answerIframe.contentWindow.document.body.style.backgroundColor =
+        "#f5f5f5"
+      console.log(
+        document.getElementById("answerIframe").contentWindow.document
+      )
+      let iframe = document
+        .getElementById("answerIframe")
+        .contentWindow.document.getElementsByTagName("p")
+
+      if (iframe.length > 0) {
+        for (let i = 0; i < iframe.length - 1; i++) {
+          iframe[i].style.lineHeight = "50px"
+          iframe[i].style.paddingBottom = "30px"
+        }
+        iframe[iframe.length - 1].style.paddingBottom = "100px"
+        iframe[iframe.length - 1].style.lineHeight = "50px"
+      }
+      html2canvas(answerIframe.contentWindow.document.body).then(
+        (canvas) => {
+          if (!this.isConnector) {
+            this.markStatus = true
+          }
+          this.markBg = canvas.toDataURL("image/png")
+        }
+      )
+    },
+    /* 上传文件到BLOB */
+    doUploadFile(file) {
+      return new Promise(async (r, j) => {
+        let { scope, cntr, examId, subjectId, stuId } = this.getComposeData
+        const sasData = scope === 'school' ? await this.getSchoolSas() : await this.$api.blob.blobSasRCW({ name: cntr, role: 'teacher' })
+        let containerClient = new BlobTool(sasData.url, sasData.name, '?' + sasData.sas, scope)
+        containerClient.upload(file, {
+          path: `exam/${examId}/${subjectId}/${stuId}`,
+          checkSize: false
+        }).then(res => {
+          let fileFullPath = res.url + '?' + sasData.sas
+          r(fileFullPath)
+        }).catch(err => {
+          console.log(err);
+          j(err)
         })
+      })
+
+    },
+  },
+  mounted() {
+    this.initEditor()
+    this.getInfo()
+    // 监听富文本画板功能
+    this.$EventBus.$off("onStuCanvas")
+    this.$EventBus.$on("onStuCanvas", (editor) => {
+      this.markBg = ""
+      this.markStuAnswer()
+    })
+    /* 富文本上传音视频 */
+    this.$EventBus.$off("selectFileFinish")
+    this.$EventBus.$on("selectFileFinish", (fileObj) => {
+      this.doUploadFile(fileObj.file).then(fileFullPath => {
+        if (fileObj.type === 'audio') {
+          this.editor.selection.getSelectionStartElem().elems[0].innerHTML += `<span><span>&nbsp;&nbsp;</span><span class="richText-audio" contenteditable="false" >
+					<span class="audio-info">
+						<i class="ivu-icon ivu-icon-ios-musical-notes" style="font-size: 24px;margin:0 10px"></i>
+						<span class="audio-name">${fileObj.file.name}</span>
+					</span>
+					<audio src="${fileFullPath}"  id="audio" controls="controls" controlsList="nodownload"></audio>
+				</span><span>&nbsp;</span></span>`
+        } else if (fileObj.type === 'video') {
+          this.editor.selection.getSelectionStartElem().elems[0].innerHTML += `<span><span>&nbsp;</span><video src="${fileFullPath}" class="richText-video" width="300" preload controls="controls"></video><span>&nbsp;</span></span>`
+        }
+        this.editor.change.emit()
+      })
+    })
+
+  },
+  computed: {
+    isConnector() {
+      return (
+        this.itemInfo.type == "connector" ||
+        this.itemInfo.type == "correct"
+      )
     },
-    computed: {
-        isConnector() {
-            return (
-                this.itemInfo.type == "connector" ||
-                this.itemInfo.type == "correct"
-            )
-        },
+    ...mapGetters([
+      "getComposeData",
+    ]),
+  },
+  watch: {
+    index() {
+      this.getInfo()
+      deep: true
+      immediate: true
     },
-    watch: {
-        index() {
-            this.getInfo()
-            deep: true
-            immediate: true
-        },
-        editorContent() {
-            if(!this.isConnector) {
-                this.$emit("dataGet", this.editorContent, this.index)
-            }
-        },
+    editorContent() {
+      if (!this.isConnector) {
+        this.$emit("dataGet", this.editorContent, this.index)
+      }
     },
+  },
 }
 </script>
 <style scoped>
 .content {
-    width: 100%;
-    height: 100%;
+  width: 100%;
+  height: 100%;
 }
 .connector-wrap /deep/ .demo {
-    justify-content: flex-start !important;
+  justify-content: flex-start !important;
 }
 .textArea {
-    width: 100%;
-    height: 100%;
+  width: 100%;
+  height: 100%;
 }
 .frame {
-    /*height: 100px;*/
-    border: none;
-    position: fixed;
-    height: 0.1px;
-    width: 50%;
-    /*margin-left:10px;*/
-    /*line-height: 50px;*/
+  /*height: 100px;*/
+  border: none;
+  position: fixed;
+  height: 0.1px;
+  width: 50%;
+  /*margin-left:10px;*/
+  /*line-height: 50px;*/
 }
 .canvas-tools {
-    bottom: -50px;
+  bottom: -50px;
 }
 </style>

+ 6 - 0
TEAMModelOS/ClientApp/src/utils/blobTool.js

@@ -183,6 +183,7 @@ export default class BlobTool {
     // upload(file, path, option = {}, checkSize = true, root = '/') { //原来参数格式
     upload(file, config = {}, option = {}) {
         let { path, root, checkSize, checkExist } = config
+        console.log(config)
         //验证和初始化参数
         if (!path) throw new Error('上传失败:config.path必传')
         root = root || '/'
@@ -626,7 +627,12 @@ export default class BlobTool {
         return new Promise(async (r, j) => {
             try {
                 let sizeRes = await BlobTool.getContainerSize(this.container, scope)
+                console.log(scope)
+                console.log(sizeRes)
                 if (sizeRes) {
+                    console.log(sizeRes.total)
+                    console.log(this.blobSpace * 1024 * 1024 * 1024)
+
                     r(sizeRes.total > this.blobSpace * 1024 * 1024 * 1024)
                 } else {
                     j('容器空间判断失败!')

+ 1 - 0
TEAMModelOS/ClientApp/src/utils/editorLangEn.js

@@ -1,5 +1,6 @@
 let editor_en_config = {
     wangEditor: {
+        重置: 'Reset',
         插入: 'Insert',
         默认: 'Default',
         创建: 'Create',

+ 1 - 0
TEAMModelOS/ClientApp/src/utils/editorLangTw.js

@@ -1,5 +1,6 @@
 let editor_tw_config = {
     wangEditor: {
+        重置: '重设',
         插入: '插入',
         默认: '默認',
         创建: '創建',

+ 146 - 105
TEAMModelOS/ClientApp/src/utils/editorTools.js

@@ -29,7 +29,7 @@ export default {
 	normalizeSafariSpaceSpans(htmlString) {
 		return htmlString.replace(/<span(?: class="Apple-converted-space"|)>(\s+)<\/span>/g, (fullMatch, spaces) => {
 			return spaces.length === 1 ? ' ' : Array(spaces.length + 1).join('\u00A0 ').substr(0, spaces
-			.length);
+				.length);
 		});
 	},
 	/* 粘贴文本的解析和轻量化操作 */
@@ -79,7 +79,7 @@ export default {
 	},
 	/* 粘贴事件监听 */
 	pasteEvent(e, editor) {
-		if(e.target.nodeName === 'INPUT'){
+		if (e.target.nodeName === 'INPUT') {
 			return
 		}
 		let that = this
@@ -148,7 +148,7 @@ export default {
 			}
 		}
 	},
-	initEditorLang(editor){
+	initEditorLang(editor) {
 		let curLang = localStorage.getItem('local') || 'zh-cn'
 		if (curLang === 'zh-tw') {
 			// 自定义语言
@@ -192,7 +192,7 @@ export default {
 		this.addVideoUpload(vm, editor)
 		this.addAudio(vm, editor)
 		this.addCanvas(vm, editor)
-		this.addTextDot(vm,editor)
+		this.addTextDot(vm, editor)
 		// editor.config.pasteFilterStyle = false
 		editor.config.uploadImgMaxSize = 2 * 1024 * 1024 // 2M
 		editor.config.uploadImgShowBase64 = true;
@@ -220,17 +220,25 @@ export default {
 		isOption && editor.config.menus.splice(editor.config.menus.indexOf('video'), 1)
 		let dom = editor.toolbarSelector
 		if (dom) {
-			editor.config.pasteTextHandle = function(pasteStr) {
-				return ''
-			}
-			dom.removeEventListener('paste', e => {
-				this.pasteEvent(e, editor)
-			})
-			dom.addEventListener('paste', e => {
-				this.pasteEvent(e, editor)
-			})
+			editor.config.pasteTextHandle = function (pasteStr) {
+				// 对粘贴的文本进行处理,然后返回处理后的结果
+				var newStr = pasteStr.replace(
+					/@font-face{[^>]*div.Section0{page:Section0;}/g,
+					""
+				);
+				return newStr;
+			};
+			// editor.config.pasteTextHandle = function (pasteStr) {
+			// 	return ''
+			// }
+			// dom.removeEventListener('paste', e => {
+			// 	this.pasteEvent(e, editor)
+			// })
+			// dom.addEventListener('paste', e => {
+			// 	this.pasteEvent(e, editor)
+			// })
 		} else {
-			editor.config.pasteTextHandle = function(pasteStr) {
+			editor.config.pasteTextHandle = function (pasteStr) {
 				// 对粘贴的文本进行处理,然后返回处理后的结果
 				var newStr = pasteStr.replace(
 					/@font-face{[^>]*div.Section0{page:Section0;}/g,
@@ -260,7 +268,7 @@ export default {
 			editor.i18next = i18next
 		}
 		// editor.config.pasteFilterStyle = false
-		editor.config.pasteTextHandle = function(pasteStr) {
+		editor.config.pasteTextHandle = function (pasteStr) {
 			// 对粘贴的文本进行处理,然后返回处理后的结果
 			var newStr = pasteStr.replace(
 				/@font-face{[^>]*div.Section0{page:Section0;}/g,
@@ -286,7 +294,7 @@ export default {
 		]
 	},
 	/* 添加自定义本地视频上传功能 */
-	addVideoUpload(vm, editor) {
+	addVideoUpload(vm, editor, isSimple) {
 		// 获取必要的变量,这些在下文中都会用到
 		const {
 			$,
@@ -370,7 +378,11 @@ export default {
 										// editor.selection.getSelectionStartElem().elems[0].innerHTML += '<span><span>&nbsp;</span><video src=' +
 										// 	url + ' class="richText-video" width="300" preload controls="controls"></video><span>&nbsp;</span></span>'
 										// editor.change.emit()
-										$tools.doUploadVideo(vm, fileList[0], editor)
+										if (isSimple) {
+											app.$EventBus.$emit('selectFileFinish', { file: fileList[0], type: 'video' })
+										} else {
+											$tools.doUploadVideo(vm, fileList[0], editor)
+										}
 									}
 								}
 								// 返回 true 可关闭 panel
@@ -389,10 +401,10 @@ export default {
 				return conf;
 			}
 
-			command(value) {}
+			command(value) { }
 
 			// 菜单是否需要激活
-			tryChangeActive() {}
+			tryChangeActive() { }
 		}
 
 		// 注册菜单
@@ -401,10 +413,10 @@ export default {
 
 		// 将菜单加入到 editor.config.menus 中
 		// 也可以通过配置 menus 调整菜单的顺序,参考【配置菜单】部分的文档
-		// editor.config.menus = editor.config.menus.concat(videoUpload);
+		editor.config.menus = editor.config.menus.concat(videoUpload);
 	},
 	/* 添加自定义本地音频上传功能 */
-	addAudio(vm, editor) {
+	addAudio(vm, editor, isSimple) {
 		// 获取必要的变量,这些在下文中都会用到
 		const {
 			$,
@@ -479,7 +491,11 @@ export default {
 									if (fileList[0].type.slice(0, 5) !== 'audio') {
 										Message.warning(app.$t('utils.audioFormatError'))
 									} else {
-										$tools.doUploadAudio(vm, fileList[0], editor)
+										if (isSimple) {
+											app.$EventBus.$emit('selectFileFinish', { file: fileList[0], type: 'audio' })
+										} else {
+											$tools.doUploadAudio(vm, fileList[0], editor)
+										}
 									}
 								}
 								// 返回 true 可关闭 panel
@@ -498,10 +514,10 @@ export default {
 				return conf;
 			}
 
-			command(value) {}
+			command(value) { }
 
 			// 菜单是否需要激活
-			tryChangeActive() {}
+			tryChangeActive() { }
 		}
 
 		// 注册菜单
@@ -540,7 +556,7 @@ export default {
 					onOk: () => {
 						app.$EventBus.$emit('doCanvasConfirm')
 					},
-					render(h){
+					render(h) {
 						return h('div', {
 							style: {
 								display: 'flex',
@@ -571,7 +587,7 @@ export default {
 										// let widthPercent = val.width > 800 ? '50%' : '100%'
 										editor.txt.append('<img  src=' + val.base64 +
 											' class="richText-canvas" style="max-width:100%"></img>'
-											);
+										);
 										Modal.remove()
 									},
 									onCloseModal() {
@@ -584,10 +600,10 @@ export default {
 				})
 			}
 
-			command(value) {}
+			command(value) { }
 
 			// 菜单是否需要激活
-			tryChangeActive() {}
+			tryChangeActive() { }
 		}
 
 		// 注册菜单
@@ -606,7 +622,7 @@ export default {
 			BtnMenu,
 			Panel
 		} = E;
-	
+
 		// 标题菜单的 class ,可作为 DropList 菜单的参考代码
 		class myPanel extends BtnMenu {
 			constructor(editor) {
@@ -617,7 +633,7 @@ export default {
 				);
 				super($elem, editor);
 			}
-	
+
 			clickHandler() {
 				// Modal.props.fullscreen.default = true
 				app.$EventBus.$emit('clickResource')
@@ -636,7 +652,7 @@ export default {
 								maxWidth: '1200px',
 								margin: '0 auto'
 							},
-	
+
 						}, [
 							h('div', {
 								style: {
@@ -648,10 +664,10 @@ export default {
 							}),
 							h('NewChooseContent', {
 								props: {
-									vm:vm,
-									showSyllabus:false,
-									showQuestion:false,
-									showPaper:false
+									vm: vm,
+									showSyllabus: false,
+									showQuestion: false,
+									showPaper: false
 								},
 								on: {
 									'on-file-change'(val) {
@@ -666,29 +682,29 @@ export default {
 					},
 				})
 			}
-	
-			command(value) {}
-	
+
+			command(value) { }
+
 			// 菜单是否需要激活
-			tryChangeActive() {}
+			tryChangeActive() { }
 		}
-	
+
 		// 注册菜单
 		const resourceDraw = "resource"; // 菜单 key ,各个菜单不能重复
 		editor.menus.extend("resource", myPanel);
-	
+
 		// 将菜单加入到 editor.config.menus 中
 		// 也可以通过配置 menus 调整菜单的顺序,参考【配置菜单】部分的文档
 		editor.config.menus = editor.config.menus.concat(resourceDraw);
 	},
-	
+
 	/* 添加文字下加重点功能 */
 	addTextDot(vm, editor) {
 		const {
 			$,
-			BtnMenu 
+			BtnMenu
 		} = E;
-		class TextDot extends BtnMenu{
+		class TextDot extends BtnMenu {
 			constructor(editor) {
 				const $elem = $(
 					`<div class="w-e-menu" style="color:red" title="${app.$t('evaluation.editor.addTextDot')}"><i class="icon iconfont icon-heavy" style="font-size: 20px;display: inline-block;margin-top: 3px;"></i></div>`
@@ -699,7 +715,7 @@ export default {
 			/**
 			 * 点击事件
 			 */
-			clickHandler(){
+			clickHandler() {
 				const editor = this.editor
 				const isSelectEmpty = editor.selection.isSelectionEmpty()
 				// console.log(editor)
@@ -711,16 +727,16 @@ export default {
 					editor.selection.createEmptyRange()
 					editor.selection.collapseRange()
 					editor.selection.restoreSelection()
-				}else{
+				} else {
 					let selectDom = editor.selection.getSelectionStartElem().elems[0]
 					let selectText = editor.selection.getSelectionText()
-					if(selectDom.localName === 'dot'){
+					if (selectDom.localName === 'dot') {
 						// 如果选中的文本 本身就有着重号 就去除
 						editor.cmd.do('insertHTML', selectText)
-					}else{
+					} else {
 						// 添加着重号
 						// console.log(selectText)
-						let textArr = selectText.replace(/\u200B/g,'').split('').filter(s => s && s.trim())
+						let textArr = selectText.replace(/\u200B/g, '').split('').filter(s => s && s.trim())
 						// console.log(textArr)
 						let html = ``
 						// let html = `&#8203<dot>${selectText}</dot>&#8203`
@@ -736,7 +752,7 @@ export default {
 			/**
 			 * 尝试修改菜单激活状态
 			 */
-			tryChangeActive(){
+			tryChangeActive() {
 				const editor = this.editor
 				if (editor.cmd.queryCommandState('textDot')) {
 					this.active()
@@ -745,7 +761,7 @@ export default {
 				}
 			}
 		}
-		
+
 		// 注册菜单
 		const TextDotKey = "textDot"; // 菜单 key ,各个菜单不能重复
 		editor.menus.extend("textDot", TextDot);
@@ -777,7 +793,7 @@ export default {
 				panel.create();
 			}
 
-			tryChangeActive() {}
+			tryChangeActive() { }
 		}
 
 		// 注册菜单
@@ -801,7 +817,7 @@ export default {
 			// 公式输入插件
 			constructor(editors) {
 				const $elem = $(
-					`<div class="w-e-menu" title="${ app.$t('evaluation.editor.canvasStemTip') }" style="color:red"><i class="icon iconfont icon-brush" style="font-size: 20px;"></i></div>`
+					`<div class="w-e-menu" title="${app.$t('evaluation.editor.canvasStemTip')}" style="color:red"><i class="icon iconfont icon-brush" style="font-size: 20px;"></i></div>`
 				);
 				super($elem, editors);
 			}
@@ -810,7 +826,7 @@ export default {
 				app.$EventBus.$emit('onStuCanvas', editor)
 			}
 
-			tryChangeActive() {}
+			tryChangeActive() { }
 		}
 
 		// 注册菜单
@@ -819,14 +835,39 @@ export default {
 
 		// 将菜单加入到 editor.config.menus 中
 		// 也可以通过配置 menus 调整菜单的顺序,参考【配置菜单】部分的文档
-		//editor.config.menus = editor.config.menus.concat(videoUpload);
+		editor.config.menus = editor.config.menus.concat(videoUpload);
+	},
+	/* 获取富文本中音视频相对路径 */
+	getRelativeSrcPath(richText) {
+		let videoSrcList = this.getRichTextSrc(richText, 'video')
+		let audioSrcList = this.getRichTextSrc(richText, 'audio')
+		let srcList = videoSrcList.concat(audioSrcList)
+		if (srcList.length) {
+			srcList.forEach(src => {
+				let withSas = src.split('/')[src.split('/').length - 1]
+				let j = withSas.lastIndexOf('?')
+				richText = richText.replaceAll(src, decodeURI(withSas.slice(0, j)));
+			})
+		}
+		return richText
+	},
+	/* 获取富文本中音视频完整路径 */
+	getMideaFullPath(richText, host, sas) {
+		let videoSrcList = this.getRichTextSrc(richText, 'video')
+		let audioSrcList = this.getRichTextSrc(richText, 'audio')
+		let srcList = videoSrcList.concat(audioSrcList)
+		if (srcList.length) {
+			srcList.forEach(src => {
+				let fullSrc = host + src + sas
+				richText = richText.replaceAll(`src="${src}"`, `src="${fullSrc}"`);
+			})
+		}
+		return richText
 	},
 	/* 移除上传的音视频富文本src中的HOST */
 	doRemoveMideaHost(exerciseItem) {
 		console.log('removeHost', exerciseItem.question)
-		let isSubjective = exerciseItem.type === 'complete' || exerciseItem.type === 'subjective' || exerciseItem
-			.type ===
-			'compose'
+		let isSubjective = exerciseItem.type === 'complete' || exerciseItem.type === 'subjective' || exerciseItem.type === 'compose'
 		let richTextObj = {
 			question: exerciseItem.question,
 			answer: Array.isArray(exerciseItem.answer) && exerciseItem.answer.length ? exerciseItem.answer[0] :
@@ -846,7 +887,7 @@ export default {
 						srcList.forEach(src => {
 							let withSas = src.split('/')[src.split('/').length - 1]
 							let j = withSas.lastIndexOf('?')
-							richTextObj[key] = richTextObj[key].replace(src, decodeURI(
+							richTextObj[key] = richTextObj[key].replaceAll(src, decodeURI(
 								withSas.slice(0, j)));
 						})
 						if (key === 'answer' && Array.isArray(exerciseItem.answer) && exerciseItem
@@ -896,13 +937,13 @@ export default {
 									let imgList = [];
 									let srcList = []
 									item.replace(srcReg, async (
-											match, capture
-											) => {
-											imgList.push(
-												match);
-											srcList.push(
-												capture)
-										});
+										match, capture
+									) => {
+										imgList.push(
+											match);
+										srcList.push(
+											capture)
+									});
 									// 如果富文本中有图片内容 则提取所有src的值
 									if (imgList.length) {
 										for (let j = 0; j < imgList
@@ -912,14 +953,14 @@ export default {
 											// 如果图片有被设置过宽高即有被拉伸过之后 则根据新的宽高来生成新的base64作为图片src
 											if (heightReg.test(
 												item) && widthReg
-												.test(item)) {
+													.test(item)) {
 												let srcHeight = ''
 												let srcWidth = ''
 												item.replace(
 													heightReg,
 													async (match,
 														capture
-														) => {
+													) => {
 														srcHeight
 															=
 															capture
@@ -928,17 +969,17 @@ export default {
 													widthReg,
 													async (match,
 														capture
-														) => {
+													) => {
 														srcWidth
 															=
 															capture
 													});
 												let newBase64 =
 													await this
-													.compressImg(
-														src,
-														srcWidth,
-														srcHeight)
+														.compressImg(
+															src,
+															srcWidth,
+															srcHeight)
 												item = item.replace(
 													src,
 													newBase64)
@@ -946,58 +987,58 @@ export default {
 											} else if (!heightReg
 												.test(item) &&
 												widthReg.test(item)
-												) {
+											) {
 												// 取到宽度的百分比
 												let srcWidth = ''
 												item.replace(
 													widthReg,
 													async (match,
 														capture
-														) => {
+													) => {
 														srcWidth
 															=
 															parseInt(
 																capture
-																)
+															)
 													});
 												let img =
-												new Image();
+													new Image();
 												img.src = src;
 												// 如果是宽度 则需要根据编辑器的宽度来计算出 拉伸后的高度和宽度
 												img.onload =
-											async () => {
-													let newWidth =
-														editorWidth
-														.optionWidth *
-														srcWidth *
-														0.01
-													let newHeight =
-														newWidth *
-														(img.height /
-															img
-															.width
-															)
-													let newBase64 =
-														await this
-														.compressImg(
-															src,
-															newWidth,
-															newHeight
+													async () => {
+														let newWidth =
+															editorWidth
+																.optionWidth *
+															srcWidth *
+															0.01
+														let newHeight =
+															newWidth *
+															(img.height /
+																img
+																	.width
 															)
-													item =
-														item
-														.replace(
-															src,
-															newBase64
-															)
-													r(item)
-												}
+														let newBase64 =
+															await this
+																.compressImg(
+																	src,
+																	newWidth,
+																	newHeight
+																)
+														item =
+															item
+																.replace(
+																	src,
+																	newBase64
+																)
+														r(item)
+													}
 											} else {
 												// 如果没有拉伸或者点击百分比显示 则按照原图进行压缩 宽高不变
 												let newBase64 =
 													await this
-													.compressImg(
-														src, 0, 0)
+														.compressImg(
+															src, 0, 0)
 												item = item.replace(
 													src,
 													newBase64)

+ 5 - 5
TEAMModelOS/ClientApp/src/utils/evTools.js

@@ -300,7 +300,7 @@ export default {
 								let blobUrl = src.includes(curHost) ? src.split('?')[0] : (curHost +  '/' + examContainer  + paperItem.blob +  '/' + src)
 								try{
 									let addSasUrl = await $tools.getFileSas(blobUrl)
-									richTextObj[key] = richTextObj[key].replace(`src="${ src }"`, `src="${ addSasUrl.url }"`);
+									richTextObj[key] = richTextObj[key].replaceAll(`src="${ src }"`, `src="${ addSasUrl.url }"`);
 								}catch(e){
 									j(500)
 								}
@@ -308,18 +308,18 @@ export default {
 								let blobUrl = src.includes(curHost) ? src.split('?')[0] : (curHost +  '/'  + container + '/paper/' + paperItem.name + '/' + src)
 								try{
 									let addSasUrl = await $tools.getFileSas(blobUrl)
-									richTextObj[key] = richTextObj[key].replace(`src="${ src }"`, `src="${ addSasUrl.url }"`);
+									richTextObj[key] = richTextObj[key].replaceAll(`src="${ src }"`, `src="${ addSasUrl.url }"`);
 								}catch(e){
 									j(500)
 								}
 							}else if(nodeId){
 								// 如果是在课纲读取课纲目录下的试题
-								let srcPath = exerciseItem.blob.replace(`${exerciseItem.id}.json`,'').split('?')[0]
+								let srcPath = exerciseItem.blob.replaceAll(`${exerciseItem.id}.json`,'').split('?')[0]
 								let blobUrl = exerciseItem.blob.includes(curHost) ? (srcPath + src) : (curHost + '/' + container +  srcPath + src)
 								// debugger;
 								try{
 									let addSasUrl = await $tools.getFileSas(blobUrl)
-									richTextObj[key] = richTextObj[key].replace(`src="${ src }"`, `src="${ addSasUrl.url }"`);
+									richTextObj[key] = richTextObj[key].replaceAll(`src="${ src }"`, `src="${ addSasUrl.url }"`);
 								}catch(e){
 									j(500)
 								}
@@ -327,7 +327,7 @@ export default {
 								let blobUrl = src.includes(curHost) ? src.split('?')[0] : (curHost +  '/'  + container + '/item/' + exerciseItem.id + '/' + src)
 								try{
 									let addSasUrl = await $tools.getFileSas(blobUrl)
-									richTextObj[key] = richTextObj[key].replace(`src="${ src }"`, `src="${ addSasUrl.url }"`);
+									richTextObj[key] = richTextObj[key].replaceAll(`src="${ src }"`, `src="${ addSasUrl.url }"`);
 								}catch(e){
 									j(500)
 								}

+ 1 - 1
TEAMModelOS/ClientApp/src/view/Home.vue

@@ -8,7 +8,7 @@
                 <Icon type="md-open" @click="changePlatform" :title="$t('system.changePlat')" v-if="hasArea" />
                 <!-- <Icon type="md-help-circle" @click="goHelpDoc" :title="$t('system.help')" v-if="!inJinNiu" /> -->
                 <Icon type="md-help-circle" @click="goHelpDoc" :title="$t('system.help')" v-if="!inGlobalSite && inIES5" />
-                <Icon type="md-text" @mouseover.native="isShowQrCode = true" @mouseleave.native="isShowQrCode = false" v-if="!inGlobalSite && !inJinNiu" />
+                <Icon type="md-text" @mouseover.native="isShowQrCode = true" @mouseleave.native="isShowQrCode = false" v-if="!inGlobalSite && inIES5" />
                 <BaseNotification :msgs="msgs"></BaseNotification>
                 <span class="header-split"></span>
                 <BaseUserPoptip @logout="basicMenu('quit')"></BaseUserPoptip>

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

@@ -4,7 +4,7 @@
 			<span class="ev-title">
 				<Icon type="ios-paper" />
 				{{ exerciseScope === 1 ? $t('evaluation.newExercise.newSchoolItem') : $t('evaluation.newExercise.newPrivateItem') }}
-				<span style="font-size: 12px;margin-left: 10px;">
+				<span style="font-size: 12px;margin-left: 10px;display: none">
 					<span v-if="isHiToolAlive" style="color: #10abe7;">
 						* {{ $t('evaluation.toolOpenTip') }}
 					</span>

+ 8 - 3
TEAMModelOS/ClientApp/src/view/teachermgmt/components/mgt/TeacherMgt.vue

@@ -685,9 +685,10 @@ export default {
                 this.$t('teachermgmt.page.text3'),
                 this.$t('teachermgmt.table.th3'),
                 this.$t('teachermgmt.table.th4'),
-                this.$t('teachermgmt.impTd5')
+                this.$t('teachermgmt.impTd5'),
+                this.$t('teachermgmt.expTd')
             ]
-            let key = ['name', 'id', 'subject', 'group', 'job', 'auth', 'note']
+            let key = ['name', 'id', 'subject', 'group', 'job', 'auth', 'note', 'status']
             console.log(title, key)
             let data = this._.cloneDeep(this.teachers)
             data = data.map(item => {
@@ -708,7 +709,8 @@ export default {
                     group: this.getGroupName(item.groups),
                     job: item.job || this.$t('teachermgmt.job.teacher'),
                     auth: auth,
-                    note: item.note
+                    note: item.note,
+                    status: item.status == 'import' ? this.$t('teachermgmt.teacherStatus1') : item.status == 'invite' ? this.$t('teachermgmt.teacherStatus2') : this.$t('teachermgmt.teacherStatus3')
                 }
             })
             console.log(data)
@@ -1222,6 +1224,9 @@ export default {
 @import "./TeacherMgt.less";
 </style>
 <style lang="less">
+.el-cascader-custom {
+    z-index: 99999 !important;
+}
 .action-wrap .role-set-btn {
     background: #1cc0f3;
     color: white;