Browse Source

Merge branch 'develop' of http://163.228.141.122:3000/TEAMMODEL/TEAMModelOS into develop

jeff 7 months ago
parent
commit
4f8f711986

+ 1 - 0
TEAMModelOS.Extension/HTEX.Lib/DOCX/Models/LangConfig.cs

@@ -34,5 +34,6 @@ namespace HTEXLib.DOCX.Models
         public string Level { get; set; }
         public string Count { get; set; }
         public string Taxonomy { get; set; }
+        public string Tag { get; set; }
     }
 }

+ 4 - 0
TEAMModelOS.Extension/HTEX.Lib/PPTX/Models/BaseItem.cs

@@ -38,6 +38,10 @@ namespace HTEXLib
         public string pid { get; set; }
         //管理知识点
         public List<string> knowledge { get; set; }
+        /// <summary>
+        /// 標簽
+        /// </summary>
+        public List<string > tags { get; set; } = new List<string>();
         //认知层次 应用 综合 理解 评鉴 知识
         public int? @field { get; set; }
 

+ 33 - 3
TEAMModelOS.Extension/HTEX.Lib/Translator/HTML2ITEMV3Translator.cs

@@ -33,6 +33,7 @@ namespace HTEXLib.Translator
         public const string Level = "Level";
         public const string Count = "Count";
         public const string Taxonomy = "Taxonomy";
+        public const string Tag = "Tag";
         //  public LangConfig langConfig { get; set; }
         //  public HtmlDocument doc { get; set; } = new HtmlDocument();
         public List<List<string>> optionsKeys { get; set; } = new List<List<string>>();
@@ -71,12 +72,12 @@ namespace HTEXLib.Translator
                 dict[_langConfig.Item.Answer] = new string[] { _langConfig.Lang, Answer };
                 dict[_langConfig.Item.Analysis] = new string[] { _langConfig.Lang, Analysis };
                 dict[_langConfig.Item.Taxonomy] = new string[] { _langConfig.Lang, Taxonomy };
-                
                 dict[_langConfig.Item.Ended] = new string[] { _langConfig.Lang, Ended };
                 dict[_langConfig.Item.Point] = new string[] { _langConfig.Lang, Point };
                 dict[_langConfig.Item.Score] = new string[] { _langConfig.Lang, Score };
                 dict[_langConfig.Item.Level] = new string[] { _langConfig.Lang, Level };
                 dict[_langConfig.Item.Count] = new string[] { _langConfig.Lang, Count };
+                dict[_langConfig.Item.Tag]= new string[] { _langConfig.Lang, Tag };
                 foreach (string key in _langConfig.Item.Type.Keys)
                 {
                     dict[_langConfig.Item.Type[key]] = new string[] { _langConfig.Lang, Summary, key };
@@ -111,6 +112,7 @@ namespace HTEXLib.Translator
                 string scr = _TagConfig.Start + _langConfig.Item.Score + _TagConfig.End;
                 string lvl = _TagConfig.Start + _langConfig.Item.Level + _TagConfig.End;
                 string cut = _TagConfig.Start + _langConfig.Item.Count + _TagConfig.End;
+                string tag = _TagConfig.Start + _langConfig.Item.Tag + _TagConfig.End;
                 string[] Fileds = _langConfig.Item.Filed.Split('|');
                 foreach (var filed in Fileds)
                 {
@@ -127,6 +129,7 @@ namespace HTEXLib.Translator
                 string[] scrarry = scr.Select(s => s.ToString()).ToArray();
                 string[] lvlarry = lvl.Select(s => s.ToString()).ToArray();
                 string[] cutarry = cut.Select(s => s.ToString()).ToArray();
+                string[] tagarry = tag.Select(s => s.ToString()).ToArray();
                 string ansReg = string.Join("\\s*", ansarry);
                 string alsReg = string.Join("\\s*", alsarry);
                 string taxReg = string.Join("\\s*", taxarry);
@@ -135,6 +138,7 @@ namespace HTEXLib.Translator
                 string scrReg = string.Join("\\s*", scrarry);
                 string lvlReg = string.Join("\\s*", lvlarry);
                 string cutReg = string.Join("\\s*", cutarry);
+                string tagReg = string.Join("\\s*", tagarry);
                 html = Regex.Replace(html, ansReg, ans);
                 html = Regex.Replace(html, alsReg, als);
                 html = Regex.Replace(html, taxReg, tax);
@@ -143,11 +147,12 @@ namespace HTEXLib.Translator
                 html = Regex.Replace(html, scrReg, scr);
                 html = Regex.Replace(html, lvlReg, lvl);
                 html = Regex.Replace(html, cutReg, cut);
+                html = Regex.Replace(html, tagReg, tag);
                 string blankReg = "\\s*";
                 foreach (string value in _langConfig.Item.Type.Values)
                 {
-                    string tag = $"{_TagConfig.Start}\\s*\\d+\\s*{string.Join("\\s*", value.Select(s => s.ToString()).ToArray())}\\s*{_TagConfig.End}";
-                    var m = Regex.Match(html, tag);
+                    string tagStr = $"{_TagConfig.Start}\\s*\\d+\\s*{string.Join("\\s*", value.Select(s => s.ToString()).ToArray())}\\s*{_TagConfig.End}";
+                    var m = Regex.Match(html, tagStr);
                     while (m.Success)
                     {
                         string blankStr = Regex.Replace(m.Value, blankReg, "");
@@ -414,6 +419,18 @@ namespace HTEXLib.Translator
                                 openFlag = true;
                                 openTagLang = keyInfo[0];
                                 break;
+                            case Tag:
+                                //下列代码不能调整顺序
+                                if (!string.IsNullOrEmpty(openTag) && openFlag)
+                                {
+                                    DoOpenTag(openTag, openTagVal, openFlag, openTagLang, content, test);
+                                    content = new StringBuilder();
+                                }
+                                openTag = Tag;
+                                openTagVal = tagValue;
+                                openFlag = true;
+                                openTagLang = keyInfo[0];
+                                break;
                             case Score:
                                 //下列代码不能调整顺序
                                 if (!string.IsNullOrEmpty(openTag) && openFlag)
@@ -656,6 +673,19 @@ namespace HTEXLib.Translator
                             }
                         }
                         break;
+                    case Tag:
+                        string Tags = BlankPointTag(content.ToString());
+                        if (!string.IsNullOrWhiteSpace(Tags))
+                        {
+
+                            string[] ps = Regex.Split(Tags, "\\.|\\.|\\。|\\;|\\;");
+                            // string[] ps = Regex.Split(Points, "\\.|\\.|\\、|\\:|\\:|\\,|\\,|\\;|\\;");
+                            if (ps != null && ps.Length > 0)
+                            {
+                                test.tags = ps.Distinct().ToList();
+                            }
+                        }
+                        break;
                     case Score:
                         //单选或多选,判断答案 脱html标签
                         string Scores = BlankTag(content.ToString());

+ 7 - 0
TEAMModelOS.SDK/Models/Cosmos/Common/Inner/BaseItem.cs

@@ -28,6 +28,10 @@ namespace TEAMModelOS.SDK.Models
         public string pid { get; set; }
         //管理知识点
         public List<string> knowledge { get; set; }
+        /// <summary>
+        /// 標簽
+        /// </summary>
+        public List<string> tags { get; set; } = new List<string>();
         //认知层次 应用 综合 理解 评鉴 知识
         public int? field { get; set; }
         // 配分  
@@ -64,6 +68,9 @@ namespace TEAMModelOS.SDK.Models
         /// 来源 0.(学校,个人)文档导入 1.(学校,个人)自建 3.学科网,4多分
         /// </summary>
         public int source { get; set; }
+        /// <summary>
+        /// 學科網的標籤
+        /// </summary>
         public string tag { get; set; }
         /// <summary>
         /// 指定回答類型 (五種, 若沒有 anserType 就是原本默認的型態, client端會以文字為主來做界面)

+ 2 - 0
TEAMModelOS.SDK/Models/Cosmos/Student/StudentArtResult.cs

@@ -99,6 +99,8 @@ namespace TEAMModelOS.SDK.Models
         /// 评语
         /// </summary>
         public string comment { get; set; }
+        public List<ArtSubjectScore> subjectScores { get; set; } = new List<ArtSubjectScore>();
+        public List<ArtQuotaResult> results { get; set; } = new List<ArtQuotaResult>();
         public List<ArtQuotaPdf> allSubjectQuotas { get; set; } = new List<ArtQuotaPdf>();
         public List<ArtSubjectPdf> subjectPdfs { get; set; } = new List<ArtSubjectPdf>();
         public string blob { get; set;}

+ 8 - 3
TEAMModelOS.SDK/Models/Service/ArtService.cs

@@ -11,6 +11,7 @@ using System.Threading.Tasks;
 using TEAMModelOS.SDK.DI;
 using TEAMModelOS.SDK.Extension;
 using TEAMModelOS.SDK.Models.Cosmos.Common;
+using static TEAMModelOS.SDK.Models.ClassAnalysis;
 
 namespace TEAMModelOS.SDK.Models.Service
 {
@@ -277,9 +278,11 @@ namespace TEAMModelOS.SDK.Models.Service
                 });
                 string level = "";
                 double allScore = 0;
-                if (allSubjectArtQuotaPdfs.Any())
+                if (x.subjectScores.Any())
                 {
-                    allScore = allSubjectArtQuotaPdfs.Sum(a => a.score) / allSubjectArtQuotaPdfs.Count;
+                    //allScore = allSubjectArtQuotaPdfs.Sum(a => a.score) / allSubjectArtQuotaPdfs.Count;
+                   // allScore = allSubjectArtQuotaPdfs.Sum(a => a.score) / allSubjectArtQuotaPdfs.Count;
+                   allScore=Math.Round(x.subjectScores.Sum(a => a.score)*1.0/ x.subjectScores.Count,2) ;
                 }
 
                 if (allScore >= 100)
@@ -454,8 +457,10 @@ namespace TEAMModelOS.SDK.Models.Service
                     level = level,
                     score = allScore,
                     allSubjectQuotas = allSubjectQuotas.ToList(),
-                    comment =string.IsNullOrWhiteSpace(x.comment)? comment.ToString():x.comment,
+                    comment =string.IsNullOrWhiteSpace(x.comment) ? comment.ToString() : x.comment,
                     subjectPdfs = subjectPdfs,
+                    subjectScores=x.subjectScores,
+                    results=x.results
                 };
                 studentPdfs.Add(studentPdf);
             });

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

@@ -1974,7 +1974,14 @@ const LANG_EN_US = {
             tip11: 'The current exam file already has an answer card, do you want to continue using it?',
             tip12: 'Continue using it',
             tip13: 'Erase the answer card data',
-            unSaveTip: 'Are you sure you want to discard the modified test question data and exit??'
+            unSaveTip: 'Are you sure you want to discard the modified test question data and exit??',
+            totalQues: 'Total Questions',
+            volumeTips1: 'Total Questions in This Volume',
+            volumeTips2: 'Questions (De-duplicated)',
+            questype: 'Question Type',
+            tips14: 'Please set the total number of questions first',
+            tips15: 'Please select the question type first',
+            tips16: 'Please select the unit first',
         },
         points: {
             addPoint: 'Create Key Concept',

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

@@ -1973,7 +1973,14 @@ const LANG_ZH_CN = {
             tip11: '检测到当前试卷已存在答题卡,是否继续使用?',
             tip12: '继续使用',
             tip13: '抹除答题卡数据',
-            unSaveTip: '确认放弃修改的试题数据并退出?'
+            unSaveTip: '确认放弃修改的试题数据并退出?',
+            totalQues: '总题数',
+            volumeTips1: '当前册别共',
+            volumeTips2: '道题(已去重)',
+            questype: '题目类型',
+            tips14: '请先设置总题数',
+            tips15: '请先勾选题目类型',
+            tips16: '请先选择单元',
         },
         points: {
             addPoint: '新增知识点',

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

@@ -1976,7 +1976,14 @@ const LANG_ZH_TW = {
             tip11: '檢測到當前試卷已存在答題卡,是否繼續使用?',
             tip12: '繼續使用',
             tip13: '抹除答題卡數據',
-            unSaveTip: '確認放棄修改的試題數據並退出?'
+            unSaveTip: '確認放棄修改的試題數據並退出?',
+            totalQues: '總題數',
+            volumeTips1: '目前冊別共',
+            volumeTips2: '題(已去重)',
+            questype: '題目類型',
+            tips14: '請先設定總題數',
+            tips15: '請先勾選題目類型',
+            tips16: '請先選擇單元',
         },
         points: {
             addPoint: '新增知識點',

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

@@ -281,6 +281,7 @@ export default {
 							jsonData.exercise.option = jsonData.item[0].option
 							jsonData.exercise.id = list[i].id
 							jsonData.exercise.pid = jsonData.pid
+							if(inSyllabus && list[i].nodeId) jsonData.exercise.nodeId = list[i].nodeId
 							jsonData.exercise = await this.doAddHost(jsonData.exercise, null, null, inSyllabus)
 							r(jsonData.exercise)
 						} catch (e) {

+ 4 - 0
TEAMModelOS/ClientApp/src/view/artexam/Mgt.vue

@@ -471,6 +471,10 @@
 				this.$api.areaArt.findArtSummary(params).then(
 					(res) => {
 						this.artInfo = res.art;
+						this.artInfo.period = {
+							id: this.curPeriod.id,
+							name: this.curPeriod.name
+						}
 						this.artInfo.stuCount = res.count;
 						if (res.art.owner === "area" && res.ae?.jsonElement) {
 							this.examInfoList = res.ae.jsonElement;

+ 2 - 2
TEAMModelOS/ClientApp/src/view/evaluation/index/CreatePaper.vue

@@ -438,9 +438,9 @@ export default {
         .length)
     },
     /* 获取自动组题数据 */
-    async getAutoQuestions(questions) {
+    async getAutoQuestions(questions, isSyllabus) {
       try {
-        let autoQuestions = await this.$evTools.getFullItem(questions)
+        let autoQuestions = await this.$evTools.getFullItem(questions, undefined, isSyllabus)
         let arr = autoQuestions
         // 拿到题目后 根据试卷总分给所有题目进行平均分配
         let scoreArr = this.averageTotalScore(this.evaluationInfo.score, arr.length)

+ 508 - 126
TEAMModelOS/ClientApp/src/view/learnactivity/AutoCreateNew.vue

@@ -2,22 +2,37 @@
   <div class="auto-create-container">
     <p class="auto-create-title">{{$t('evaluation.createPaper.setAutoConditions')}}</p>
     <div class="auto-filter-wrap">
+      <div class="filter-wrap-item">
+        <span class="filter-title">{{ $t('cusMgt.typeLabel') }} : </span>
+        <div class="filter-content">
+          <RadioGroup v-model="originType" border @on-change="onFilterOriginChange" style="display: inline-block">
+              <Radio label="bank">{{ $t('utils.bank') }}</Radio>
+              <Radio label="syllabus">{{ $t('auth.syllabus') }}</Radio>
+          </RadioGroup>
+        </div>
+      </div>
       <div class="filter-wrap-item" v-if="!isSchoolPaper">
         <span class="filter-title">{{$t('evaluation.createPaper.origin')}} : </span>
         <div class="filter-content">
           <CheckboxGroup v-model="filterOrigin" @on-change="onFilterOriginChange">
-            <Checkbox label="private" v-if="!isSchoolPaper" :disabled="filterOrigin.length === 1 && filterOrigin[0] === 'private'">{{$t('evaluation.filter.privateBank')}}</Checkbox>
-            <Checkbox label="school" :disabled="onlySchool" v-if="hasSchool">{{$t('evaluation.filter.schoolBank')}}</Checkbox>
+            <Checkbox label="private" v-if="!isSchoolPaper" :disabled="filterOrigin.length === 1 && filterOrigin[0] === 'private'">
+              {{ originType === 'bank' ? $t('evaluation.filter.privateBank') : $t('courseManage.syllabus.personalSyllabus') }}
+            </Checkbox>
+            <Checkbox label="school" :disabled="onlySchool" v-if="hasSchool">
+              {{ originType === 'bank' ? $t('evaluation.filter.schoolBank') : $t('courseManage.syllabus.schoolSyllabus') }}
+            </Checkbox>
           </CheckboxGroup>
         </div>
       </div>
-      <div class="filter-wrap-item" v-if="includeSchool && filterOrigin.length > 1">
-        <span class="filter-title">{{$t('evaluation.createPaper.schoolRate')}} : </span>
-        <div class="filter-content">
-          <InputNumber :max="100" :min="0" :step="10" v-model="schoolRate"></InputNumber>
-          <span style="margin-left: 10px;min-width: 300px;color: #d1d1d1;"> % ( {{$t('evaluation.createPaper.rateTip')}} )</span>
+      <template v-if="originType === 'bank'">
+        <div class="filter-wrap-item" v-if="includeSchool && filterOrigin.length > 1">
+          <span class="filter-title">{{$t('evaluation.createPaper.schoolRate')}} : </span>
+          <div class="filter-content">
+            <InputNumber :max="100" :min="0" :step="10" v-model="schoolRate"></InputNumber>
+            <span style="margin-left: 10px;min-width: 300px;color: #d1d1d1;"> % ( {{$t('evaluation.createPaper.rateTip')}} )</span>
+          </div>
         </div>
-      </div>
+      </template>
       <div class="filter-wrap-item" v-if="includeSchool && !isSchoolPaper">
         <span class="filter-title">{{$t('evaluation.newExercise.choosePeriod')}} : </span>
         <div class="filter-content">
@@ -34,29 +49,95 @@
           </Select>
         </div>
       </div>
-      <div class="filter-wrap-item" v-for="(item,index) in quInfos" :key="index">
-        <span class="filter-title">{{ item.label }} :</span>
-        <div class="filter-content light-iview-select light-iview-input-number">
-          <span class="filter-content-text">{{$t('evaluation.filter.diff')}} </span>
-          <Select v-model="item.policy" @on-change="onSelectDiff(item,index)">
-            <Option v-for="(diff,index) in diffList" :value="diff.level" :key="diff.level" :label="diff.label">
-              <span>{{ diff.label }}</span>
-              <span style="margin-left: 10px;color: red;">({{ countResult.length ? countResult.find(i => i.type === item.type).counts[index] : 0 }})</span>
-            </Option>
-          </Select>
-          <span class="filter-content-text">{{$t('evaluation.createPaper.total')}} </span>
-          <InputNumber :max="canUseArr[index]" :min="0" v-model="item.count"></InputNumber>
-          <span class="filter-content-text" style="margin:0 10px"> {{$t('evaluation.createPaper.nums')}} </span>
+      <template v-if="originType === 'bank'">
+        <div class="filter-wrap-item" v-for="(item,index) in quInfos" :key="index">
+          <span class="filter-title">{{ item.label }} :</span>
+          <div class="filter-content light-iview-select light-iview-input-number">
+            <span class="filter-content-text">{{$t('evaluation.filter.diff')}} </span>
+            <Select v-model="item.policy" @on-change="onSelectDiff(item,index)">
+              <Option v-for="(diff,index) in diffList" :value="diff.level" :key="diff.level" :label="diff.label">
+                <span>{{ diff.label }}</span>
+                <span style="margin-left: 10px;color: red;">({{ countResult.length ? countResult.find(i => i.type === item.type).counts[index] : 0 }})</span>
+              </Option>
+            </Select>
+            <span class="filter-content-text">{{$t('evaluation.createPaper.total')}} </span>
+            <InputNumber :max="canUseArr[index]" :min="0" v-model="item.count"></InputNumber>
+            <span class="filter-content-text" style="margin:0 10px"> {{$t('evaluation.createPaper.nums')}} </span>
+          </div>
+          <span v-if="filterOrigin.length === 1">( <span style="color: #2c99c7;">{{ canUseArr[index] }}</span> {{ $t('evaluation.canChoose') }} )</span>
         </div>
-        <span v-if="filterOrigin.length === 1">( <span style="color: #2c99c7;">{{ canUseArr[index] }}</span> {{ $t('evaluation.canChoose') }} )</span>
-      </div>
-      <div class="filter-wrap-item">
-        <span class="filter-title">{{$t('evaluation.newExercise.knowledge')}} : </span>
-        <div class="filter-content">
-          <Tag v-for="(item,index) in relatePoints" color="success" :key="index" :name="item" closable @on-close="onClosePoint(index)">{{ item }}</Tag>
-          <Icon type="md-add-circle" @click="onSelectPoint" size="18" style="cursor: pointer;" />
+        <div class="filter-wrap-item">
+          <span class="filter-title">{{$t('evaluation.newExercise.knowledge')}} : </span>
+          <div class="filter-content">
+            <Tag v-for="(item,index) in relatePoints" color="success" :key="index" :name="item" closable @on-close="onClosePoint(index)">{{ item }}</Tag>
+            <Icon type="md-add-circle" @click="onSelectPoint" size="18" style="cursor: pointer;" />
+          </div>
         </div>
-      </div>
+      </template>
+      <template v-else>
+        <!-- <div class="filter-wrap-item" v-if="!isSchoolPaper">
+          <span class="filter-title">{{$t('evaluation.createPaper.origin')}} : </span>
+          <div class="filter-content">
+            <CheckboxGroup v-model="filterOrigin" @on-change="onFilterOriginChange" border style="display: inline-block">
+                <Checkbox label="private" v-if="!isSchoolPaper" :disabled="filterOrigin.length === 1 && filterOrigin[0] === 'private'">{{ $t('courseManage.syllabus.personalSyllabus') }}</Checkbox>
+                <Checkbox label="school" :disabled="onlySchool" v-if="hasSchool">{{ $t('courseManage.syllabus.schoolSyllabus') }}</Checkbox>
+            </CheckboxGroup>
+          </div>
+        </div> -->
+        <div class="filter-wrap-item">
+          <span class="filter-title">{{ $t('auth.select') }}{{ $t('selflearn.choose.book') }} : </span>
+          <div class="filter-content" style="max-width: 85%;">
+            <template v-if="volumeList.length">
+                <RadioGroup v-model="volumeFilter" @on-change="getTreeByVolumeId(volumeList[volumeFilter - 1])" border style="display: inline-block; margin-bottom: 10px;">
+                    <Radio v-for="(item, index) in volumeList" :key="index" :label="index + 1">
+                      {{ item.name }}
+                      <span style="color: #2db7f5;">({{ item.scope === 'school' ? $t('cusMgt.school') : $t('cusMgt.private') }})</span>
+                    </Radio>
+                </RadioGroup>
+            </template>
+            <span v-else>{{ $t('assessment.no') }}</span>
+          </div>
+        </div>
+        <template v-if="volumeList.length">
+          <div>
+            <el-tree ref="tree" :data="treeList" :props="defaultProps" class="tree" node-key="id" show-checkbox check-on-click-node accordion highlight-current @node-click="onNodeClick" :expand-on-click-node="false">
+                <div class="custom-tree-node" slot-scope="{ node, data }">
+                    <span class="tree-node-lable">
+                        <span class="text-cut" style="width: 60%;display: inline-block;vertical-align: bottom;" :title="data.title">
+                            {{ data.title }}
+                        </span>
+                    </span>
+                    <span class="ques-num">
+                      <!-- <span class="filter-content-text">{{$t('evaluation.createPaper.total')}} </span>
+                      {{ data.count }}
+                      <span class="filter-content-text" style="margin:0 10px"> {{$t('evaluation.createPaper.nums')}} </span> -->
+                        {{ $t('evaluation.index.item') }}:{{ data.quesList.length }}/{{ data.count }}
+                    </span>
+                </div>
+            </el-tree>
+          </div>
+          <div class="filter-wrap-item">
+            <span class="filter-title">{{ $t('evaluation.createPaper.totalQues') }} : </span>
+            <InputNumber :max="quesTotal" :min="0" v-model="quesCount" style="width: 100px;"></InputNumber>
+            <span style="margin-left: 10px;"><Icon type="ios-alert" color="#e14040" />{{ $t('evaluation.createPaper.volumeTips1') }}{{ quesTotal }}{{ $t('evaluation.createPaper.volumeTips2') }}</span>
+          </div>
+          <div class="filter-wrap-item">
+            <span class="filter-title">{{ $t('evaluation.createPaper.questype') }} : </span>
+            <div class="filter-content light-iview-select light-iview-input-number">
+              <CheckboxGroup v-model="syllabusType">
+                <Checkbox v-for="(item, index) in syllQuInfos" :key="index" :label="item.type" :disabled="!item.count">
+                    <span>{{ item.label }}</span>
+                    (<span>{{ item.count }}</span>{{ $t('evaluation.paperPickTip2') }})
+                </Checkbox>
+              </CheckboxGroup>
+            </div>
+            <!-- <p style="color: #e14040;">
+              <Icon type="ios-alert" />
+              未选择题型,将默认随机分配题型
+            </p> -->
+          </div>
+        </template>
+      </template>
     </div>
     <div class="auto-btn-wrap">
       <Button type="info" :loading="isLoading" @click="doAutoCreate">{{$t('evaluation.createPaper.doAutoCreate')}}</Button>
@@ -174,8 +255,63 @@ export default {
       ],
       itemConds: [],
       countResult: [],
-      canUseArr: new Array(8).fill(0)
-
+      canUseArr: new Array(8).fill(0),
+      originType: 'bank',
+      volumeList: [],
+      treeList: [],
+      volumeFilter: 1,
+      defaultProps: {
+          children: 'children',
+          label: 'title'
+      },
+      quesCount: 0,
+      quesTotal: 0,
+      syllabusType: [],
+      syllQuInfos: [
+        {
+          type: 'single',
+          label: this.$t('evaluation.single'),
+          count: 0,
+          ids: [],
+        }, {
+          type: 'multiple',
+          label: this.$t('evaluation.multiple'),
+          count: 0,
+          ids: [],
+        }, {
+          type: 'judge',
+          label: this.$t('evaluation.judge'),
+          count: 0,
+          ids: [],
+        }, {
+          type: 'complete',
+          label: this.$t('evaluation.complete'),
+          count: 0,
+          ids: [],
+        }, {
+          type: 'subjective',
+          label: this.$t('evaluation.subjective'),
+          count: 0,
+          ids: [],
+        },
+        {
+          type: 'connector',
+          label: this.$t('evaluation.connector'),
+          count: 0,
+          ids: [],
+        },
+        {
+          type: 'correct',
+          label: this.$t('evaluation.correct'),
+          count: 0,
+          ids: [],
+        }, {
+          type: 'compose',
+          label: this.$t('evaluation.compose'),
+          count: 0,
+          ids: [],
+        }
+      ],
     }
   },
 
@@ -211,125 +347,347 @@ export default {
         })
       })
     },
+    getVolumeList(scope) {
+        let findParams = {
+            "scope": scope,
+            "code": scope === 'school' ? this.$store.state.userInfo.schoolCode : this.$store.state.userInfo.TEAMModelId,
+            "periodId": scope === "school" ? this.periodCode : '',
+            "subjectId": scope === "school" ? this.subjectCode : '',
+            // gradeIds: this.manualFilter.gradeIds,
+            "status": 1,
+        }
+        return new Promise((resolve, reject) => {
+            this.$api.syllabus.FindVolumes(findParams).then(res => {
+                if(!res.error) {
+                    if(!this.isSchoolPaper) res.volumes.forEach(item => item.name = `${item.name}(${scope === 'school' ? '学校' : '个人'})`)
+                    resolve(res.volumes)
+                }
+            }).catch((e) => {
+                reject(e);
+            })
+        })
+    },
+    /* 根据册别查询对应课纲树形结构 */
+    getTreeByVolumeId(volume) {
+        this.$api.syllabus.GetTreeByVolume({
+            volumeId: volume.id,
+            volumeCode: volume.code,
+            scope: volume.scope
+        }).then(res => {
+            if (!res.error) {
+                this.treeList = res.tree.map(i =>{
+                    return i.trees[0]
+                })
+                this.getQuesNum(this.treeList)
+            } else {
+                this.$Message.warning(res.error);
+                this.treeList = []
+            }
+        }).catch(err => {
+            this.treeList = []
+        }).finally(() => {
+            this.isLoading = false
+        })
+    },
+
+    /* 处理树中题目数量 */
+    /* getQuesNum(arr, obj) {
+        arr.forEach(item => {
+            item.quesList = item.rnodes.filter(nodes => nodes.type === 'item') || []
+            if(item.children.length) {
+                this.getQuesNum(item.children, item)
+            }
+            item.count = item.quesList.length
+            if(obj) {
+              obj.count += item.quesList.length
+            }
+        })
+    }, */
+    /* 处理树中题目数量 */
+    getQuesNum(arr) {
+        let quesCount = 0
+        arr.forEach(item => {
+            item.quesList = item.rnodes.filter(nodes => nodes.type === 'item') || []
+            item.quesList.forEach(ques => {
+              let info = this.syllQuInfos.find(syll => syll.type === ques.subType)
+              if(info && !info.ids.includes(ques.id)) {
+                info.count++
+                info.ids.push(ques.id)
+                this.quesTotal ++
+              }
+            })
+            item.count = item.quesList.length
+            if(item.children.length) {
+              item.count += this.getQuesNum(item.children)
+            }
+            quesCount += item.count
+        })
+        return quesCount
+    },
 
     /* 学段切换 */
     onPeriodChange(val) {
       let curPeriod = this.periodList.filter(i => i.id === val)[0]
       this.subjectList = curPeriod.subjects
       this.subjectCode = this.subjectList.length ? this.subjectList[0].id : ''
-      if (this.onlySchool) {
-        this.periodCode = val
-        this.onCondChange()
+      if(this.originType === 'bank') {
+        if (this.onlySchool) {
+          this.periodCode = val
+          this.onCondChange()
+        }
+      } else {
+        this.onFilterOriginChange()
       }
     },
 
     onSubjectChange() {
-      this.onlySchool && this.onCondChange()
+      if(this.originType === 'bank') {
+        this.onlySchool && this.onCondChange()
+      } else {
+        this.onFilterOriginChange()
+      }
     },
 
-    /* 开始组题 */
+    /* 开始组题(课纲由前端来生成题目) */
     doAutoCreate() {
-      if (this.quInfos.map(i => i.count).filter(j => j === null).length) {
-        this.$Message.warning(this.$t('evaluation.createPaper.noItemTip'))
-        return
-      }
-      this.isLoading = true
-      let params = []
-      let copyInfos = JSON.parse(JSON.stringify(this.quInfos))
-      let copyInfos2 = JSON.parse(JSON.stringify(this.quInfos))
-      this.filterOrigin.forEach(scope => {
-        if (scope === 'private') {
-          params.push({
-            code: 'Item-' + this.$store.state.userInfo.TEAMModelId,
-            scope: scope,
-            period: '',
-            subject: '',
-            points: this.relatePoints,
-            quInfos: this.filterOrigin.length === 1 ? copyInfos : copyInfos.map(i => {
-              i.count = (i.count - Math.round(i.count * this.schoolRate * 0.01))
-              return i
-            })
-          })
+      if(this.originType === 'syllabus') {
+        if(!this.volumeList.length) return
+        let syllabusTree = this.$refs.tree.getCheckedNodes(false, true)
+        let quesList = [
+          {
+            type: 'single',
+            question: [],
+          }, {
+            type: 'multiple',
+            question: [],
+          }, {
+            type: 'judge',
+            question: [],
+          }, {
+            type: 'complete',
+            question: [],
+          }, {
+            type: 'subjective',
+            question: [],
+          },
+          {
+            type: 'connector',
+            question: [],
+          },
+          {
+            type: 'correct',
+            question: [],
+          }, {
+            type: 'compose',
+            question: [],
+          }
+        ]
+        if(!this.quesCount) {
+          this.$Message.warning(this.$t('evaluation.createPaper.tips14'))
+          return
+        }
+        if(!this.syllabusType.length) {
+          this.$Message.warning(this.$t('evaluation.createPaper.tips15'))
+          return
+        }
+        if(!syllabusTree.length) {
+          this.$Message.warning(this.$t('evaluation.createPaper.tips16'))
+          return
         } else {
-          params.push({
-            code: 'Item-' + this.$store.state.userInfo.schoolCode,
-            scope: scope,
-            period: this.periodCode,
-            subject: this.subjectCode,
-            points: this.relatePoints,
-            quInfos: this.filterOrigin.length === 1 ? copyInfos2 : copyInfos2.map((i, index) => {
-              i.count = Math.round(i.count * this.schoolRate * 0.01)
-              return i
+          // let syllQues = syllabusTree.map(item => item.quesList).flat()
+          let syllQues = []
+          syllabusTree.forEach(item => {
+            item.quesList.forEach(ques => {
+              ques.nodeId = item.id
+              ques.blob = ques.link
+              syllQues.push(ques)
             })
           })
+          syllQues.forEach(item => {
+            let info = quesList.find(ques => ques.type === item.subType)
+            if(info && !info.question.find(infos => infos.id === item.id)) {
+              info.question.push(item)
+            }
+          })
         }
-      })
+        this.isLoading = true
+        let syllAutoQues = [] //组成题目集合
+        let excessNum = this.quesCount //排除一道题目的类型后,剩余题目数量
+        let excessQues = [] //有多道题目的类型
+        this.syllabusType.forEach(item => {
+          let info = quesList.find(ques => ques.type === item)
+          if(info && info.question.length) {
+            if(info.question.length > 1) {
+              excessQues.push({type: info.type, num: 0})
+            } else {
+              excessNum--
+              info.question[0].blob = info.question[0].link
+              syllAutoQues.push(info.question[0])
+            }
+          }
+        })
+        if(excessQues.length) {
+          if(excessQues.length === 1) {
+            excessQues[0].num = excessNum
+          } else { //多个题型有多个题目时,随机分配数量 >= 1
+            excessQues = this.setTypeNum(excessQues, excessNum, quesList)
+          }
+          excessQues.forEach(item => {
+            let info = quesList.find(ques => ques.type === item.type)
+            if(info) {
+              const array = []
+              for (let i = 0; i < info.question.length; i++) {
+                array.push(i)
+              }
+              const indexArr = []
+              while (indexArr.length < item.num) {
+                const index = Math.floor(Math.random() * array.length)
+                indexArr.push(array[index])
+                array.splice(index, 1)
+              }
+              for (let i = 0; i < indexArr.length; i++) {
+                info.question[indexArr[i]].blob = info.question[indexArr[i]].link
+                info.question[indexArr[i]].blob = info.question[indexArr[i]].link
+                syllAutoQues.push(info.question[indexArr[i]])
+              }
+            }
+          })
+        }
+        // 根据选出来的题目id 获取blob信息
+        this.$emit('autoQuestions', syllAutoQues, true)
+        this.isLoading = false
 
-      // 针对自定义难度的参数进行调整
-      params.forEach(i => {
-        i.quInfos.forEach(j => {
-          if (j.policy === 'random' || j.policy === 'average') {
-            j.custom = []
+      } else {
+        if (this.quInfos.map(i => i.count).filter(j => j === null).length) {
+          this.$Message.warning(this.$t('evaluation.createPaper.noItemTip'))
+          return
+        }
+        this.isLoading = true
+        let params = []
+        let copyInfos = JSON.parse(JSON.stringify(this.quInfos))
+        let copyInfos2 = JSON.parse(JSON.stringify(this.quInfos))
+        this.filterOrigin.forEach(scope => {
+          if (scope === 'private') {
+            params.push({
+              code: 'Item-' + this.$store.state.userInfo.TEAMModelId,
+              scope: scope,
+              period: '',
+              subject: '',
+              points: this.relatePoints,
+              quInfos: this.filterOrigin.length === 1 ? copyInfos : copyInfos.map(i => {
+                i.count = (i.count - Math.round(i.count * this.schoolRate * 0.01))
+                return i
+              })
+            })
           } else {
-            j.custom = [{
-              level: this.diffList.find(k => k.level === j.policy).value,
-              count: j.count
-            }]
-            j.policy = 'custom'
+            params.push({
+              code: 'Item-' + this.$store.state.userInfo.schoolCode,
+              scope: scope,
+              period: this.periodCode,
+              subject: this.subjectCode,
+              points: this.relatePoints,
+              quInfos: this.filterOrigin.length === 1 ? copyInfos2 : copyInfos2.map((i, index) => {
+                i.count = Math.round(i.count * this.schoolRate * 0.01)
+                return i
+              })
+            })
           }
         })
-      })
 
-      // 访问API获取随机题目
-      this.$api.learnActivity.Automatic(params).then(
-        res => {
-          if (res.length > 0) {
-            let needCount = params.reduce((a, b) => a + b.quInfos.reduce((c, d) => c + d.count, 0), 0)
-            let getCount = res.reduce((a, b) => a + b.count, 0)
-            if (needCount > getCount) {
-              console.log(needCount, getCount);
-              this.$Modal.confirm({
-                title: this.$t('settings.modalTip4'),
-                content: this.$t('evaluation.createPaper.tip10'),
-                onOk: () => {
-                  let result = []
-                  res.forEach(i => {
-                    result = result.concat(i.item)
-                  })
-                  console.log('组题成功', result)
-                  this.$emit('autoQuestions', result)
-                  this.isLoading = false
-                },
-                onCancel: () => {
-                  this.isLoading = false
-                }
-              })
+        // 针对自定义难度的参数进行调整
+        params.forEach(i => {
+          i.quInfos.forEach(j => {
+            if (j.policy === 'random' || j.policy === 'average') {
+              j.custom = []
             } else {
-              let result = []
-              res.forEach(i => {
-                result = result.concat(i.item)
-              })
-              console.log('组题成功', result)
-              this.$emit('autoQuestions', result)
-              this.isLoading = false
+              j.custom = [{
+                level: this.diffList.find(k => k.level === j.policy).value,
+                count: j.count
+              }]
+              j.policy = 'custom'
             }
+          })
+        })
 
-          } else {
-            this.$Message.error(this.$t('evaluation.createPaper.noResultTip'))
+        // 访问API获取随机题目
+        this.$api.learnActivity.Automatic(params).then(
+          res => {
+            if (res.length > 0) {
+              let needCount = params.reduce((a, b) => a + b.quInfos.reduce((c, d) => c + d.count, 0), 0)
+              let getCount = res.reduce((a, b) => a + b.count, 0)
+              if (needCount > getCount) {
+                console.log(needCount, getCount);
+                this.$Modal.confirm({
+                  title: this.$t('settings.modalTip4'),
+                  content: this.$t('evaluation.createPaper.tip10'),
+                  onOk: () => {
+                    let result = []
+                    res.forEach(i => {
+                      result = result.concat(i.item)
+                    })
+                    console.log('组题成功', result)
+                    this.$emit('autoQuestions', result)
+                    this.isLoading = false
+                  },
+                  onCancel: () => {
+                    this.isLoading = false
+                  }
+                })
+              } else {
+                let result = []
+                res.forEach(i => {
+                  result = result.concat(i.item)
+                })
+                console.log('组题成功', result)
+                this.$emit('autoQuestions', result)
+                this.isLoading = false
+              }
+
+            } else {
+              this.$Message.error(this.$t('evaluation.createPaper.noResultTip'))
+              setTimeout(() => {
+                this.isLoading = false
+              }, 1000)
+            }
+          },
+          err => {
+            this.$Message.error('API ERROR!')
             setTimeout(() => {
               this.isLoading = false
             }, 1000)
           }
-        },
-        err => {
-          this.$Message.error('API ERROR!')
-          setTimeout(() => {
-            this.isLoading = false
-          }, 1000)
-        }
-      )
+        )
 
-      console.log(params)
+        console.log(params)
+      }
+    },
+    // 为题型随机分配数量
+    setTypeNum(arr, total, quesList) {
+      let partSum = total / arr.length
+      arr.forEach((item, index) => {
+        // 题目数量不能超过题型本身数量
+        let info = quesList.find(ques => ques.type === item.type)
+        let num = index === arr.length - 1 ?
+          ((info.question.length - item.num) > total ? total : (info.question.length - item.num)) :
+          (Math.floor(Math.random() * (info ? ((info.question.length - item.num) > (partSum - 1) ? (partSum - 1) : (info.question.length - item.num)) : (partSum - 1))) + 1)
+        item.num += num
+        item.noRemain = info.question.length === item.num ? true : false
+        total -= num
+      })
+      // 筛出未分配完题目的题型,再继续分配数量,直至 total = 0
+      if(total) {
+        let autoArr = arr.filter(item => !item.noRemain)
+        if(autoArr.length) {
+          autoArr = this.setTypeNum(autoArr, total, quesList)
+          autoArr.forEach(item => {
+            let info = arr.find(ques => ques.type === item.type)
+            let quesinfo = quesList.find(ques => ques.type === item.type)
+            // info.num = info.num + item.num
+            info.noRemain = quesinfo.question.length === info.num ? true : false
+          })
+        }
+      }
+      return arr
     },
 
     onClosePoint(index) {
@@ -351,15 +709,39 @@ export default {
     },
 
     /* 来源选择 */
-    onFilterOriginChange() {
-      if (this.filterOrigin.length === 1) {
-        this.getFilterCount(this.filterOrigin[0]).then(res => {
-          this.itemConds = res
-          this.onCondChange()
-        })
+    async onFilterOriginChange() {
+      if(this.originType === 'bank') {
+        if (this.filterOrigin.length === 1) {
+          this.getFilterCount(this.filterOrigin[0]).then(res => {
+            this.itemConds = res
+            this.onCondChange()
+          })
+        }
+      } else {
+        let resultList = []
+        for (let i = 0; i < this.filterOrigin.length; i++) {
+            try {
+                let data = []
+                data = await this.getVolumeList(this.filterOrigin[i]);
+                resultList = resultList.concat(data);
+            } catch (e) {
+                console.log(e);
+            }
+        }
+        this.treeList = []
+        this.volumeList = resultList
+        this.volumeFilter = 1
+        if(this.volumeList.length) {
+            this.getTreeByVolumeId(this.volumeList[0])
+        }
       }
     },
 
+    /* 点击某个节点 */
+    async onNodeClick(data) {
+      console.log('111111111111', data);
+    },
+
     /* 题目数量发生变化 */
     async onCondChange() {
       // if(!this.isSchoolPaper) return

+ 1 - 0
TEAMModelOS/ClientApp/src/view/teachcontent/index.vue

@@ -345,6 +345,7 @@ export default {
       BlobTool.getContainerSize(this.containerName, this.routerScope).then(
         res => {
           this.sizeInfo = res
+          this.sizeInfo.other = (this.sizeInfo?.other || 0) + this.sizeInfo.homework
           console.log('01010', this.sizeInfo)
           this.teachSpace = res.teachSpace || 0
         },

+ 64 - 70
TEAMModelOS/Controllers/Analysis/ArtAnalysisController.cs

@@ -459,7 +459,7 @@ namespace TEAMModelOS.Controllers.Analysis
                         persent =
                              blockScore.Where(z => z.name.Equals(c)).Sum(v => v.score) > 0 ?
                              blockScore.Where(z => z.name.Equals(c)).Sum(v => v.av) / blockScore.Where(z => z.name.Equals(c)).Sum(v => v.score) : 0
-                    }).Sum(o => o.persent), 4)
+                    }).Sum(o => o.persent) / x.blocks.Count, 4)
                 });
 
 
@@ -528,46 +528,48 @@ namespace TEAMModelOS.Controllers.Analysis
                     {
                         id = x.Key.classId,
                         name = x.Key.className,
+                        count = classResults.Where(k => k.info.id.Equals(x.Key.classId)).FirstOrDefault().status.Where(o => o == 0).ToList().Count,
                         max = x.ToList().Where(p => p.score > 0).Select(z => z.score).ToList().Count > 0 ? x.ToList().Where(p => p.score > 0).Select(z => z.score).ToList().Max(s => Math.Abs((double)s)) : 0,
                         min = x.ToList().Where(p => p.score > 0).Select(z => z.score).ToList().Count > 0 ? x.ToList().Where(p => p.score > 0).Select(z => z.score).ToList().Min(s => Math.Abs((double)s)) : 0,
-                        excellent = x.ToList().Where(p => p.score > 0).Select(z => z.score).ToList().Count > 0 ? Math.Round(x.ToList().Where(p => p.score > 0).Select(z => z.score).ToList().Where(s => s >= 80).Count() * 1.0 / x.ToList().Count, 4) : 0,
-                        pass = x.ToList().Where(p => p.score > 0).Select(z => z.score).ToList().Count > 0 ? Math.Round(x.ToList().Where(p => p.score > 0).Select(z => z.score).ToList().Where(s => s >= 60).Count() * 1.0 / x.ToList().Count, 4) : 0,
-                        score = x.ToList().Count > 0 ? Math.Round((double)x.ToList().Sum(z => z.score) * 1.0 / x.ToList().Count, 2) : 0,
-                        persent = x.ToList().Count > 0 ? Math.Round((double)x.ToList().Sum(z => z.score) * 1.0 / x.ToList().Count / key7.Value, 4) : 0,
+                        excellent = x.ToList().Where(p => p.score > 0).Select(z => z.score).ToList().Count > 0 ? Math.Round(x.ToList().Where(p => p.score > 0).Select(z => z.score).ToList().Where(s => s >= 80).Count() * 1.0 / classResults.Where(k => k.info.id.Equals(x.Key.classId)).FirstOrDefault().status.Where(o => o == 0).ToList().Count, 4) : 0,
+                        pass = x.ToList().Where(p => p.score > 0).Select(z => z.score).ToList().Count > 0 ? Math.Round(x.ToList().Where(p => p.score > 0).Select(z => z.score).ToList().Where(s => s >= 60).Count() * 1.0 / classResults.Where(k => k.info.id.Equals(x.Key.classId)).FirstOrDefault().status.Where(o => o == 0).ToList().Count, 4) : 0,
+                        score = classResults.Where(k => k.info.id.Equals(x.Key.classId)).FirstOrDefault().status.Where(o => o == 0).ToList().Count > 0 ? Math.Round((double)x.ToList().Sum(z => z.score) * 1.0 / classResults.Where(k => k.info.id.Equals(x.Key.classId)).FirstOrDefault().status.Where(o => o == 0).ToList().Count, 2) : 0,
+                        persent = classResults.Where(k => k.info.id.Equals(x.Key.classId)).FirstOrDefault().status.Where(o => o == 0).ToList().Count > 0 ? Math.Round((double)x.ToList().Sum(z => z.score) * 1.0 / classResults.Where(k => k.info.id.Equals(x.Key.classId)).FirstOrDefault().status.Where(o => o == 0).ToList().Count / key7.Value, 4) : 0,
                         kno = key5.Value.Where(c => c.cId.Equals(x.Key.classId)).Select(z => new
                         {
                             z.name,
-                            persent = Math.Round(z.point > 0 ? z.score / z.point / students.Count() : 0, 4),
+                            persent = Math.Round(z.point > 0 ? z.score / z.point / classResults.Where(k => k.info.id.Equals(x.Key.classId)).FirstOrDefault().status.Where(o => o == 0).ToList().Count : 0, 4),
                             block = knos.Where(v => null != v.kno && v.kno.Contains(z.name)).Select(x => x.name)
                         }),
                         block = claBlock.Count() > 0 ? claBlock.Where(c => c.Key.Equals(x.Key.classId))?.FirstOrDefault().block.Select(z => new
                         {
                             name = z.Key,
-                            persent = Math.Round(z.point > 0 ? z.score / z.point / students.Count() : 0, 4)
+                            persent = Math.Round(z.point > 0 ? z.score / z.point / classResults.Where(k => k.info.id.Equals(x.Key.classId)).FirstOrDefault().status.Where(o => o == 0).ToList().Count : 0, 4)
                         }) : null,
                         dim = claDims.Where(c => c.claId.Equals(x.Key.classId)).Select(z => new
                         {
                             name = z.dim,
-                            persent = Math.Round(z.point > 0 ? z.score / z.point / students.Count() : 0, 4)
+                            persent = Math.Round(z.point > 0 ? z.score / z.point / classResults.Where(k => k.info.id.Equals(x.Key.classId)).FirstOrDefault().status.Where(o => o == 0).ToList().Count : 0, 4)
                         }),
                         examResults[0].classes.Where(c => c.id.Equals(x.Key.classId))?.FirstOrDefault().gradeId
                     });
 
-                
-                
+
+
 
                 //年级信息
+                var realCount = stus.Count - info.lostStu.Count;
                 var grades = students.GroupBy(c => c.gradeId).Select(x => new { gradeId = x.Key, list = x.ToList().Select(v => v.score).Where(c => c > 0) });
                 var gscore = grades.Select(x => new
                 {
                     id = x.gradeId,
                     name = perMore[int.Parse(x.gradeId)],
-                    score = x.list.Any() ? Math.Round((double)(x.list.Sum() / x.list.Count()), 2) : 0,
-                    persent = x.list.Any() ? Math.Round((double)(x.list.Sum() / x.list.Count()) / key7.Value, 4) : 0,
+                    score = x.list.Any() ? Math.Round((double)(x.list.Sum() / realCount), 2) : 0,
+                    persent = x.list.Any() ? Math.Round((double)(x.list.Sum() / realCount) / key7.Value, 4) : 0,
                     max = x.list.Any() ? x.list.Max(s => Math.Abs((double)s)) : 0,
                     min = x.list.Any() ? x.list.Min(s => Math.Abs((double)s)) : 0,
-                    excellent = x.list.Any() ? Math.Round(x.list.Where(s => s >= 80).Count() * 1.0 / x.list.Count(), 4) : 0,
-                    pass = x.list.Any() ? Math.Round(x.list.Where(s => s >= 60).Count() * 1.0 / x.list.Count(), 4) : 0
+                    excellent = x.list.Any() ? Math.Round(x.list.Where(s => s >= 80).Count() * 1.0 / realCount, 4) : 0,
+                    pass = x.list.Any() ? Math.Round(x.list.Where(s => s >= 60).Count() * 1.0 / realCount, 4) : 0
                 });
                 //获奖次数
                 List<ArtAttachment> artAttachments = new();
@@ -599,7 +601,6 @@ namespace TEAMModelOS.Controllers.Analysis
                  else {
                      url = _azureStorage.GetBlobContainerClient($"{code}").GetBlobClient($"/art/{id}/{subjectId}.json").Uri.ToString();
                  }*/
-                var realCount = stus.Count - info.lostStu.Count;
                 await _azureRedis.GetRedisClient(8).HashSetAsync($"ArtSchool:count:{code}", $"count:{subjectId}:{id}", tchList.Count.ToJsonString());                
                 await _azureRedis.GetRedisClient(8).HashSetAsync($"ArtSchool:max:{code}", $"max:{subjectId}:{id}", max.ToJsonString());
                 await _azureRedis.GetRedisClient(8).HashSetAsync($"ArtSchool:min:{code}", $"min:{subjectId}:{id}", min.ToJsonString());
@@ -679,8 +680,8 @@ namespace TEAMModelOS.Controllers.Analysis
 
         [ProducesDefaultResponseType]
         [HttpPost("oldStatistics")]
-        [Authorize(Roles = "IES")]
-        [AuthToken(Roles = "teacher,admin")]
+        //[Authorize(Roles = "IES")]
+        //[AuthToken(Roles = "teacher,admin")]
         public async Task<IActionResult> getOldAnalysis(JsonElement request)
         {
             var client = _azureCosmos.GetCosmosClient();
@@ -739,6 +740,16 @@ namespace TEAMModelOS.Controllers.Analysis
                         }
                     }
                 }
+                //获取本次评测所有科目结算结果
+                List<ExamResult> examResults = new();
+                List<ExamClassResult> classResults = [];
+                ExamInfo info = await client.GetContainer(Constant.TEAMModelOS, "Common").ReadItemAsync<ExamInfo>(examId.ToString(), new PartitionKey($"Exam-{code}"));
+                var query = $"select value(c) from c where c.examId = '{examId}' and c.subjectId = '{subjectId}' ";
+                await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "Common").GetItemQueryIteratorSql<ExamClassResult>(queryText: query, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"ExamClassResult-{code}") }))
+                {
+                    classResults.Add(item);
+                }
+                var realCount = stus.Count - info.lostStu.Count;
                 var subjectScore = new
                 {
                     name = As.Where(a => a.subjectId.Equals(subjectId.GetString())).Select(x => x.score).Where(c => c > 0)
@@ -747,25 +758,17 @@ namespace TEAMModelOS.Controllers.Analysis
                 double max = subjectScore.name.Max(s => Math.Abs(s));
                 double min = subjectScore.name.Min(s => Math.Abs(s));
                 double total = subjectScore.name.Sum();
-                double average = Math.Round(total / stus.Count, 2);
-                double excellent = Math.Round(subjectScore.name.Where(s => s >= 80).Count() * 1.0 / stus.Count, 4);
-                double pass = Math.Round(subjectScore.name.Where(s => s >= 60).Count() * 1.0 / stus.Count, 4);
+                double average = Math.Round(total / realCount, 2);
+                double excellent = Math.Round(subjectScore.name.Where(s => s >= 80).Count() * 1.0 / realCount, 4);
+                double pass = Math.Round(subjectScore.name.Where(s => s >= 60).Count() * 1.0 / realCount, 4);
                 double powSum = 0;
                 foreach (var sc in subjectScore.name)
                 {
                     powSum += Math.Pow(sc - average, 2);
                 }
-                var pow = Math.Round(stus.Count > 0 ? Math.Pow(powSum / stus.Count, 0.5) : 0, 2);
+                var pow = Math.Round(stus.Count > 0 ? Math.Pow(powSum / realCount, 0.5) : 0, 2);
 
-                //获取本次评测所有科目结算结果
-                List<ExamResult> examResults = new();
-                List<ExamClassResult> classResults = [];
-                ExamInfo info = await client.GetContainer(Constant.TEAMModelOS, "Common").ReadItemAsync<ExamInfo>(examId.ToString(), new PartitionKey($"Exam-{code}"));
-                var query = $"select value(c) from c where c.examId = '{examId}' and c.subjectId = '{subjectId}' ";
-                await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "Common").GetItemQueryIteratorSql<ExamClassResult>(queryText: query, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"ExamClassResult-{code}") }))
-                {
-                    classResults.Add(item);
-                }
+               
                 (List<RMember> tchList, List<RGroupList> classLists) = await GroupListService.GetMemberByListids(_coreAPIHttpService, client, _dingDing, classIds, code.GetString(), null, -1, info.startTime);
                 //获取评测ID
                 //var examId = arts[0].settings.SelectMany(s => s.task).Where(a => a.type == 1 && a.subject.Equals(subjectId.GetString())).FirstOrDefault().acId;
@@ -920,7 +923,7 @@ namespace TEAMModelOS.Controllers.Analysis
                         persent =
                              blockScore.Where(z => z.name.Equals(c)).Sum(v => v.score) > 0 ?
                              blockScore.Where(z => z.name.Equals(c)).Sum(v => v.av) / blockScore.Where(z => z.name.Equals(c)).Sum(v => v.score) : 0
-                    }).Sum(o => o.persent), 4)
+                    }).Sum(o => o.persent) / x.blocks.Count , 4)
                 });
 
 
@@ -931,60 +934,51 @@ namespace TEAMModelOS.Controllers.Analysis
                     block = knos.Where(v => null != v.kno && v.kno.Contains(x.name)).Select(x => x.name)
                 });
                 //学生信息
-                var students = stus.Select(s => new
+                List<(string id, double score, string name, string classId, string className, string gradeId, List<(string kname, double persent, List<string> blok)> kno,
+               List<(string kname, double persent)> block, List<(string kname, double persent)> dim)> stuAsync = [];
+                await foreach (var ss in stuTask(stus, tchList, examResults, subjectId.GetString(), key6, knos, stuBlockScore, stuDims))
                 {
-                    id = s.sIds,
-                    s.scs.FirstOrDefault(x => !string.IsNullOrWhiteSpace(x.subjectId) && x.subjectId.Equals(subjectId.GetString()))?.score,
-                    tchList.Where(t => t.id.Equals(s.sIds)).FirstOrDefault()?.name,
-                    classId = s.cd,
-                    className = examResults[0].classes.Where(c => c.id.Equals(s.cd)).FirstOrDefault()?.name,
-                    examResults[0].classes.Where(c => c.id.Equals(s.cd)).FirstOrDefault()?.gradeId,
-                    //key.Value.Where(c => c.id.Equals(s.sIds))?.FirstOrDefault().sta,
-                    //key.Value.Where(c => c.id.Equals(s.sIds))?.FirstOrDefault().pass,
-                    // key.Value.Where(c => c.id.Equals(s.sIds))?.FirstOrDefault().stu,
-                    kno = key6.Value.Where(c => c.sId.Equals(s.sIds))?.Select(z => new
-                    {
-                        z.name,
-                        persent = z.point > 0 ? Math.Round(z.score / z.point, 4) : 0,
-                        block = knos.Where(v => null != v.kno && v.kno.Contains(z.name))?.Select(x => x.name)
-                    }),
-                    block = stuBlock.Where(c => c.Key.Equals(s.sIds)).FirstOrDefault()?.block.Select(x => new
-                    {
-                        name = x.Key,
-                        persent = x.point > 0 ? Math.Round(x.score / x.point, 4) : 0
-                    }),
-                    dim = stuDims.Where(c => c.stuId.Equals(s.sIds))?.Select(z => new
-                    {
-                        name = z.dim,
-                        persent = z.point > 0 ? Math.Round(z.score / z.point, 4) : 0
-                    })
+                    stuAsync = ss.students;
+                }
+                var students = stuAsync.Select(c => new {
+                    c.id,
+                    c.score,
+                    c.name,
+                    c.classId,
+                    c.className,
+                    c.gradeId,
+                    kno = c.kno.Select(x => new { name = x.kname, x.persent, x.blok }),
+                    block = c.block.Select(x => new { name = x.kname, x.persent }),
+                    dim = c.dim.Select(x => new { name = x.kname, x.persent })
                 });
+
                 List<(string cId, double sc, double max, double min, double excellent, double pass, double count)> clsInfo = new();
                 var cInfo = students.GroupBy(c => (c.classId, c.className)).Select(x => new
                 {
                     id = x.Key.classId,
                     name = x.Key.className,
+                    count = classResults.Where(k => k.info.id.Equals(x.Key.classId)).FirstOrDefault().status.Where(o => o == 0).ToList().Count,
                     max = x.ToList().Where(p => p.score > 0).Select(z => z.score).ToList().Count > 0 ? x.ToList().Where(p => p.score > 0).Select(z => z.score).ToList().Max(s => Math.Abs((double)s)) : 0,
                     min = x.ToList().Where(p => p.score > 0).Select(z => z.score).ToList().Count > 0 ? x.ToList().Where(p => p.score > 0).Select(z => z.score).ToList().Min(s => Math.Abs((double)s)) : 0,
-                    excellent = x.ToList().Where(p => p.score > 0).Select(z => z.score).ToList().Count > 0 ? Math.Round(x.ToList().Where(p => p.score > 0).Select(z => z.score).ToList().Where(s => s >= 80).Count() * 1.0 / x.ToList().Count, 4) : 0,
-                    pass = x.ToList().Where(p => p.score > 0).Select(z => z.score).ToList().Count > 0 ? Math.Round(x.ToList().Where(p => p.score > 0).Select(z => z.score).ToList().Where(s => s >= 60).Count() * 1.0 / x.ToList().Count, 4) : 0,
-                    score = x.ToList().Count > 0 ? Math.Round((double)x.ToList().Sum(z => z.score) * 1.0 / x.ToList().Count, 2) : 0,
-                    persent = x.ToList().Count > 0 ? Math.Round((double)x.ToList().Sum(z => z.score) * 1.0 / x.ToList().Count / key7.Value, 4) : 0,
+                    excellent = x.ToList().Where(p => p.score > 0).Select(z => z.score).ToList().Count > 0 ? Math.Round(x.ToList().Where(p => p.score > 0).Select(z => z.score).ToList().Where(s => s >= 80).Count() * 1.0 / classResults.Where(k => k.info.id.Equals(x.Key.classId)).FirstOrDefault().status.Where(o => o == 0).ToList().Count, 4) : 0,
+                    pass = x.ToList().Where(p => p.score > 0).Select(z => z.score).ToList().Count > 0 ? Math.Round(x.ToList().Where(p => p.score > 0).Select(z => z.score).ToList().Where(s => s >= 60).Count() * 1.0 / classResults.Where(k => k.info.id.Equals(x.Key.classId)).FirstOrDefault().status.Where(o => o == 0).ToList().Count, 4) : 0,
+                    score = classResults.Where(k => k.info.id.Equals(x.Key.classId)).FirstOrDefault().status.Where(o => o == 0).ToList().Count > 0 ? Math.Round((double)x.ToList().Sum(z => z.score) * 1.0 / classResults.Where(k => k.info.id.Equals(x.Key.classId)).FirstOrDefault().status.Where(o => o == 0).ToList().Count, 2) : 0,
+                    persent = classResults.Where(k => k.info.id.Equals(x.Key.classId)).FirstOrDefault().status.Where(o => o == 0).ToList().Count > 0 ? Math.Round((double)x.ToList().Sum(z => z.score) * 1.0 / classResults.Where(k => k.info.id.Equals(x.Key.classId)).FirstOrDefault().status.Where(o => o == 0).ToList().Count / key7.Value, 4) : 0,
                     kno = key5.Value.Where(c => c.cId.Equals(x.Key.classId)).Select(z => new
                     {
                         z.name,
-                        persent = Math.Round(z.point > 0 ? z.score / z.point / students.Count() : 0, 4),
+                        persent = Math.Round(z.point > 0 ? z.score / z.point / classResults.Where(k => k.info.id.Equals(x.Key.classId)).FirstOrDefault().status.Where(o => o == 0).ToList().Count : 0, 4),
                         block = knos.Where(v => null != v.kno && v.kno.Contains(z.name)).Select(x => x.name)
                     }),
                     block = claBlock.Count() > 0 ? claBlock.Where(c => c.Key.Equals(x.Key.classId))?.FirstOrDefault().block.Select(z => new
                     {
                         name = z.Key,
-                        persent = Math.Round(z.point > 0 ? z.score / z.point / students.Count() : 0, 4)
+                        persent = Math.Round(z.point > 0 ? z.score / z.point / classResults.Where(k => k.info.id.Equals(x.Key.classId)).FirstOrDefault().status.Where(o => o == 0).ToList().Count : 0, 4)
                     }) : null,
                     dim = claDims.Where(c => c.claId.Equals(x.Key.classId)).Select(z => new
                     {
                         name = z.dim,
-                        persent = Math.Round(z.point > 0 ? z.score / z.point / students.Count() : 0, 4)
+                        persent = Math.Round(z.point > 0 ? z.score / z.point / classResults.Where(k => k.info.id.Equals(x.Key.classId)).FirstOrDefault().status.Where(o => o == 0).ToList().Count : 0, 4)
                     }),
                     examResults[0].classes.Where(c => c.id.Equals(x.Key.classId))?.FirstOrDefault().gradeId
                 });
@@ -994,12 +988,12 @@ namespace TEAMModelOS.Controllers.Analysis
                 {
                     id = x.gradeId,
                     name = perMore[int.Parse(x.gradeId)],
-                    score = x.list.Any() ? Math.Round((double)(x.list.Sum() / x.list.Count()), 2) : 0,
-                    persent = x.list.Any() ? Math.Round((double)(x.list.Sum() / x.list.Count()) / key7.Value, 4) : 0,
+                    score = x.list.Any() ? Math.Round((double)(x.list.Sum() / realCount), 2) : 0,
+                    persent = x.list.Any() ? Math.Round((double)(x.list.Sum() / realCount) / key7.Value, 4) : 0,
                     max = x.list.Any() ? x.list.Max(s => Math.Abs((double)s)) : 0,
                     min = x.list.Any() ? x.list.Min(s => Math.Abs((double)s)) : 0,
-                    excellent = x.list.Any() ? Math.Round(x.list.Where(s => s >= 80).Count() * 1.0 / x.list.Count(), 4) : 0,
-                    pass = x.list.Any() ? Math.Round(x.list.Where(s => s >= 60).Count() * 1.0 / x.list.Count(), 4) : 0
+                    excellent = x.list.Any() ? Math.Round(x.list.Where(s => s >= 80).Count() * 1.0 / realCount, 4) : 0,
+                    pass = x.list.Any() ? Math.Round(x.list.Where(s => s >= 60).Count() * 1.0 / realCount, 4) : 0
                 });
                 //获奖次数
                 List<ArtAttachment> artAttachments = new();
@@ -1031,7 +1025,7 @@ namespace TEAMModelOS.Controllers.Analysis
                  else {
                      url = _azureStorage.GetBlobContainerClient($"{code}").GetBlobClient($"/art/{id}/{subjectId}.json").Uri.ToString();
                  }*/
-                var realCount = stus.Count - info.lostStu.Count;
+
                 //var realClassCount = classResults.Where(c => c.info.id.Equals(classIds[0])).SelectMany(z => z.status).Count(k => k == 0);
                 return Ok(new { count = tchList.Count, scount = realCount, max, min, average, excellent, pass, pow, blk, kno, dim, optCount, students, cInfo, gscore });
                
@@ -1324,7 +1318,7 @@ namespace TEAMModelOS.Controllers.Analysis
                 Score.Add(scores);
 
                 //该知识点平均得分
-                double sc = exam.studentIds.Count > 0 ? Math.Round(scores * 1.0 / exam.studentIds.Count, 2) : 0;
+                double sc = (exam.studentIds.Count - exam.lostStus.Count) > 0 ? Math.Round(scores * 1.0 / (exam.studentIds.Count - exam.lostStus.Count), 2) : 0;
                 //知识点占比 此处为了让结果更好看 乘以了系数1.5
                 double average = sc * 1.5;
                 if (average > OnePoint)

+ 8 - 4
TEAMModelOS/JsonFile/Core/LangConfigV3.json

@@ -28,7 +28,8 @@
         "Judge": "对|错",
         "Ended": "结束",
         "Level": "难度",
-        "Count": "填空数量"
+        "Count": "填空数量",
+        "Tag": "标签"
       }
     },
     {
@@ -56,7 +57,8 @@
         "Judge": "對|錯",
         "Ended": "結束",
         "Level": "難度",
-        "Count": "填充數量"
+        "Count": "填充數量",
+        "Tag": "標簽"
       }
     },
     {
@@ -84,7 +86,8 @@
         "Judge": "對|錯",
         "Ended": "結束",
         "Level": "難度",
-        "Count": "填充數量"
+        "Count": "填充數量",
+        "Tag": "標簽"
       }
     },
     {
@@ -112,7 +115,8 @@
         "Judge": "True|False",
         "Ended": "Ended",
         "Level": "Level",
-        "Count": "Count"
+        "Count": "Count",
+        "Tag": "Tag"
       }
     }
   ]